Thursday, December 22, 2011

How to Resolve Spring Data Neo4j / Jersey / Jackson Conflict with JBoss AS 7 RESTEasy

I'm using Spring Data Neo4j REST Client which uses Jersey JAX-RS Client. Unfortunately when deployed to JBoss AS 7.0.2, it conflicts with the built-in RESTEasy deployer (bug DATAGRAPH-159).

In order to run the application, jersey-server must be excluded :

<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-neo4j-rest</artifactId> <version>${spring-data-neo4j.version}</version> <exclusions> ... <exclusion> <artifactId>jersey-server</artifactId> <groupId>com.sun.jersey</groupId> </exclusion> </exclusions> </dependency>

Another issue I came across is my web application is also a JAX-RS Service Application, therefore it requires JBoss RESTEasy. Unfortunately Jackson JSON Provider which is a dependency of Neo4j REST Client and Jersey JAX-RS Client conflicts with RESTEasy's Jackson Provider, with the following exception stacktrace message:

java.lang.RuntimeException: Unable to instantiate MessageBodyReader  org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:760)  org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:742)  org.jboss.resteasy.spi.ResteasyDeployment.registerProvider(ResteasyDeployment.java:505)  org.jboss.resteasy.spi.ResteasyDeployment.registration(ResteasyDeployment.java:305)  org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:225)  org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:67)  org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)  org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:139)  org.jboss.as.web.NamingValve.invoke(NamingValve.java:57)  org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)  org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362)  org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:897)  org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:626)  org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:2054)  java.lang.Thread.run(Thread.java:722)

root cause

java.lang.RuntimeException: Illegal to inject a message body into a singleton into public org.codehaus.jackson.jaxrs.JacksonJsonProvider(org.codehaus.jackson.map.ObjectMapper,org.codehaus.jackson.jaxrs.Annotations[])  org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:209)  org.jboss.resteasy.core.ConstructorInjectorImpl.injectableArguments(ConstructorInjectorImpl.java:63)  org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:129)  org.jboss.resteasy.spi.ResteasyProviderFactory.getProviderInstance(ResteasyProviderFactory.java:1038)

I thought this was issue RESTEASY-503, because JBoss AS 7.0.2 happened to use the somewhat buggy RESTEasy 2.2.1.GA. But it turns out there is an easy fix to this problem, thanks to Configuring Module Classloading in JBoss AS 7.

Edit src/main/webapp/WEB-INF/jboss-deployment-structure.xml as follows:


<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">

<deployment>

<dependencies>

<module name="org.codehaus.jackson.jackson-jaxrs"/>

<module name="org.codehaus.jackson.jackson-core-asl"/>

<module name="org.codehaus.jackson.jackson-mapper-asl"/>

</dependencies>

</deployment>

</jboss-deployment-structure>

Now Spring Data Neo4j, Jersey Client with JSON Jackson Provider, and my JAX-RS Application served by RESTEasy, all can coexist in the same web application WAR. :-)

To learn more about Java Web Development using Java EE 6, I highly recommend The Java EE 6 Tutorial: Basic Concepts (4th Edition) (Java Series) by Eric Jendrock, Ian Evans, Devika Gollapudi and Kim Haase.