TornadoFX & Guice

KeepassFX contains the UI running in the JavaFX thread and an embedded jetty server running in its own thread. They both need access to the same data. To make this possible, I use Guice to inject dependencies into the code. Using Guice with TornadoFX is quite easy.

configuring Guice

Guice needs to be started with one or more classes that implement either AbstractModule or ServletModule.

class GuiceConfigurator : AbstractModule() {
    override fun configure() {
        logger.info("setting up guice for normal fx injection")
        bind(BridgeService::class.java).to(BridgeServiceFX::class.java)
    }
}

BridgeService is a Kotlin interface, BridgeServiceFX is the implementation. The bind(BridgeService::class.java).to(BridgeServiceFX::class.java) informs Guice how to resolve the interface. Because BridgeService is a Kotlin class we need to use ::class.java to tell the Kotlin runtime to serve it as a java class instead of a Kotlin class.

class GuiceServletConfigurator : ServletModule() {
    override fun configureServlets() {
        logger.info("setting up guice for jetty")
        serve("/*").with(CommandServlet::class.java)
    }
}

This class sets up the routing for Jetty. In the ContectHandler of the jetty server a filter supplied by juice is setup:

public void start() throws Exception {
    server = new Server(preferences.getHttpPort());
    ServletContextHandler servletContextHandler = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
    servletContextHandler.addFilter(GuiceFilter.class, "/", EnumSet.allOf(DispatcherType.class));
    servletContextHandler.addServlet(DefaultServlet.class, "/");
    server.start();
}

Now that the configuration classes are made we can bootstrap guice

val guice = Guice.createInjector(GuiceConfigurator(), GuiceServletConfigurator())

Guice is up and running.

Linking guice to tornadoFX.

TornadoFX has excellent support for CDI frameworks like Guice or Spring. Where tornadoFX’s own injection is done with the by inject() pattern. Guice or Spring beans are injected using by di().

tornadoFX however needs to know how to get a bean from the CDI framework. For this it has a dicontainer object where we can override the getInstance function. For Guice this is:

FX.dicontainer = object : DIContainer {
    override fun <T : Any> getInstance(type: KClass<T>): T = guice.getInstance(type.java)
}

The di() function will use this getInstance to resolve the injection.

Using it

In tornadoFX this is used as follows:

val repository: KeepassRepository by di()

In the jetty server thread (which is still written in java):

@Inject private KeepassRepository keepassRepository;

Conclusion

CDI framework support is very good with tornadoFX and makes it a breeze to setup. If you do a setup like this where two threads have access to the same object, you have to make sure the access to it is threadsafe. Guice or tornadoFX will not do that for you.

comments powered by Disqus
comments powered by Disqus