Search code examples
javatomcatspring-bootjarclassnotfoundexception

Spring Boot with External JAR gets NoClassDefFoundError


So I have a set of subprojects in my main project. The first project (called mainCode in the example below) implements the full functionality of the application. However, the WidgetAImpl and WidgetBImpl are separated into separate subprojects so that they can be deployed independently when they need to be fixed. The WidgetAImpl and WidgetBImpl classes reside in the same package as the interface WidgetInterface.

Here's the project layout:

project
    + mainCode
        + /src/main/java/com/mycompany/myproject/Application.java
        + /src/main/java/com/mycompany/myproject/AppConfig.java
        + /src/main/java/com/mycompany/myproject/widgets/WidgetInterface.java
    + widgets
        + WidgetA
            + /src/main/java/com/mycompany/myproject/widgets/WidgetAImpl.java
        + WidgetB
            + /src/main/java/com/mycompany/myproject/widgets/WidgetBImpl.java

I have gradle building these in such a way that the mainCode project becomes our war file for deploying to tomcat and the widgets each turn out a jar file. The goal is for the jar files to be added to tomcat's classpath and the war file is deployed separately. I spent the day getting Gradle to build the WidgetAImpl project by declaring the mainCode project a dependency. So Gradle builds them properly now and I have a mainCode.war and a WidgetAImpl.jar and a WidgetBImpl.jar file. In the implementation, the widgets are all clumped together with an Autowired HashMap so that all implemented Widgets are created in a single HashMap at run time. I've added the widgets to tomcat's classpath through catalina.properties sharedLoader property.

My problem is, when I deploy my war file to tomcat, I get the following error (trimmed for space):

2017-11-15 21:55:39.841  WARN 16687 --- [nio-8443-exec-4] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.mycompany.myproject.widgets.WidgetAImpl] for bean with name 'WidgetAImpl' defined in URL [jar:file:/mycompany/libs/WidgetAImpl.jar!/com/mycompany/myproject/widgets/WidgetAImpl.class]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: com/mycompany/myproject/widgets/WidgetInterface
2017-11-15 21:55:39.845 ERROR 16687 --- [nio-8443-exec-4] o.s.b.f.s.DefaultListableBeanFactory     : Destroy method on bean with name 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor' threw an exception

java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@3633386f: startup date [Wed Nov 15 21:55:38 UTC 2017]; root of context hierarchy
    at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:404) [spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97) ~[spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:253) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:954) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:961) [spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1033) [spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:555) [spring-context-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) [spring-boot-1.4.2.RELEASE.jar:1.4.2.RELEASE]


...


Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [com.mycompany.myproject.widgets.WidgetAImpl] for bean with name 'WidgetAImpl' defined in URL [jar:file:/mycompany/libs/WidgetAImpl.jar!/com/mycompany/myproject/widgets/WidgetAImpl.class]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: com/mycompany/myproject/widgets/WidgetInterface
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1364) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:639) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:607) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1456) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:420) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:390) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:220) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1243) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1164) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1089) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1059) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:663) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    ... 68 common frames omitted
Caused by: java.lang.NoClassDefFoundError: com/mycompany/myproject/widgets/WidgetInterface
    at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_144]
    at java.lang.ClassLoader.defineClass(Unknown Source) ~[na:1.8.0_144]
    at java.security.SecureClassLoader.defineClass(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader.defineClass(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader.access$100(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader$1.run(Unknown Source) ~[na:1.8.0_144]
    at java.net.URLClassLoader$1.run(Unknown Source) ~[na:1.8.0_144]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_144]
    at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.Class.forName0(Native Method) ~[na:1.8.0_144]
    at java.lang.Class.forName(Unknown Source) ~[na:1.8.0_144]
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1271) ~[catalina.jar:8.5.20]
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119) ~[catalina.jar:8.5.20]
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:250) ~[spring-core-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:394) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1408) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1353) ~[spring-beans-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    ... 79 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.mycompany.myproject.widgets.WidgetInterface
    at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_144]
    ... 98 common frames omitted

(Full Exception Stack Trace here: https://pastebin.com/KwaNGaeN)

It appears from this that Spring can't find my WidgetInterface. However, I've confirmed that the WidgetInterface classfile is inside the war file. Also, it's the same java package as the WidgetAImpl and WidgetBImpl (even though they're in separate files). Why can't it see the WidgetInterface?


Solution

  • Can you create common, or base module, with that WidgetInterface.java? Add it to Tomcat as widgets. Then all other jars, and war can rely on that, common part. Tomcat will have clean path how to load it, what is used by which other module.