user1578872 user1578872 - 2 months ago 27
Java Question

Jackson Databind classpath issue

I have a spring boot app which works fine when i do the deploy using "mvn clean install" in my local, but when the war is generated through Jenkin, it is throwing the following error.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'objectMapper' defined in class path resource [com/test/common/TestRestConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.fasterxml.jackson.databind.ObjectMapper]: Factory method 'objectMapper' threw exception; nested exception is java.lang.NoClassDefFoundError: Could not initialize class com.fasterxml.jackson.databind.SerializationConfig
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 62 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.fasterxml.jackson.databind.ObjectMapper]: Factory method 'objectMapper' threw exception; nested exception is java.lang.NoClassDefFoundError: Could not initialize class com.fasterxml.jackson.databind.SerializationConfig
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
... 74 common frames omitted
Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.fasterxml.jackson.databind.SerializationConfig
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:535)
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:452)


I just tried comparing the 2 war files using beyond compare and i dont see any diff except the JDK minor version being used to compile.

I tried to search for the SerializationConfig.class in my local build and in the jenkin build,

The output for the below command is,

find . -type f -name '*.jar' -print0 | xargs -0 -I '{}' sh -c 'jar tf {} | grep SerializationConfig.class && echo {}'


Local war O/P :-

com/fasterxml/jackson/databind/SerializationConfig.class
./jackson-databind-2.7.3.jar
org/codehaus/jackson/map/SerializationConfig.class
./jackson-mapper-asl-1.9.13.jar
com/fasterxml/jackson/databind/SerializationConfig.class
./jersey-all-2.18.jar


Jenkin war O/P :-

com/fasterxml/jackson/databind/SerializationConfig.class
./jersey-all-2.18.jar
org/codehaus/jackson/map/SerializationConfig.class
./jackson-mapper-asl-1.9.13.jar
com/fasterxml/jackson/databind/SerializationConfig.class
./jackson-databind-2.7.3.jar


Basically, I inject the ObjectMapper in my TestRestConfiguration class as follows,

@Inject
private ObjectMapper objectMapper;


Not sure, why the war file being generated through Jenkin is causing problem.

Any help on this will be appreciated.

Answer

You appear to be pulling the same class (SerializationConfig) from two different jar file dependencies. From your question, it's clear that the one in com.fasterxml.jackson.databind (which is cited in the stack trace) can be found in either the jackson-databind-2.7.3.jar or the jersey-all-2.18.jar:

com/fasterxml/jackson/databind/SerializationConfig.class
./jackson-databind-2.7.3.jar
org/codehaus/jackson/map/SerializationConfig.class
./jackson-mapper-asl-1.9.13.jar
com/fasterxml/jackson/databind/SerializationConfig.class
./jersey-all-2.18.jar

I would first try to pare back your dependencies such that you rely on either jackson-databind-2.7.3.jar or jersey-all-2.18.jar, but not both. If your application will work with one or the other, I suspect that this will fix your issue (although I admit that I might have expected to see Spring's "No unique bean of type ... is defined" message and I did not notice it in your post).

Anyway, if I'm correct, then what you're seeing is an artifact of class loading happening differently in your local environment vs. what happens in the Jenkins-generated artifact and deployed on your server (additional complexity here--you may want to scrutinize your server for any provided libs and understand exactly what order your class loading is happening--not fun, I know).

Peeking at the source code for Jackson's SerializationConfig class, I find the following, which could make things interesting if the classes in the two different jar files are not actually identical..

private static final long serialVersionUID = 1

Hope it helps. Good luck!

EDIT 1:

It might be interesting to set up a pair of builds producing so-called "fat jar" files with some embedded server like Tomcat or Jetty. You might learn something from that if you compare behavior of the one you produce locally vs. the one produced on Jenkins. Do you see the same problem? Using the fat jar files, you have more explicit control over the deployed environment than you do if you deploy into an existing (and pre-configured, alterable) container.

EDIT 2:

Couple of things you might to do help figure out your environment differences..

mvn dependency:tree

or, if you have a lot of patience

mvn -X
Comments