Thursday, April 8, 2010

Make @Configurable work with Spring 2.5.6 without @DirtiesContext

At a recent project we finally went for compile time weaving and bumped into the following issue: @Configurable injects sometimes the incorrect version of a certain dependency.

Spring's @Configurable is based upon a static aspect.

Hence you might end up in the following scenario:
  • Test1 uses Ctx1
  • Test2 uses Ctx2
  • Test3 uses Ctx1
In case the execution order of the tests is Test1, Test2, Test3 and no test is marked with @DirtiesContext then what happens is:
  • Test1 runs

    • Ctx1 is not in the context cache of spring

    • Ctx1 is loaded and the aspect is initialized with that context

    • Ctx1 is put into the context cache of spring

  • Test2 runs
    • Ctx2 is not in the context cache of spring

    • Ctx2 is loaded and the aspect is initialized with that context

    • Ctx2 is put into the context cache of spring


  • Test3 runs
    • Ctx1 is inside the context cache of spring

    • Ctx1 is retrieved from the cache and the aspect is still pointing to Ctx2


We solved this issue by reinitializing the context the aspect is pointing to, by extending the SpringJUnit4ClassRunner and registering a custom TestExecutionListener




package x.y.z;

import org.aspectj.lang.Aspects;
import org.junit.internal.runners.InitializationError;
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractTestExecutionListener;

public class MySpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {

public MySpringJUnit4ClassRunner(Class clazz) throws InitializationError {
super(clazz);
getTestContextManager().registerTestExecutionListeners(new AbstractTestExecutionListener() {
@Override
public void prepareTestInstance(TestContext testContext)
throws Exception {
Aspects.aspectOf(AnnotationBeanConfigurerAspect.class).destroy();
Aspects.aspectOf(AnnotationBeanConfigurerAspect.class)
.setBeanFactory(((GenericApplicationContext)testContext.getApplicationContext()).getDefaultListableBeanFactory());

}
});
}

}

Display in a JSF page properties from MANIFEST.MF using spring framework

In a recent project we wanted to show the Hudson build info in the footer of the template.

So we configured the maven pom to execute the

<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-war-plugin</artifactid>
<version>2.0.2</version>
<configuration>
<manifest>
<adddefaultimplementationentries>true</adddefaultimplementationentries>
</manifest>
<archive>
<manifestentries>
<specification-title>${project.name}</Specification-Title>
<specification-version>${project.version}</Specification-Version>
<implementation-version>${BUILD_TAG}</Implementation-Version>
<build-final-name>${project.build.finalName}</build-final-name>
</manifestentries>
</archive>
</configuration>
</plugin>


to ensure the data is saved inside the META-INF/MANIFEST.MF.

To show the data in the screen we relied upon the InputStreamPropertyEditor of the Spring framework defining a bean

<bean id="manifestFile" class="java.util.jar.Manifest">
<constructor-arg value="/META-INF/MANIFEST.MF" type="java.io.InputStream"/>
</bean>


and in the footer part of the facelets template we simply wrote:

hudson build tag: #{manifestFile.mainAttributes.getValue('implementation-version')}


Note: we used jboss-EL that allows evaluation of method calls.