Friday, October 28, 2011

Guiced Configuration

Google guice and apache commons-configuration are two of large set of common libraries which I'm using in my own projects. While working one of projects I just thought that it would be pretty natural to tie those components together so guice can configure managed instances using data from configuration.
Integration was done in three iterations. Each iteration has it's pros and cons, so I'll describe each one.
Iteration 1. Injecting Configuration.
First of all I have implemented guice provider which initialized configuration object. Provider was mapped into one of modules in singleton scope. Each class relying on configuration was begging for injection of configuration object. Then it was the object's work to configure itself properly. 
I didn't like this approach right from the beginning. Let's go through pros and cons briefly:
Pros: 
  • Solution is simple and is very quick to implement.
Cons:
  • Each class relying on configuration became dependent on Configuration interface.
  • Each class contains code that maps fields to configuration properties. Code is repeated from class to class.
  • Testability is affected. All classes that need configuration must be provided with either special test configuration instance or appropriate mock.
Looking into pros and cons I understood that that isn't what I really want. First of all I'd like to quit passing Configuration objects and rather map fields directly.
 Iteration 2. Named constant bindings.
That's where named annotations steps in together with constant bindings. We need few steps to perform to get things working.
  1. Initialize Configuration.
  2. Use bindConstant together with Names.named bindings to bind configuration properties to some names.
  3. Use @Inject annotation together with @Named annotations to annotate fields (or setters, that is event better) that should be injected with configuration values.
Much better now. We break dependency on Configuration interface and injecting target fields instead. And now Guice is doing configuration injection for us. So no need for object to do it. On the other hand we pay for this by binding each configuration parameter as constant. And if you have large configuration that can be pretty annoying thing.
There are some methods to address this problem, and one of them is custom injection annotation.
Iteration 3. Annotation injection.
The main idea behind annotation injection is to create special annotation that will inject configuration properties to fields. As opposite to previous iterations it's not out-of-the-box solution. But it still worth trying.
OK, what we going to do is to create @InjectConfig annotation which is provided with name parameter. Name reflects property name in configuration. Also some kind of defaultValue field should be provided to handle injection of missing properties.
Custom annotation approach is described in integrating log4j example. We need to implement three components: Annotation itself, TypeListener object that will detect places where we should inject configuration properties and MembersInjector that will do injecting work for us. Let us start with annotation:



Name property should be filled with configuration property name. Rest of fields are used to provide default value that will be injected in case of missing configuration property. All default... are provided with default values so it is optional to specify. Converter attribute is used to provide converter from string to type expected by field. By default dummy implementation of String-to-String can be used. Iterface of converter is like that:

Next thing to create is TypeListener:
TypeListener should be populated with initialized Configuration instance in order to provide member injectors capable of injecting configuration properties. TypeListener's hear method is invoked on each class initialized by Guice injector. It iterates through all declared fields and provides injector for each field annotated by @InjectConfig. Guice injector will use provided member injector instance to inject field value. Member injector is the place where injection happens. No magic there, let's look into:

Well, this one is bigger than previous. Don't be afraid, I'll explain everything. When time comes to do field injection Guice calls injectMembers method providing it with instance under injection. InjectMembers retrieves @InjectConfig annotation from processed field. Now we can retrieve configuration property name and default values from annotation. Second step to do is to detect type of injected field. We can do it by using getType() method of field. Retrieving type is followed by many if-elses that are used to inject primitive types. If the field type isn't primitive than converters come into playground. For non-primitive field getProperty method is called. It retrieves specified configuration property (or default value on missing property) as string. After that converter instance is created. Convert method invoked on converter resulting in object capable for field injection. Then execution is passed back to injectMembers method and object become injected.
All necessary objects are ready. The only thing left is to tell Guice to use them. We should do this in Guice configuration module. It can look like this:

There are only two things we should do (or even one, because binding Configuration is fully optional). Look to line which starts with bindListener. It installs new type listener. Listener's hear() method is invoked on each detection of class that must be initialized with Guice. 
What we get now is the configuration injection which is as simple as annotating field with @InjectConfig annotation:

Happy coding, folks!

Friday, April 15, 2011

Using maven to build java+groovy projects

I have just started new project where I plan to use Java and Groovy at the same time. Groovy will be used primarily for implementing configuration DSL, and Java will implement core functionality. So I need some way to join Java and Groovy sources in one project.
I'm using maven for building, so it was natural to start searching plugin for maven. Well, after some minutes I have found Gmaven
First of all modify your pom.xml by adding Gmaven plugin

<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>

In order to make it compiling and running we need also groovy libraries

<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>1.7.10</version>
</dependency>

The last thing to do is to create folders for groovy sources. Just make /src/main/groovy for sources and /test/main/groovy for tests

Thursday, July 1, 2010

Hello, world!

Это первый пост моего блога, который, в общем, совсем ни о чем. Я имею в виду пост - сам блог будет посвящен программированию вообще и языку Java в частности. Правда вполне возможно, что будет здесь место и для всяких размышлизмов и наблюдений в других плоскостях.
Немного о себе: я Java-разработчик с почти 6-летним стажем, разрабатываю в основном web-приложения. Блог о Java завел скорее всего чтобы систематизировать накопленный опыт и поделиться им, а также дабы отмечать новые находки в области программирования.
Комментирующим, ежели такие вдруг появятся, адресую просьбу не стесняясь исправлять ошибки и критиковать изложенное. Буду рад, если какие-то из материалов пригодятся кому-либо.
Ну, думаю для вступления вполне достаточно :)