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>


1 comment:

  1. Where/How do you construct the injector and pass it around from in the application?
    I'm familiar with Java/Jakarta EE using CDI but you get DI for free. I've also used Guice in DropWizard applications.
    I have a fairly old application built on the RCP that I would really love to introduce Guice into but can't wrap head around where to bootstrap everything from. It's also a multi-module application where we have various builds for different environments so I'm a little curious about handling different Guice modules at the same time as NB/maven modules. I've already tackled the issue you spoke of above. Could you go into how you actually used Guice in the application some more?

    ReplyDelete