Issue Details (XML | Word | Printable)

Key: GLASSFISH-18793
Type: Bug Bug
Status: Resolved Resolved
Resolution: Duplicate
Priority: Major Major
Assignee: arjavdesai
Reporter: ljnelson
Votes: 0
Watchers: 5
Operations

If you were logged in you would be able to see more operations.
glassfish

Cannot @Inject an EJB implementation into a JAX-RS resource

Created: 10/Jun/12 02:06 PM   Updated: 12/Apr/13 01:13 PM   Resolved: 12/Apr/13 01:13 PM
Component/s: cdi
Affects Version/s: 3.1.2
Fix Version/s: 4.0

Time Tracking:
Not Specified

File Attachments: 1. GZip Archive cdi-jaxrs-bug-v2.tar.gz (4 kB) 11/Jun/12 01:50 PM - ljnelson
2. GZip Archive cdi-jaxrs-bug-v3.tar.gz (4 kB) 11/Jun/12 02:32 PM - ljnelson
3. GZip Archive cdi-jaxrs-bug.tar.gz (6 kB) 10/Jun/12 02:06 PM - ljnelson


Tags: cdi jax-rs ejb
Participants: arjavdesai, jjsnyder83, ljnelson, Martin Matula and TangYong


 Description  « Hide

In an .ear file with JAX-RS resources in the lib directory, it is impossible to have CDI inject an EJB implementation into a JAX-RS resource.

I have an .ear file with a lib directory.

