Search code examples
javamavenjakarta-eeear

Maven EAR dependency structure


I'm trying to deploy an EAR to Wildfly 10 via Eclipse Neon with the latest JBoss Tools. This is my first attempt with EARs, so I documented myself and this is the structure I came up with:

Maven modules:

  • api (type jar, no deps): contains the interfaces MyService and Person.
  • ejb (type ejb, depends on api): contains a @Stateless implementation of MyService and an @Entity implementation of Person.
  • war (type war, depends on api): contains a JAX-RS resource which uses MyService.
  • ear (type ear, depends on ejb and war): the EAR module.

(See here for the full source: https://github.com/heruan/maven-ear-example)

The problem is when I deploy this to Wildfly, I get:

java.lang.NoClassDefFoundError: Failed to link ejb/MyServiceImpl: api/MyService

Full stack trace:

INFO  [org.jboss.weld.deployer] (MSC service thread 1-6) WFLYWELD0003: Processing weld deployment ear-1.0.0.ear
WARN  [org.jboss.modules] (MSC service thread 1-6) Failed to define class ejb.MyServiceImpl in Module "deployment.ear-1.0.0.ear.ejb-1.0.0.jar:main" from Service Module Loader: java.lang.NoClassDefFoundError: Failed to link ejb/MyServiceImpl (Module "deployment.ear-1.0.0.ear.ejb-1.0.0.jar:main" from Service Module Loader): api/MyService
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.__newInstance(DelegatingConstructorAccessorImpl.java:45)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:446)
    at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:274)
    at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:78)
    at org.jboss.modules.Module.loadModuleClass(Module.java:606)
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:363)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:351)
    at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:93)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.jboss.as.ee.utils.ClassLoadingUtils.loadClass(ClassLoadingUtils.java:21)
    at org.jboss.as.ee.utils.ClassLoadingUtils.loadClass(ClassLoadingUtils.java:14)
    at org.jboss.as.ee.component.deployers.InterceptorAnnotationProcessor.processComponentConfig(InterceptorAnnotationProcessor.java:84)
    at org.jboss.as.ee.component.deployers.InterceptorAnnotationProcessor.deploy(InterceptorAnnotationProcessor.java:76)
    at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:147)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

What am I missing? My goal is to have the most basic EAR with a common JPA Persistence Unit shared by its modules.


Solution

  • I found out what the problem was: duplicate .class files. Seems like the Maven EAR plugin isn't clever enough to avoid duplicate dependencies, so you need to explicitly set them as provided.

    For example, the solution in the example project is to set the api module as provided in the war's pom.xml:

    <dependency>
        <groupId>com.example</groupId>
        <artifactId>api</artifactId>
        <version>1.0.0</version>
        <scope>provided</scope>
    </dependency>