spring-mvcjacksonsakai

ClassNotFoundException using Jackson as message converter with Spring MVC in a Sakai tool


I'm trying to create a REST web service using Spring MVC. I created a barebones Sakai tool using the Spring MVC Maven archetype.

I have defined my POJO, and want to have Spring convert them to JSON automatically and return them from my controller. I've created a controller:

@Controller
public class JSONController {

    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public @ResponseBody
    Test getJSON() {
        return new Test;
    }
}

When I add a dependency on Jackson (I've tried v1 and v2), and add <mvn:annotation-driven> and <context:component-scan base-package="package.name" /> to the web.xml, I immediately get this ClassDefNotFoundError:

Aug 06, 2015 3:01:04 PM org.apache.catalina.startup.HostConfig undeploy
INFO: Undeploying context [/test-app-tool]
2015-08-06 15:01:04,372  INFO ContainerBackgroundProcessor[StandardEngine[Catalina]] org.sakaiproject.util.ContextLoaderListener - Destroying Components in sakai.test-app
Aug 06, 2015 3:01:04 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive {path-to}\tomcat\sakai-10.4\webapps\test-app-tool.war
2015-08-06 15:01:05,645  INFO localhost-startStop-2 org.sakaiproject.util.ToolListener - registering tools from resource: /tools/sakai.sakai-spring-maven-archetype.xml
2015-08-06 15:01:06,060 ERROR localhost-startStop-2 org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0': Cannot create inner bean '(inner bean)' of type [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter] while setting bean property 'messageConverters' with key [6]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#8': Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/JsonProcessingException
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:282)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:126)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:353)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:154)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1387)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1128)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:626)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:651)
    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:599)
    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:665)
    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:518)
    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:459)
    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
    at javax.servlet.GenericServlet.init(GenericServlet.java:158)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1087)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5229)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5516)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1083)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1880)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#8': Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/JsonProcessingException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1007)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:953)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:487)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:271)
    ... 37 more
Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/JsonProcessingException
    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2585)
    at java.lang.Class.getConstructor0(Class.java:2885)
    at java.lang.Class.getDeclaredConstructor(Class.java:2058)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1000)
    ... 41 more
Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.core.JsonProcessingException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    ... 47 more

As I understand it, if Jackson is found in the classpath, Spring activates it as a message converter (the MappingJackson2HttpMessageConverter bean referenced in the error). But for some reason it is not finding the JsonProcessingException class. I can see this class file at the correct path in the jackson-core-2.0.2.jar which is present in my webapp's WEB-INF/lib directory. I get the same error concerning the same class with Jackson 1 and 2.

Oddly enough, I have no problem actually importing and using Jackson in my code, so the dependency is there. I've also gotten this approach working in a plain webapp (without any of the Sakai stuff). Why can't Spring wire this bean?


Solution

  • As jonespm pointed out, this issue should be resolved in Sakai as of v11 (which hasn't been released yet) as per this Jira. Integrating the changes from the associated commit resolved the issue.

    Jackson must be deployed to shared since that's where spring is.