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>