Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JAX-RS sub resource fails to be created #5467

Closed
sdaschner opened this issue Oct 25, 2021 · 7 comments
Closed

JAX-RS sub resource fails to be created #5467

sdaschner opened this issue Oct 25, 2021 · 7 comments
Assignees
Labels
Status: Pending Waiting on the issue requester to give more details or share a reproducer

Comments

@sdaschner
Copy link

Description


When using the following project, the OriginsResource (see on GitHub) can't be created and instantiated as expected.

The log shows the following output:

[#|2021-10-25T07:52:44.168+0000|WARNING|Payara 5.2021.7|org.glassfish.jersey.server.internal.JerseyResourceContext|_ThreadID=80;_ThreadName=http-thread-pool::http-listener-1(2);_TimeMillis=1635148364168;_LevelValue=900;|
  Lookup and initialization failed for a resource class: class com.sebastian_daschner.coffee_shop.orders.boundary.OriginsResource.
MultiException stack 1 of 1
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=CoffeeShop,parent=OriginsResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,2113035965)
	at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:51)
	at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:988)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:999)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1074)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1065)
	at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.createAndInitialize(AbstractHk2InjectionManager.java:189)
	at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.createAndInitialize(ImmediateHk2InjectionManager.java:30)
	at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:106)
	at org.glassfish.jersey.server.JerseyResourceContextConfigurator.lambda$init$0(JerseyResourceContextConfigurator.java:45)
	at org.glassfish.jersey.server.internal.JerseyResourceContext.getResource(JerseyResourceContext.java:83)
	at com.sebastian_daschner.coffee_shop.orders.boundary.TypesResource.originsResource(TypesResource.java:44)
	at com.sebastian_daschner.coffee_shop.orders.boundary.TypesResource$Proxy$_$$_WeldClientProxy.originsResource(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.glassfish.jersey.server.internal.routing.SubResourceLocatorRouter.lambda$getResource$0(SubResourceLocatorRouter.java:132)
	at org.glassfish.jersey.server.internal.routing.SubResourceLocatorRouter.getResource(SubResourceLocatorRouter.java:150)
	at org.glassfish.jersey.server.internal.routing.SubResourceLocatorRouter.apply(SubResourceLocatorRouter.java:86)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:86)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:69)
	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:38)
	at org.glassfish.jersey.process.internal.Stages.process(Stages.java:173)
	at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:247)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
	at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1636)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:259)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:757)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:577)
	at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158)
	at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
	at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
	at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
	at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182)
	at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156)
	at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218)
	at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88)
	at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53)
	at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524)
	at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89)
	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94)
	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33)
	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114)
	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
	at java.base/java.lang.Thread.run(Thread.java:834)
|#]

Environment

From Docker Image: payara/server-full:5.2021.7-jdk11
Operating System: Linux archlinux 5.14.8-arch1-1 #1 SMP PREEMPT Sun, 26 Sep 2021 19:36:15 +0000 x86_64 GNU/Linux

@sdaschner sdaschner added Status: Open Issue has been triaged by the front-line engineers and is being worked on verification Type: Bug Label issue as a bug defect labels Oct 25, 2021
@OndroMih
Copy link
Contributor

Hi @sdaschner, what you're trying to do isn't supported but I think there's a way to do what you want.

You're trying to retrieve a subresource using JAX-RS ResourceContext, which creates an object which isn't a CDI bean, therefore injection doesn't work. Or, it doesn't work as you expect. The @Inject annotation isn't picked up by the CDI container but it's picked up by the HK2 container that Jersey uses internally and this container doesn't know how to inject the requested bean, hence the HK2 error.

You should instead make the subresource class a CDI bean (which you already did), inject the subresource into the main resource and then initialize it using the resource context to inject all JAX-RS fields. Have a look at this PR into your project: sdaschner/coffee-testing#2

@sdaschner
Copy link
Author

Thanks for your fast response @OndroMih!

Fair enough, given that the JAX-RS standard wants to move to a more CDI-aligned direction, that's probably the more future-proof way (although I'm not quite sure if the 2.0/2.1 standard back then required this issue to work, but anyway, let's move on :))

@sdaschner
Copy link
Author

Actually I was too fast closing this issue, because now another one emerged (or the same if you have a look at the log I was pasting). The OriginsResource itself doesn't seem to be able to inject the CoffeeShop class, although all of them now should be CDI beans:

[#|2021-10-27T05:56:52.687+0000|WARNING|Payara 5.2021.7|javax.enterprise.web|_ThreadID=80;_ThreadName=http-thread-pool::http-listener-1(3);_TimeMillis=1635314212687;_LevelValue=900;|
  StandardWrapperValve[com.sebastian_daschner.coffee_shop.CoffeeApplication]: Servlet.service() for servlet com.sebastian_daschner.coffee_shop.CoffeeApplication threw exception
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=CoffeeShop,parent=OriginsResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1190603523)
	at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:51)
	at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:988)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:999)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:977)
	at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.inject(AbstractHk2InjectionManager.java:202)
	at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.inject(ImmediateHk2InjectionManager.java:30)
	at org.glassfish.jersey.server.internal.JerseyResourceContext.initResource(JerseyResourceContext.java:93)
	at com.sebastian_daschner.coffee_shop.orders.boundary.TypesResource.originsResource(TypesResource.java:47)
	at com.sebastian_daschner.coffee_shop.orders.boundary.TypesResource$Proxy$_$$_WeldClientProxy.originsResource(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.glassfish.jersey.server.internal.routing.SubResourceLocatorRouter.lambda$getResource$0(SubResourceLocatorRouter.java:132)
	at org.glassfish.jersey.server.internal.routing.SubResourceLocatorRouter.getResource(SubResourceLocatorRouter.java:150)
	at org.glassfish.jersey.server.internal.routing.SubResourceLocatorRouter.apply(SubResourceLocatorRouter.java:86)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:86)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:69)
	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:38)
	at org.glassfish.jersey.process.internal.Stages.process(Stages.java:173)
	at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:247)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
	at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1636)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:259)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:757)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:577)
	at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158)
	at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
	at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
	at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
	at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182)
	at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156)
	at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218)
	at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109)
	at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88)
	at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53)
	at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524)
	at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89)
	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94)
	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33)
	at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114)
	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
	at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
	at java.base/java.lang.Thread.run(Thread.java:834)
|#]

See the updated reproducer project (with your merged changes)

@OndroMih
Copy link
Contributor

OndroMih commented Oct 27, 2021

I see, I was able to reproduce this. It seems that even though the CoffeeShop instance is injected into the OriginsResource subresource via CDI, Jersey still tries to inject it via the HK2 engine even if it's not null. it seems that Jersey doesn't only inject fields annotated with @Context from JAX-RS but also annotated with @Inject from JSR-330.

My humble opinion is that injection for @Inject should be disabled in Jersey or Payara should configure Jersey to inject those fields via CDI and not via HK2. However, this isn't trivial.

You can only work around this by returning a plain CDI bean from the subresrouce method, without calling initResource. In that case, JAX-RS resource injection for fields annotated with @Context in the subresource wouldn't work. But you can inject them as CDI reasources with a help of ClientRequestFilter JAX-RS Provider like in this new PR I made as an example: sdaschner/coffee-testing#3

I think it's also possible to support @Context injection in subresources with this approach using a CDI extension but that would be more work to implement. I can try to build it when I have some time.

@OndroMih
Copy link
Contributor

OndroMih commented Oct 28, 2021

Actually, I found out that JAX-RS injection with @context works automatically if the subresource is a CDI bean. So the simplest solution that supports both CDI and JAX-RS injection in Payara is to just return the innjected subresource without any JaxRs resources factory. In other words, you can revert the PR sdaschner/coffee-testing#3 and simply return the injected resource without using the resourceContext.initResource method and all will work:

- return resourceContext.initResource(originsResource);
+return originsResource;

I don't understand why also injection using @Context will work but I assume that Jersey scans the returned resource for @Context annotations and will inject them. In this case, it will not attempt to inject fields annotated with @Inject and therefore won't conflict with CDI.

I'm not sure whether this is portable or only works in Payara Server.

@OndroMih
Copy link
Contributor

When I debugged more, I found out that with this setup (sub-resource created as a CDI bean), Jersey enables a specific class analyzer for HK2 which enables CDI integration: https://github.com/eclipse-ee4j/jersey/blob/e5b7905f83acab1a3ca647c82e11c3de7640e537/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java. This doesn't happen when ResourceContext.initResource or getResource are called - in that case the the default straegy is used, which instructs HK2 to attempt injecting also fields annotated with @Inject, which will fail.

I didn't find anything about CDI and JAX-RS subresources in the JAX-RS specification, so I'm not sure whether all application servers should inject JAX-RS resources into a CDI bean. But Payara does it via the Jersey CDI integration in Jersey, so it's enough to inject a sub-resource and just return it from the method.

@OndroMih
Copy link
Contributor

I tested the solution without calling resourceContext.initResource and it also works in WildFLy. It doesn't work in OpenLiberty, which won't inject JAX-RS resources. But in OpenLIberty, all works if resourceContext.initResource is called, which is more portable and also works in WildFly. It only doesn't work in Payara because Payara/Jersey forgets to enable CDI integration when resourceContext.initResource is called and attempt to inject fields with @Inject which isn't expected.

So, in summary:

  • the proper and portable code is to inject subresource and call resourceContext.initResource and then return it from a resource method (return resourceContext.initResource(subresource)) - this works in WildFly and OpenLiberty, Payara raises an exception due to a conflict between HK2 and CDI
  • In Payara, it's enough to return the subresource (return subresource) - this also works in WildFly but not in OpenLiberty, which won't inject JAX-RS fields. Payara and WildFly automatically resolve JAX-RS resources in CDI beans, however, this isn't required by the specification

Payara Server should be fixed to support the portable solution and avoid injecting @Inject fields by HK2.

@shub8968 shub8968 added Status: Pending Waiting on the issue requester to give more details or share a reproducer and removed Type: Bug Label issue as a bug defect Status: Open Issue has been triaged by the front-line engineers and is being worked on verification labels Oct 28, 2021
@shub8968 shub8968 closed this as completed May 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Pending Waiting on the issue requester to give more details or share a reproducer
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants