Oct 16, 2014

Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDI

This is a detailed case study about the migration of a JSF-based web application from Spring 3 to Java EE 7 and CDI. I presented this talk at the JavaOne 2014 conference.

At first sight this didn’t seem to be too difficult. Both technologies are based on similar concepts and provide similar mechanisms for dependency injection (DI). So migrating pure annotation based bean wiring from Spring to CDI was obvious and straight forward.


But the deeper we looked into the source code the more Spring specific code we found with no direct and easy mapping in neither of JEE7, EJB3 or CDI available. Some of the problems and challenges we were confronted with were:

  • How to migrate XML based bean wirings? 
  • How to migrate FactoryBeans? 
  • How to migrate AspectJ based AOP proxies? 
  • How to implement Spring profiles for environment based wiring? 
  • How to implement custom bean scopes? 
  • How to implement custom Spring XML schema notations and wiring? 
  • How to migrate code that is built against Spring APIs? 
  • How to migrate unit test based on Spring Test?

To answer these questions the talk will present the patterns and strategies used to map, transform and migrate the different concepts from Spring 3 to JEE7with CDI. The talk will discuss the changes and implications of the migration on the system’s architecture.
You can find more details abou the talk in the JavaOne content catalog. The final slides are also available at Speaker Deck and SlideShare.

Using CDI annotations from Spring

A question from the audience was, if it is possible to do the migration and still be able to implement upcoming features in parallel. The answer was: yes, this is technically possible.

First of all, my advice would be to perform the migration as quickly as possible. Also try to keep the change set of the newly developed features as small as possible to reduce the merge efforts to an absolute minimum. Use a separate SVN or Git branch for further development and perform the migration directly in your trunk or master branch.

The Spring Framework uses a ScopeMetadataResolver to resolve the Spring based scopes of any bean definition. Since Spring 3 there already is a JSR330 supporting implementation: Jsr330ScopeMetadataResolver. But, this implementation only supports the @Singleton annotation per default, so we need to extend it a little.
/**
 * A custom CDI scope metadata resolver implementation. 
 */
public class CdiScopeMetadataResolver 
                           extends Jsr330ScopeMetadataResolver {
    /**
     * Register additional CDI scope annotations with their Spring 
     * scope equivalent.
     */
    public CdiScopeMetadataResolver() {
       registerScope("javax.enterprise.context.ApplicationScoped", 
               BeanDefinition.SCOPE_SINGLETON);
       registerScope("javax.enterprise.context.Dependent", 
               BeanDefinition.SCOPE_PROTOTYPE);
       registerScope("javax.enterprise.context.SessionScoped", 
               "session");
       registerScope("javax.enterprise.context.RequestScoped", 
               "request");
    }

    @Override
    public ScopeMetadata resolveScopeMetadata(BeanDefinition bean) {
       ScopeMetadata metadata = super.resolveScopeMetadata(bean);
       String scopeName = metadata.getScopeName();

       // CDI always uses scoped proxies except for @Dependent
       if (!BeanDefinition.SCOPE_PROTOTYPE.equals(scopeName)) {
          metadata.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS);
       }

       return metadata;
    }
}
Basically, what this implementation does it to translate the CDI scope annotations to their Spring scope equivalent. Your newly developed components can now be annotated with CDI scope annotations instead, but under the hood you are still using Spring. That is the trick. Once you are done with the migration, these components should work without any further migration efforts. All that is left to do in your Spring code is to activate the scope resolver in your component scan definition:
<context:component-scan base-package="your.package.here"
    scope-resolver="...CdiScopeMetadataResolver"/>

Using Arquillian with Spock and the Shrinkwrap Groovy DSL

In my talk I described the migration of Spring Testing Framework based integration tests towards Arquillian as quite painful and cumbersome. At the end of my session Reza Rahman asked me what my experiences with Arqillian were.

Don't get me wrong now: Arquillian is a great framework for in-container testing of your CDI enabled artifacts and is by far the best I found. But: the shrink wrap process to build a testable and isolated CDI archive is very elaborate. Shrinkwrap might have a nice fluent API to do this but still you have to write a lot of Java code. So how can we improve this?

Testing Java code using Groovy, and especially using the Spock Framework, is in my opinion the way how tests should be written nowadays. Your tests are more expressive, you need less code and it is more fun writing Spock specifications than it is writing plain old JUnit tests.

So after searching the WWW for a few minutes I discovered that there is an offical Arquillian Spock Testrunner. This already felt a lot better, but still not 100% perfect. After a few more minutes I then discovered that there is a Shrinkwrap Groovy DSL that enables you to describe Shrinkwrap archives using Groovy closures. Now have a look at this:
@RunWith(ArquillianSputnik.class)
class TestableCdiArchiveSpec extends Specification {
    @Deployment
    def static JavaArchive "Create testable CDI archive"() {
        ShrinkWrap.jar {
            packages true, "your.package.here"
            asManifestResource EmptyAsset.INSTANCE, "beans.xml"
        }.build()
    }
}

Awesome and Groovy!

No comments:

Post a Comment