Blog

Using Spring JavaConfig on Google App Engine

17 Jan, 2010
Xebia Background Header Wave

Recently, I put together a Spring demonstration for jclouds, the Java cloud library. This quickly turned into unexpected multi-dimensional experiment in integrating Guice, Google App Engine and Spring, but after much trial-and-error I finally came across a configuration that does the trick – or at least works1 as well as seems possible on GAE.

The tweetstore demo

Tweetstore is a simple web application that demonstrates jclouds’ cloud capabilities. It queries Twitter for mentions of the user’s account and creates a "backup" of this priceless record of your contemporary image in three cloud stores: Amazon S3, Microsoft’s Azure and Rackspace. Nothing less than double redundancy is good enough for your popularity!
The original tweetstore application uses Guice for dependency injection and request mapping. In order to make it as easy as possible to compare the Spring version to the original, I decided to try to similarly do as much in Java code as possible. A perfect opportunity for me to get my hands dirty with Spring 3.0’s support for Java configuration.

Bootstrapping Spring’s Java Configuration

The first, relatively straight-forward step was to convert the Guice servlet module into an equivalent Spring @Configuration:
[java]
@Configuration
public class SpringServletConfig extends LoggingConfig implements ServletConfigAware {
private ServletConfig servletConfig;

@PostConstruct
public void initialize() {

}
@Bean
public StoreTweetsController storeTweetsController() {
StoreTweetsController controller = new StoreTweetsController(providerTypeToBlobStoreMap,
container, twitterClient);
injectServletConfig(controller);
return controller;
}
@Bean
public AddTweetsController addTweetsController() {
AddTweetsController controller = new AddTweetsController(providerTypeToBlobStoreMap,
serviceToStoredTweetStatuses());
injectServletConfig(controller);
return controller;
}
private void injectServletConfig(Servlet servlet) {
try {
servlet.init(checkNotNull(servletConfig));
} catch (ServletException exception) {
throw new BeanCreationException("Unable to instantiate " + servlet, exception);
}
}
@Bean
ServiceToStoredTweetStatuses serviceToStoredTweetStatuses() {
return new ServiceToStoredTweetStatuses(providerTypeToBlobStoreMap, container);
}
@Bean
public HandlerMapping handlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
Map<String, Object> urlMap = Maps.newHashMapWithExpectedSize(2);
urlMap.put("/store/", storeTweetsController());
urlMap.put("/tweets/
", addTweetsController());
mapping.setUrlMap(urlMap);
/*

  • "/store" and "/tweets" are part of the servlet mapping and thus stripped
  • by the mapping if using default settings.
    */
    mapping.setAlwaysUseFullPath(true);
    return mapping;
    }
    @Bean
    public HandlerAdapter servletHandlerAdapter() {
    return new SimpleServletHandlerAdapter();
    }

    }
    [/java]
    Being ServletConfigAware isn’t exactly Spring best practice but then the chosen aim was to stay as close as possible to the original set-up rather than building a Spring reference application.
    Hooking Spring into the GAE startup was the next step. The Spring reference example was my first attempt:
    [xml]
    <web-app>
    <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!– Configure DispatcherServlet to use JavaConfigWebApplicationContext
    instead of the default XmlWebApplicationContext –>
    <init-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>org.jclouds.demo.tweetstore.config.SpringServletConfig</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>

    </web-app>
    [/xml]
    Unfortunately, this unceremonially blew up in my face with a security violation: AnnotationConfigWebApplicationContext (and all Spring contexts that register a CommonAnnotationBeanPostProcessor) attempts to load javax.annotation.Resource to determine support for JSR-250, and that class, unlike others in the javax.annotation package, happens to be missing from the GAE whitelist.

    It ain’t over until the App Engine sings

    Moving back to a standard -servlet.xml file gets rid of the tt>@Resource</tt problem, but I was still left with an exception caused by Guice, which jclouds uses internally for dependency injection. I later discovered that the exception is relatively harmless and can be ignored2. But of course no developer likes exceptions – however harmless – in the boot logs, so I wondered whether executing this call earlier in the boot sequence might resolve the problem.
    So I moved the offending call from the servlet to the application context, guessing that the ContextLoaderListener might have more permissions than a servlet:
    [xml]
    <web-app>
    <!– can’t use Java sconfiguration due to GAE’s security restrictions –>
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/tweetstoreContext.xml</param-value>
    </context-param>
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>

    [/xml]
    And it worked! Or rather, it worked on the Java Development Server, the App Engine "simulator" provided with the SDK which is intended for development testing. But it didn’t work on App Engine itself3.
    A word of caution, therefore: test on the "real" App Engine before concluding that something works, especially if it’s related to security restrictions!

    Salvaging some @AnnotationConfig

    By this time I knew that I wasn’t going to get away without writing at least a bit of Spring XML, but I was still trying to stick to Java as much as possible. After plenty of digging around in the Spring sources and a bit of experimentation, I discovered that, of the bean post processors currently registered by <context:annotation-config/>4 that I was interested in, only the CommonAnnotationBeanPostProcessor actually causes a problem. In fact, you can even use its immediate superclass, InitDestroyAnnotationBeanPostProcessor, which provides the tt>@PostConstruct</tt and tt>@PreDestroy</tt support I was looking for:
    [xml name="foo"]
    <beans xmlns="…>
    <!– the usual <context:annotation-config/> can’t be used because the
    CommonAnnotationBeanPostProcessor causes a security exception in GAE when it
    tries to load javax.annotation.Resource –>
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
    <bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor" />
    <bean class="org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor">
    <property name="initAnnotationType" value="javax.annotation.PostConstruct" />
    <property name="destroyAnnotationType" value="javax.annotation.PreDestroy" />
    </bean>
    <bean class="org.jclouds.demo.tweetstore.config.SpringServletConfig" />
    </beans>
    [/xml]
    So if you can live without tt>@Resource</tt, it seems that you can get away with most of Spring’s annotation-driven configuration on the GAE.

    Debugging the Java Development Server
    The GAE SDK starts the Java Development Server using a platform-independent launcher called KickStart, which is quite easy to integrate into your test suite.
    Unfortunately, KickStart doesn’t start the server in a new thread, but in a completely separate process, so attaching a debugger from your IDE is, well, tricky.
    Luckily KickStart accepts jvm_flag arguments, as outlined in the Javadocs5:
    At present, the only valid option to KickStart itself is:
    –jvm_flag=<vm_arg>
    Passes <vm_arg> as a JVM argument for the child JVM. May be repeated

    Here you can add the standard JVM debugging parameters.
    Footnotes

    1. Well, actually it’s running into a timeout at the time of writing, but that’s not related to the Spring config. Really 😉
    2. Which is to say that it doesn’t interrupt the bootstrapping process (it occurs in a separate thread), and your application will run fine. Here it is:

      com.google.inject.internal.FinalizableReferenceQueue : Failed to start reference finalizer thread. Reference cleanup will only occur when new references are created.
      java.lang.reflect.InvocationTargetException
      ...
      at com.google.inject.internal.InjectorBuilder.initializeStatically(InjectorBuilder.java:134)
      at com.google.inject.internal.InjectorBuilder.build(InjectorBuilder.java:108)
      at com.google.inject.Guice.createInjector(Guice.java:93)
      at com.google.inject.Guice.createInjector(Guice.java:70)
    3. I suspect this has something to do with how and when the security manager is installed in the Dev Server, but haven’t investigated this in any detail.
    4. The Spring docs don’t mention ConfigurationClassPostProcessor, for some reason, but according to its Javadoc it is “Registered by default when using <context:annotation-config/> or <context:component-scan/>.”
    5. Which don’t appear to be available online, for some reason.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts