Showing posts with label guice. Show all posts
Showing posts with label guice. Show all posts

Tuesday, 4 September 2012

Reusable FXML Controls with Guice

Update:
This code is now integrated into the fx-guice project where it will be maintained.  I am not intending to modify the GitHub gists so this post now only serves to document the code as seen here.



It is very simple to inject controllers into an FXML control using Guice but once the UI starts to get bigger it is likely that we will need to split it out into smaller files. I wanted to do this with a list builder control simple to that in SceneBuilder used to build a list of CSS classes.

In my UI the controller of the parent control (called parent controller from now on) would tell the list builder control what items it could add to the list and what items were already in the list using the list builder's controller (the child controller).  Getting this to work this is far from simple.  

It is not possible to get or set a controller on an arbitrary control node.  I fired up VisualVM to see where the list builder controller was referenced and the only incoming references were to the event handlers I had set up.  If there were no event handlers it looks like the controller would get garbage collected.  It was also not possible to set a Guice singleton scope on the controller as there may be several list builders in the user interface at the same time.

Here is a run down of my final solution although it is not altogether as simple as I would have liked it.  This tutorial is much more complex than my previous FXML/Guice post, I had to look up my own tutorial on Guice scopes to work it out.  Please let me know if anything is not clear

1. Add the Guice Module

Add the following Guice module to your Injector.  This example also assumes that you are using the same Injector to inject controllers when loading FXML, this is detailed on my previous Guice/FXML post.

The module uses the following Guice scope to track all injected controllers.

2. Use a scope annotation on the controller

Reusable controllers are annotated with the custom @FXMLLoadingScope.

3. Implement the IdentifiableController interface in the controller

Reusable controllers must implement the IdentifiableController interface which has one method, #getId().  #getId() must return a non-null String containing the ID of the parent of the root pane of the control.  If this ID is null then return the second parent-parent's ID.  Keep going until a non-null ID is found.

4. Set an ID on the parent

When importing one FXML file into a parent, set the container ID to the ID you want for the controller.

5. Retrieve the child controller

In our parent controller (the one that that will tell the list builder what to do) we need to retrieve the list builders controller to populate options and so on.  To do this, Inject an instance of ControllerLookup into the parent controller.
In the parent controller, make sure you implement the Initializable interface.  In the initialise method of the parent controller call ControllerLookup#lookup(String) on the instance you have injected to retrieve the child controller.  The ID that is passed in should be the ID that is returned by IdentifiableController#getId().  This could be hard coded but in this example it is using the FXML ID of the parent control, "factorsList" in this case.  This allows several list builders to be used in the same parent control.

I am using this in my own JavaFX project and although the implementation was not simple I have found it quite easy to use.

Sunday, 1 July 2012

Creating JavaFX Controllers Using Guice

I really like Guice Dependency Injection so was pleased to hear that some work has been done to make this interact with JavaFX. The solution presented though, was less than ideal. It had some duplication of configuration where the class name of the controller needed to specified in the FXML as you would expect but also when loading it. After a brief fumble through FXMLLoader's methods I found #setControllerFactory. By passing this wafer thin adapter below as a parameter to #setControllerFactory, any controllers will be instantiated via Guice.

Sunday, 25 September 2011

Detangling Dependencies Between Guice Injected Services

It is very easy to allow services to depend on each other, even when they are in different modules.  In fact Guice explicitly supports this behaviour with the AbstractModule#requiredBindings method.  I think this is very bad and in my experience has led to some complex interactions and strict requirements on order of creation.  Even worse, in some cases you may require a cyclic relationship to fulfill what you want to do which means you can't fully initialise the services via constructors.

Instead of a clever technical solution, I believe it is the software design that is incorrect.  The issue can be resolved using SOLID principles to identify and rectify the issue.

The services require the dependencies because they are trying to do too much, this is violating the Single Responsibility Principle (SRP).  I often think of SRP as a DON'T whereas the Interface Segregation Principle (ISP) is a DO: to fix an SRP issue apply ISP and move that behaviour out into new classes.

This still hasn't fixed the issue yet because those dependencies still have to be wired up in the modules, except you don't.  The new classes with dependencies can be instantiated via Guice when required with injected dependencies, the code to wire them up in the modules can be removed altogether.

In this way, layers of objects are being created.  The bottom layer of services are injected into the next layer of composite services which are then injected into the application.  This is the Open Closed Principle (OCP) where classes should be open for extension but closed for modification.  In other words, prefer to building on top of existing classes instead of modifying them.

I do suggest that all layers can talk to each other unless the software is huge enough to justify such abstraction.  Also, be sceptical of composite services which simply provide a layer over another service and perhaps wrap exceptions or even provide a something useful.  In this case a new API has been created to replace a more universal one.  Ask if the new API is actually better than what it is truly wrapping and it is worth the new layer being imposed.

Guice Custom Scopes

Guice custom scopes are, for some reason badly explained in almost all web documentation that I was able to find.  All implementations cover extra Guice concepts or muddy the code with concurrency handling while not describing the main concept which is surprisingly simple once realised.

A scope is a condition for reusing injected objects, instead of creating a new one each time.  For example, if a network connection was being injected then the scope would be while the network connection was open, reuse the object.  When it is closed, open and inject a new one.

The implementation for this is to basically provide a factory object,  implementing the Scope interface.  This has one method:

Provider scope(Key<T> key, Provider<T> unscoped)

The Key object defines the injected type, the unscoped Provider can be used to create a new instance.  Typically the returned Provider implementation will  create the required object using the unscoped Provider and keep a reference to it while in scope.  When the object is requested again the stored object is returned if still in scope.  This example usage above is how I needed to use custom scopes but it is flexible enough to do all kinds of weird things.

Here is the Scope implementation.  It is a bit more complex than necessary because for this example I wanted to allow objects to be created out of scope.

Here is out annotation which we will use on classes that can be stored within the scope.
The interface to inject:
The class that implements that interface to be injected.  Note that we have annotated this with our @BookScoped annotation.  The constructor will display new objects being created.
Now our Guice module.  The scope annotation is bound to an EnterableScope instance which we'll keep a hold of.
Finally the main class to demo the scope.
When the main class is run it outputs the following to the command line:

entering scope
created customscopes.Hobbit@12a54f9
exiting scope
created customscopes.Hobbit@30e280
created customscopes.Hobbit@16672d6
created customscopes.Hobbit@fd54d6
created customscopes.Hobbit@1ccb029

When within scope the four calls to #getInstance only create one instance, when not in scope a new instance is created each time.  That's a whole lot of code although it is all fairly simple.  From a maintainability viewpoint I do see some issues with this, scopes are so arcane to anyone not experienced with Guice that it would be quite easy to misuse a scope which could cause all kinds of issues with retained state and possibly memory leaks.  All I can suggest is document everything and get this over to the team.