Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there a WELD-001408 even when the BeanManager knows the EJB

When trying to deploy an ear we get the infamous WELD-001408 (see below for stacktrace).

Problem: It seems that WELD can't inject an EJB via @Inject into a CDI managed bean (!= @ManagedBean) in a lib/shared.jar. Why is this? Is there a standard that says this is not supposed work?

UPDATE I also had an ejb-jar.xml at the relevant location...

UPDATE2: I created a minimal versin on github

First the setup - my research/findings and more detailed questions at the end:

We are currently using Glassfish 4.1 => Weld 2.2.2.Final, but the error is the same using Payara 4.1.1.154 => Weld 2.2.16.Final, also Java EE 7

Layout of the ear

sample.ear
├── a-ejb.jar (contains AEjb.java + beans.xml + ejb-jar.xml)
├── b-ejb.jar (contains AnotherCdiIManagedBeanPojo.java + DummyEjb.java + beans.xml)
├── lib
|   └── shared.jar (contains ACdiManagedBeanPojo.java, AnotherCdiDependency.java + beans.xml)
└── META-INF
    └── application.xml (...)

In the shared.jar there is

public class ACdiManagedBeanPojo {
    @Inject
    private AEjb aEjb;

    @Inject
    private AnotherCdiDependency anotherCdiDependency;        
}

AEjb is an EJB residing in the a-ejb.jar

@javax.ejb.Singleton
@javax.ejb.LocalBean
@javax.enterprise.context.ApplicationScoped
public class AEjb {}

AnotherCdiDependency is another Pojo in the shared.jar

public class AnotherCdiDependency {}

The following class resides in b-ejb.jar

public class AnotherCdiManagedBeanPojo {
    @Inject
    private AEjb aEjb;
}

beans.xml (CDI 1.1)

<beans bean-discovery-mode="all"
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
</beans>

ejb-jar.xml

<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
         version="3.1">
    <enterprise-beans>   
        <session>
            <ejb-name>AEjb</ejb-name>
            <ejb-class>com.xxx.ejb.AEjb</ejb-class>
            <session-type>Singleton</session-type>
        </session>
    </enterprise-beans>
</ejb-jar>

stacktrace

org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type AEjb with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject @Default private com.managed.pojo.ACdiManagedBeanPojo.aEjb
  at com.managed.pojo.ACdiManagedBeanPojo.aEjb(ACdiManagedBeanPojo.java:0)

    at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:370)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:291)
    at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:134)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:165)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:529)
    at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:515)
    at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:490)
    at org.jboss.weld.bootstrap.WeldStartup.validateBeans(WeldStartup.java:419)
    at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:90)
    at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:225)
    at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:131)
    at org.glassfish.internal.data.ApplicationInfo.load(ApplicationInfo.java:328)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:496)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
    at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:539)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:535)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:360)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:534)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:565)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:557)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:360)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:556)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1464)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1300(CommandRunnerImpl.java:109)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1846)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1722)
    at org.glassfish.admin.rest.utils.ResourceUtil.runCommand(ResourceUtil.java:253)
    at org.glassfish.admin.rest.utils.ResourceUtil.runCommand(ResourceUtil.java:231)
    at org.glassfish.admin.rest.utils.ResourceUtil.runCommand(ResourceUtil.java:275)
    at org.glassfish.admin.rest.resources.TemplateListOfResource.createResource(TemplateListOfResource.java:133)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:151)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:171)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:152)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:104)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:387)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:331)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:103)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:297)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:254)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1028)
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:365)
    at org.glassfish.admin.rest.adapter.RestAdapter$2.service(RestAdapter.java:316)
    at org.glassfish.admin.rest.adapter.RestAdapter.service(RestAdapter.java:179)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
    at java.lang.Thread.run(Thread.java:745)
]]

Research and findings

  • Debugging Validator.validateInjectionPointForDeploymentProblems() I saw, that the BeanManager of lib/shared.jar had the instance of AEjb in his enterpriseBeans collection. At no time this collection is used for look ups of dependencies.
  • The injection of none-EJB classes (like AnotherCdiDependency) works fine in the classes of shared.jar
  • Injecting AEjb via @Inject into AnotherCdiManagedBeanPojo that resides in b-ejb.jar (read: toplevel / outside of /lib) works fine as well

My questions

  • My first question: Why can't the BeanManager inject the EJB even if it knows about it? Is there any standard that says shared libs can't be injected with "global" EJBs? If so where to find it?

  • What would be the "easiest" way out of this? Where easy means changing as little code as possible and not creating a big mess we'll have trouble with later on.

  • Bonus question: What is with this comment in BeanManagerImpl.getBeans(InjectionPoint injectionPoint) - where is this FAQ?

    We always cache, we assume that people don't use inline annotation literal declarations, a little risky but FAQd

PS: I have read the following and a lot other stuff concerning classloading, context and cdi and the special behaviour of different application servers concerning those topics - but still...

  • https://struberg.wordpress.com/2015/02/18/cdi-in-ears/
  • http://balusc.omnifaces.org/2013/10/cdi-behaved-unexpectedly-in-ear-so.html

Disclaimer: No new was called during my research.

like image 227
Benjamin Seiller Avatar asked Dec 03 '15 11:12

Benjamin Seiller


1 Answers

The rules for class visibility in EAR files are laid out in §8 of the Java EE Specification, v7.

In summary:

Each module in the EAR effectively gets it's own class loader.

The jars in the EAR/lib directory are considered to be in a single module for the purposes of class visibility. Classes in the EAR/lib module class loader are automatically made visible to all of the other modules (as described n §8.3).

The converse is not true. Classes in the other modules are not automatically accessible to those in the EAR/lib module.

Some Java EE implementations provide ways of getting around these restrictions (at the expense of making your application non-portable).

One possible solution is to move shared.jar into the root of the EAR and use manifest Class-Path entries in each jar to ensure access.

ie. the META-INF/MANIFEST.MF in shared.jar would then contain:

Class-Path: b-ejb.jar

If your ejb-jars need to see classes in the moved sample.jar then they would then need their own manifest class-path entries.

I think the jar needs to be moved because in my experience the following doesn't work.

Class-Path: ../b-ejb.jar
like image 168
Steve C Avatar answered Jan 27 '23 11:01

Steve C