JAX-RS resources should have implicit Dependent annotation
See original GitHub issueCurrently I must annotate a JAX-RS resource with @Path (for JAX-RS) and @RequestScoped or @ApplicationScoped (for CDI).
As these resources are by default request scoped, maybe this should be automatically used for injection.
If I do not annotate the class with neither of Application or Request Scoped annotations, injection is going to fail in HK2
When debugging, I could see the CDI actually does inject the values correctly, yet HK2 then subsequently fails that it cannot inject them
Environment Details
- Helidon Version: 0.10.5-SNAPSHOT
- Helidon MP
- JDK version: 8
- OS: Mac
Stack trace:
WARNING: The following warnings have been detected: WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 1
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=JsonWebToken,parent=Runner$TheResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,942306357)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:75)
at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:1012)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:1008)
at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.inject(AbstractHk2InjectionManager.java:231)
at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.inject(ImmediateHk2InjectionManager.java:54)
at org.glassfish.jersey.ext.cdi1x.internal.AbstractCdiBeanSupplier$2.getInstance(AbstractCdiBeanSupplier.java:112)
at org.glassfish.jersey.ext.cdi1x.internal.AbstractCdiBeanSupplier._provide(AbstractCdiBeanSupplier.java:127)
at org.glassfish.jersey.ext.cdi1x.internal.GenericCdiBeanSupplier.get(GenericCdiBeanSupplier.java:66)
at org.glassfish.jersey.inject.hk2.InstanceSupplierFactoryBridge.provide(InstanceSupplierFactoryBridge.java:77)
at org.jvnet.hk2.internal.FactoryCreator.create(FactoryCreator.java:153)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:487)
at org.jvnet.hk2.internal.PerLookupContext.findOrCreate(PerLookupContext.java:70)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2126)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:777)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:740)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:710)
at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.getInstance(AbstractHk2InjectionManager.java:184)
at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.getInstance(ImmediateHk2InjectionManager.java:54)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:129)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:284)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:75)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:110)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:113)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:113)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:113)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:93)
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:269)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)
at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
at org.glassfish.jersey.internal.Errors.process(Errors.java:268)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)
at io.helidon.webserver.jersey.JerseySupport$JerseyHandler.lambda$accept$1(JerseySupport.java:207)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Issue Analytics
- State:
- Created 5 years ago
- Comments:7 (7 by maintainers)
Top Results From Across the Web
JAX-RS resources should have implicit Dependent annotation
Currently I must annotate a JAX-RS resource with @path (for JAX-RS) and @RequestScoped or @ApplicationScoped (for CDI).
Read more >29.2 Creating a RESTful Root Resource Class
Root resource classes are "plain old Java objects" (POJOs) that are either annotated with @Path or have at least one method annotated with...
Read more >jax rs - What is the best way to inject a singleton service into a ...
Declare the service class with an @ApplicationScoped annotation and inject it on your resources with @Inject . CDI will only instantiate one ...
Read more >Chapter 3. Dependency Injection - JBoss.org
In order to inject TimeService , you must annotate it with @ApplicationScoped or the Errai DI container will not acknowledge the type as...
Read more >Jersey 2.37 User Guide - GitHub Pages
You just need to declare (provided) dependency on JAX-RS API to be able to compile your application. 1. 2. 3. 4. 5. 6....
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Some background mostly for other readers who may come across this:
The JAX-RS specification states that a resource class’ default lifecycle is per-lookup. Section 3.1.1 reads:
Strictly speaking the CDI scope that matches this exactly is
Dependent
. However most users would probably expectRequestScoped
semantics, and just as a point of note this is exactly what, for example, RestEasy does. So in general yes, treating resources that are {handwave handwave} supposed to be per-request as if they were annotated with@RequestScoped
from a CDI perspective is probably the right thing to do at some abstract level.And now a digression on JAX-RS and lifecycles (this is actually relevant! No, really!):
It is worth noting that if (again, pure JAX-RS here, no CDI, no MicroProfile) a user defines an
Application
such that itsgetSingletons()
method returns a resource instance, then it is that resource instance and none other that should be used. If instead a user defines anApplication
such that itsgetClasses()
method returns a resource class, then regardless of whether an instance of that class appears in the return value ofgetSingletons()
, the lifecycle should be per-request. (If a user does not define anApplication
at all, then the behavior is up to the JAX-RS implementation as to what to do.)These
Application
methods are the only portable ways to define lifecycles in JAX-RS; that is: JAX-RS has no notion of scope annotations at all.Obviously JAX-RS implementations can add capabilities here and both Jersey and RestEasy do.
Jersey augments all of this by also permitting
javax.inject
scope annotations on resource classes. So if a resource class handled by Jersey has@Singleton
on it, and if that resource class is among the set ofClass
es returned byApplication#getClasses()
, then although that normally indicates to theApplication
’s caller that a per-request lifecycle is desired—nevertheless@Singleton
will trump here in Jersey: only one instance of that resource class will be instantiated, and it will be instantiated by Jersey (not the user or theApplication
), and will be dependency injected by whatever Jersey is using for dependency injection.Next, Jersey supplies a CDI integration component, used by Helidon, and it puts CDI on top of its dependency injection facilities. So now resource classes returned from
Application#getClasses()
are instantiated by CDI if they are known to CDI and in the scopes that they are annotated with.So when CDI is in the picture, as it is in MicroProfile, and therefore as it is in Helidon MicroProfile, and when you want to use behavior that is non-portable with respect to JAX-RS like auto-detecting lifecycles and/or auto-detecting resource classes and so on, you have to make CDI aware of the classes in question. (We should also double-check and make sure that
Application#getSingletons()
instances are shoehorned into CDI in either@ApplicationScoped
or@Singleton
scope; I assume the Jersey CDI support does this?)CDI becomes aware of classes when bean discovery happens. Bean discovery broadly speaking can take place over all Java classes or only those with bean-defining annotations. (That is a vastly oversimplified summary but correct as far as it goes.) Normally you set the bean discovery mode to
annotated
, which means that only those classes annotated with@RequestScoped
,@ApplicationScoped
,@Dependent
, and so on—but not, notably,@Singleton
!—become known to the CDI container.So fine: if your resource-class-that-you-want-to-be-known-to-CDI doesn’t have one of these annotations on it, then CDI doesn’t know about it. If CDI doesn’t know about it, and you’re in an environment where Jersey is in play and Jersey’s CDI support is in play (such as is the case in Helidon MicroProfile), then the behavior is going to be undefined. In this case, it looks like Jersey’s CDI support will see if CDI knows about the requested injection, which as we’ve already determined it doesn’t, and if CDI says “nope” then it goes and tries to do it itself using HK2.
The easiest way of course to solve this problem is to put an explicit bean-defining annotation on a resource class you’re planning on using with Helidon MicroProfile server, such as
@Dependent
or@RequestScoped
. That should do the trick with a minimum of fuss. (It may even be part of the MicroProfile specification somewhere as to how to reconcile its requirement of CDI and the requirement of JAX-RS since strictly speaking by both specifications read separately this combination’s behavior is undefined. Java EE if I remember right is where, and only where, the specification of the behavior that should result from these two specifications’ co-presence is defined.)When for whatever reason that is not an option, the next least-magic way to solve this problem is to programmatically inform the CDI container itself that it should consider the resource class as a bean in a particular scope. You can do this in a portable extension, which can use the
BeforeBeanDiscovery#addAnnotatedType(Class, String)
method. The returned “configurator” can be used to add the@RequestScoped
or@Dependent
annotation directly. Obviously you want to do this only for resource classes that are not already annotated with bean-defining annotations.Finally you can change Helidon MicroProfile server to take any resource classes that are added to it programmatically via the
Server.Builder#addResourceClass(Class)
method to also add such classes into the CDI container. Currently this addition does not occur. This step is basically another formulation of the portable extension strategy spelled out one paragraph above.This is now merged. Helidon now adds all resources annotated with
@Path
as beans. Default scope is@Dependent
, user can define any scope desired.