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?
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.