Showing posts with label maven. Show all posts
Showing posts with label maven. Show all posts

Thursday, August 14, 2014

Using Google Guice in a Netbeans RCP application

We have been using Tapestry IoC in a Netbeans RCP application for a while. We recently added more NBMs which uses Tapestry, either for IoC or as web framework. We then ran into some dependency issues, and we decided to take the opportunity to replace Tapestry as IoC container with the JSR-330 compliant Google Guice.

The way the application is configured is pretty similar in Tapestry and Guice, so rewriting the module didn't take long. In addition, we annotated the necessary classes with @Inject, some of them residing in different NBMs.

When launching the application we got six errors with the message:

Could not find a suitable constructor in com.example.ClassName. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private
Googling gave the impression that not that many people have combined Netbeans RCP and Guice. At least, I wasn't able to find any solution.

Then, I noticed that all the errors were related to classes in NBMs other than the one we were trying to setup IoC in. Netbeans uses different class loaders for each NBM, and the @Inject-annotation in the other NBMs was loaded by different class loaders than the one used by Guice. That's why Guice couldn't recognise that the constructors were correctly annotated.

The solution for us was to create a Library Wrapper Module for the jar containing the @Inject annotation, namely javax.inject.

The pom file we used for creating the wrapper, minus release- and deploy-related stuff, looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
    <packaging>nbm</packaging>

    <name>Wrapper for javax.inject</name>

    <dependencies>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>nbm-maven-plugin</artifactId>
                <version>3.13</version>
                <extensions>true</extensions>
                <configuration>
                    <publicPackages>
                        <publicPackage>javax.inject.**</publicPackage>
                    </publicPackages>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <useDefaultManifestFile>true</useDefaultManifestFile>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


Wednesday, March 16, 2011

Grails - Excluding plugin dependencies from war

I started using maven to build my Grails project, and upgraded Grails to version 1.3.7. Running mvn grails:run-app with Tomcat works fine, but when I tried to generate a war file with mvn package, and deploy that to Jetty, it failed:

Exception in thread "main" java.lang.IllegalAccessError: tried to access field
org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory
at org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java:60)

Googling led me to the SLF4J FAQ (http://www.slf4j.org/faq.html) which says that this is caused by slf4j-api <= 1.5.5 being incompatible with an slf4j binding > 1.5.5. My application declares slf4j-api 1.5.8 and slf4j-log4j12 1.5.8 as dependencies. Running mvn dependency:tree didn't reveal any slf4j-api <= 1.5.5, but, indeed, in WEB-INF/lib in the application's war-file there was a slf4j-api-1.5.2.jar. Where did it come from?

When running mvn package with the log level of Ivy resolver set to "info", it seems that it was pulled in by Hibernate, through its dependencies hibernate-core and hibernate-commons-annotations. Problem solved! Grails has great support for determining which of the plugin's dependencies to exclude. In the Grails Reference Documentation it says:

If a plugin is using a JAR which conflicts with another plugin, or an application dependency then you can override how a plugin resolves its dependencies inside an application using exclusions. For example:

plugins {
   runtime( "org.grails.plugins:hibernate:1.3.0" ) {
     excludes "javassist"
   }
}

This means that all I have to do is include the following in my BuildConfig.groovy:

runtime("org.grails.plugins:hibernate:1.3.7") {
  excludes "slf4j-api"
}

No luck. slf4j-api-1.5.2.jar was still there. Googling suggested that there is a bug in Grails that prevents it from excluding transitive dependencies of plugins (GRAILS-6910). Allright. Let me exclude hibernate-core and hibernate-comons-annotations, then, and include them as dependencies with slf4j-api excluded, ie.

dependencies {
  compile("org.hibernate:hibernate-commons-annotations:3.1.0.GA") {
    excludes "slf4j-api"
  }
  compile("org.hibernate:hibernate-core:3.1.0.GA") {
    excludes "slf4j-api"
  }
}

plugins {
  runtime("org.grails.plugins:hibernate:1.3.7") {
    excludes "hibernate-core", "hibernate-commons-annotations"
  }
}

Still no luck.

Finally, Google led me to this page: Excluding files from a WAR with Grails – the right way. There, Marc Palmer suggested adding the following to Config.groovy to exclude a certain .jar-file from the war file:



grails.war.resources = { stagingDir ->
  delete(file:"${stagingDir}/WEB-INF/lib/slf4j-api-1.5.2.jar")
}

Ran mvn package while I held my breath. Once finished, I checked the generated war file, and, to my disappointment, the slf4j-api-1.5.2.jar was still there.

However, reading through the mentioned post's comments, some guy suggested that it should be added to BuildConfig.groovy, rather than Config.groovy.


Tried that. And finally, no slf4j-api-1.5.2.jar in the generated war file, only my own slf4j-api-1.5.8.jar.

Deployed the war to Jetty, and it worked like a charm.

It seems to me that Grails' dependency manipulation methods are rather buggy at the moment. I find this an acceptable workaround.