In the .ear file I have:

  • an "api" .jar in the lib directory consisting of a single interface (a business interface)
  • an EJB module (a .jar file at the root level containing a local stateless session bean implementing the business interface described above, and a META-INF/beans.xml file causing it to be a CDI bean archive
  • a "jaxrs" .jar in the lib directory containing a single JAX-RS resource class with an injection point in it declared as @Inject private Greeter greeter; (where Greeter is the business interface), and a META-INF/beans.xml file causing it to be a CDI bean archive
  • a .war file containing a single class that extends javax.ws.rs.core.Application and serves as the "mount point" for a JAX-RS application whose single resource is the resource described above

Through various permutations of this application, deployment fails with Weld claiming that it cannot satisfy the Greeter injection point.

If I get CDI out of the mix and put @ManagedBean on the resource class and change the @Inject to @EJB everything works fine. But I want to use CDI injection, not @EJB injection.

I have attached a test case. Unzip/Untar it and run mvn clean install on it. Then deploy the resulting .ear file. It will be available under (e.g.) http://localhost:8080/cdi-jaxrs-war/api/greeting.



Martin Matula added a comment - 10/Jun/12 04:30 PM

FYI - I was able to make it work by removing beans.xml from the "jaxrs" jar and changing the annotation on the GreetingResource from @RequestScoped to @ManagedBean.
Apparently @RequestScoped does not work if there is no beans.xml in the jar. On the other hand, if I add beans.xml to the jar it complains in cannot satisfy the injection point.


ljnelson added a comment - 11/Jun/12 02:02 AM

Wait, that's odd. You're saying you de-bean-archived the GreetingResource and then @Inject worked? I confess that is a whole different sort of specification violation I didn't even think to try.


ljnelson added a comment - 11/Jun/12 12:04 PM

Martin: I tried your approach on the off chance it would work for some strange reason. I'm guessing that you got deployment to "work" (i.e. the console said that deployment completed normally)? Deployment may have completed, but injection did not. If you then actually hit the URL you should discover that the injection point is null, as you'd expect.


Martin Matula added a comment - 11/Jun/12 01:39 PM

After I made the changes it does work including the injection. Here is what I did:

  • after downloading your sample, war was missing in the list of modules in the top-level pom (or at least it did not open in the IDE when I opened the pom), so I added it
  • I removed META-INF/beans.xml from the "jaxrs" jar - i.e. the jar that contains the JAX-RS resource (this fixes the deployment issue)
  • I changed the annotation on the resource from @RequestScoped to @ManagedBean

After I made these changes it started working (including the injection).

Before I did this, I actually created my own version of your app (based on your stackoverflow.com question) where I observed this behavior - so then I just applied the same to the project attached to the issue - both apps worked for me after I made these changes.


ljnelson added a comment - 11/Jun/12 01:50 PM - edited

Martin, thanks for the effort.

I've attached the latest version of the project, complete with Martin's suggested changes. It still does not work.

In case I'm doing something wrong, here are the steps I followed:

  • Made sure GlassFish 3.1.2 is running. I happen to have created a domain called (for unimportant reasons) jx with its admin port on 9048.
  • Made sure this GlassFish instance is empty of applications.
  • Downloaded the "cdi-jaxrs-bug-v2.tar.gz" tarball attached to this issue.
  • cd'ed into its root-level directory and ran mvn clean install
  • cd'ed into ear/target
  • Ran asadmin --port=9048 deploy ./cdi-jaxrs-ear-0.0.1-SNAPSHOT.ear
  • Deployment completed successfully
  • Pointed my browser at http://localhost:9080/cdi-jaxrs-war/api/greeting and observed a NullPointerException where the resource class attempts to use the injected Greeter implementation

Martin Matula added a comment - 11/Jun/12 02:05 PM

Ah, one more thing I forgot was missing in your original project - I've added beans.xml to WEB-INF folder of your web app (war project). Do that in your v2 of the project and it'll start working.


ljnelson added a comment - 11/Jun/12 02:10 PM

Thank you! Indeed, that works. I will see if I can write up a post-mortem here. I still think this bug is valid, although as you have helpfully pointed out there is a workaround.


ljnelson added a comment - 11/Jun/12 02:32 PM - edited

I've attached "version 3" of my project, complete with Martin's changes. This version works.

It is difficult to know whether this bug is a valid one or not.

Section 1.2.3 of the CDI specification says in part:

The container performs dependency injection on all managed bean instances, even those which are not contextual instances.

Because of the confusion in the specification between the term "managed bean" in the Managed Beans sense and "managed bean" in the CDI managed bean sense (see CDI specification section 3.1) it is unclear what this means. I'll capitalize the former term and leave the latter term lowercase.

It would appear that some combination of GlassFish and Jersey have interpreted this sentence to mean that CDI injection is performed on Managed Beans, whether or not those Managed Beans are managed beans. This would explain why Martin's fixes work here.

On the other hand, the CDI specification then says very explicitly in section 12.1 that "bean classes of enabled beans must be deployed in bean archives". (A bean archive is any .jar or .war file with a META-INF/bean.xml or a WEB-INF/beans.xml file (respectively).) Section 5 then goes on to describe in great detail how dependency injection in CDI works, but the thing that all scenarios share in common is that they work on bean classes, which, as we've just seen, are required to be part of bean archives.

Finally, other application server vendors recommend very strongly that all JAX-RS resources that should serve as the target for CDI injection should be annotated with @RequestScoped, which would also of course require their containing .jar or .war file to have a beans.xml.

My added wrinkle is that my JAX-RS classes are discovered "outside" of the .war file that serves as their "mount point". That is, my .war file does not (deliberately) bundle the JAX-RS resource classes it manages under either its classes or lib directory. Instead, they are located in the containing .ear file's lib directory where the JAX-RS Application class can discover them dynamically, per the JAX-RS specification. This allows me to do much more flexible .ear packaging.

So the odd thing about that is that of course technically speaking my .war file shouldn't really have to be a bean archive itself as it contains no bean classes.

For all these reasons I believe that GlassFish is in error here, at least in the following respects:

  • I should be able to put a META-INF/beans.xml file in my JAX-RS jars, annotate my JAX-RS resources with @RequestScoped and have CDI injection performed on them appropriately, whether or not they are also annotated with @ManagedBean.
  • My JAX-RS classes should not be required to have @ManagedBean on them in order for CDI injection to occur.
  • If my JAX-RS classes are divorced from the .war file that manages them, there should be no reason I should have to put a WEB-INF/beans.xml file in the .war file.

TangYong added a comment - 12/Apr/13 07:45 AM

I have confirmed the sample(cdi-jaxrs-bug-v3.tar.gz ) on current v4, and while launching http://localhost:8080/cdi-jaxrs-war/api/greeting, accessing rest resource failed and in the server.log, the following exception happened,

[2013-04-12T16:39:34.602+0900] [glassfish 4.0] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=19 _ThreadName=http-listener-1(1)] [timeMillis: 1365752374602] [levelValue: 900] [[
StandardWrapperValve[com.edugility.cdi.jaxrs.Application]: Servlet.service() for servlet com.edugility.cdi.jaxrs.Application threw exception
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=Greeter,parent=GreetingResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,23965041)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:771)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:781)
at org.glassfish.jersey.gf.cdi.CdiComponentProvider$CdiFactory$2.getInstance(CdiComponentProvider.java:183)
at org.glassfish.jersey.gf.cdi.CdiComponentProvider$CdiFactory.provide(CdiComponentProvider.java:130)
at org.jvnet.hk2.internal.FactoryCreator.create(FactoryCreator.java:96)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:448)
at org.jvnet.hk2.internal.PerLookupContext.findOrCreate(PerLookupContext.java:69)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2203)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:574)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:561)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:172)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:185)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:105)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:118)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:121)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:121)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:121)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:121)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:102)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:62)
at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:208)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:231)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:227)
at org.glassfish.jersey.internal.Errors.process(Errors.java:275)
at org.glassfish.jersey.internal.Errors.process(Errors.java:257)
at org.glassfish.jersey.internal.Errors.process(Errors.java:227)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:191)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:819)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:311)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)
]]

Noting the exception is the same as GLASSFISH-20255, so the issue has been blocked by GLASSFISH-20255

-Tang


TangYong added a comment - 12/Apr/13 08:13 AM

BTW: even if not using @ManagedBean instead using CDI, the result should be same.
Component/s should be changed into jax-rs.


jjsnyder83 added a comment - 12/Apr/13 01:13 PM