Tuesday, May 17, 2011

Annotation scanner with Spring

Spring framework, from version 2.5, can retrieve bean based on an annotation set such as @Service, @Repository. This mechanism uses an annotation scanner to retrieve the annotated classes through the classpath.

As Spring is easily extendable, it is possible to work with an annotation scanner dedicated to our own annotations.

The annotation

The annotation we want to use as a class marker, is not a psecial Spring define one. The only requirement is the retention must be runtime and the target must be at least TYPE.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation { }

The scanner

Firstly, we need to create an application context:

GenericApplicationContect applicationContext = new GenericApplicationContext();

And now, the annotation scanner:

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(applicationContext, false);

An annotation scanner needs an application context to store retrieved classes. The second parameter is used to avoid this scanner search after Spring framework defined annotations. Now, we must configure the scanner to retrieve only our annotation:

scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class));

Several filters are available to include or exclude classes from the search, such as RegexPatternTypeFilter.

To trigger the scanning, we must call scan method with a package list as string array:

scanner.scan(packageNames);

Now, just refresh the context and get the annotated instance:

applicationContext.refresh();
Map<String, Object> result = applicationContext.getBeansWithAnnotation(MyAnnotation.class);

The map key is the bean Spring id, and the value is an instance. By default, the id is the simple class name.

Thursday, March 3, 2011

Parameterized Unit Tests

Recently, I have to write a unit test to check link reference validity in static HTML pages. The problem is: how to run test for each URL, without hard coded HTML files location (what append if several files are added ;) and with a minimum of code. The answer is provided by JUnit itself. From version 4, parameterized tests are included: it is a runner that is able to get a collection of values and to execute a test for each of these values.

A parameterized test looks as following:

@RunWith(Parameterized.class)
public class ParamTest {

private String msg;

public ParamTest(String msg) {
this.msg = msg;
}

@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {{"first"}, {"second"}, {"third"}});
}

@Test
public void test(){
System.out.println(msg);
}

}

To get a parameterized test, it has to declare a specific runner with: @RunWith(Parameterized.class). And a static method that return a Collection<Object[]>. By this way, several arguments can be passed. This method must be annotated with @Parameters that declares the method as a parameters provider. A constructor with all arguments as parameters must be written. Then runner will execute the parameters method provider, and for each value, it creates an instance of the test and execute it. So, to use parameters, the constructor can save them as class attributes.

The result is:

first
second
third

Parameterized tests mechanism brings a simple and ellegant way to test things that are not fix.

Sunday, February 13, 2011

Emulate a non supported class in GWT

To develop with GWT, only a Java SDK subset is available, plus GWT specific classes. Mainly (look at JRE emulation for details):

  • java.lang
  • java.lang.annotation
  • java.util
  • java.io
  • java.sql

This emulation mechanism is used to bypass GWT limitations, such as, no reflection due to the JavaScript translation.

As GWT support only a few JDK classes, as developers, we often miss something to do the work. To fill the gap, it is easy to use emulation to implement a full GWT compliant class. To do so, the best practice is to create a directory out of the class path named super, that will contain the emulations. The name of this directory has no importance, it is just done that way in GWT sources. Your package structure takes place under this directory. At this place, a module must be created, it contains only a super-source directive as following:

<super-source path="source_location"/>

path attribute provides the root directory of simulated classes. For example, to simulate a class from package java.io:

The reason to not include emulated classes in classpath, is, these classes must declare the package name from the class they emulate, not the package previously defined:

package java.io;

Now, to use these classes, you should only inherits from the emulation module.