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

RESTEasy resource with constructor or field injection failing with RESTEASY003190 #39137

Open
nimo23 opened this issue Mar 3, 2024 · 16 comments
Labels
area/resteasy-classic kind/bug Something isn't working

Comments

@nimo23
Copy link
Contributor

nimo23 commented Mar 3, 2024

Describe the bug

I have the following JAX-RS Endpoint:

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.Validator;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.sse.Sse;
import lombok.extern.jbosslog.JBossLog;

@Path("/")
@ApplicationScoped
@JBossLog
public class ApplicationConstructorInjectionResource {
	
	private final Sse sse;
	private final SecurityContext security;
	private final Validator validator;
	
	
	@Inject
	public ApplicationConstructorInjectionResource(@Context Sse sse, SecurityContext security, Validator validator) {
		log.info("CDI Constructor called");
		this.sse = sse;
		this.security = security;
		this.validator = validator;
		log.infov("this.sse = {0}", this.sse.getClass());
		log.infov("this.security = {0}", this.security.getClass());
		log.infov("this.validator = {0}", this.validator.getClass());
	}

	@PostConstruct
	public void postConstruct() {
		log.infov("@PostConstruct called: {0}", this.getClass().getSimpleName());
		log.infov("this.sse = {0}", this.sse.getClass());
	}
	
	
	@GET
	@Path("/hello")
	@Produces(MediaType.TEXT_PLAIN)
	public String test() {
		return "hello";
	}

}

I get this compile error:

2024-03-03 12:30:44,985 ERROR [io.qua.run.boo.StartupActionImpl] (Quarkus Main Thread) Error running Quarkus: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:113)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ExceptionInInitializerError
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160)
	at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300)
	at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(MethodHandleAccessorFactory.java:103)
	at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(ReflectionFactory.java:200)
	at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:549)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:70)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
	at io.quarkus.runner.GeneratedMain.main(Unknown Source)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	... 3 more
Caused by: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source)
	... 16 more
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: RESTEASY003190: Could not find constructor for class: app.rest.ApplicationConstructorInjectionResource
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder.bootServletContainer(UndertowDeploymentRecorder.java:556)
	at io.quarkus.deployment.steps.UndertowBuildStep$build767851419.deploy_0(Unknown Source)
	at io.quarkus.deployment.steps.UndertowBuildStep$build767851419.deploy(Unknown Source)
	... 17 more
Caused by: java.lang.RuntimeException: RESTEASY003190: Could not find constructor for class: app.rest.ApplicationConstructorInjectionResource
	at org.jboss.resteasy.spi.metadata.ResourceBuilder.getConstructor(ResourceBuilder.java:712)
	at org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory.registered(POJOResourceFactory.java:54)
	at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:218)
	at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:201)
	at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:184)
	at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:171)
	at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:156)
	at org.jboss.resteasy.core.ResourceMethodRegistry.addPerRequestResource(ResourceMethodRegistry.java:81)
	at org.jboss.resteasy.core.ResteasyDeploymentImpl.registerResources(ResteasyDeploymentImpl.java:463)
	at org.jboss.resteasy.core.ResteasyDeploymentImpl.registration(ResteasyDeploymentImpl.java:431)
	at org.jboss.resteasy.core.ResteasyDeploymentImpl.startInternal(ResteasyDeploymentImpl.java:159)
	at org.jboss.resteasy.core.ResteasyDeploymentImpl.start(ResteasyDeploymentImpl.java:124)
	at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:134)
	at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:39)
	at io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:118)
	at io.undertow.servlet.core.ManagedServlet$DefaultInstanceStrategy.start(ManagedServlet.java:295)
	at io.undertow.servlet.core.ManagedServlet.createServlet(ManagedServlet.java:140)
	at io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:585)
	at io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:556)
	at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
	at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$9$1.call(UndertowDeploymentRecorder.java:604)
	at io.undertow.servlet.core.DeploymentManagerImpl.start(DeploymentManagerImpl.java:598)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder.bootServletContainer(UndertowDeploymentRecorder.java:546)
	... 19 more

Expected behavior

Constructor injection with @ApplicationScoped and final fields should work without the need to set a no-args-constructor:

Actual behavior

Constructor injection with @ApplicationScoped and final fields does not work .

How to Reproduce?

Reproducer:

  1. Take the class above and compile
  2. The error above is thrown

Output of uname -a or ver

No response

Output of java -version

openjdk version "21.0.2" 2024-01-16 OpenJDK Runtime Environment (build 21.0.2+13-58) OpenJDK 64-Bit Server VM (build 21.0.2+13-58, mixed mode, sharing)

Quarkus version or git rev

No response

Build tool (ie. output of mvnw --version or gradlew --version)

3.8.1

Additional information

A similiar issue (but with @Singleton-scope) is reported here #16475.

@nimo23 nimo23 added the kind/bug Something isn't working label Mar 3, 2024
@quarkus-bot quarkus-bot bot added the area/arc Issue related to ARC (dependency injection) label Mar 3, 2024
Copy link

quarkus-bot bot commented Mar 3, 2024

/cc @Ladicek (arc), @manovotn (arc), @mkouba (arc)

@manovotn
Copy link
Contributor

manovotn commented Mar 3, 2024

I am not in front of my work laptop but it seems the no-args ctor might not be generated here.

RE seems to check for list of allowed annotations on class to determine if it generates the c-tor - I wonder is the @JBossLog could cause this?
EDIT: shouldn't be the case, it has lombok prefix which is accounted for here

@mkouba
Copy link
Contributor

mkouba commented Mar 4, 2024

CC @geoand

@geoand
Copy link
Contributor

geoand commented Mar 4, 2024

#39146 takes care of the issue

geoand added a commit to geoand/quarkus that referenced this issue Mar 4, 2024
geoand added a commit to geoand/quarkus that referenced this issue Mar 4, 2024
@geoand
Copy link
Contributor

geoand commented Mar 4, 2024

It turns out that the PR above was wrong.

The use case here actually is not supported because of the use of @Context on constructor parameters.

I opened #39152 to improve logging of this kind of thing in the future

geoand added a commit to geoand/quarkus that referenced this issue Mar 4, 2024
@nimo23
Copy link
Contributor Author

nimo23 commented Mar 4, 2024

The use case here actually is not supported because of the use of @context on constructor parameters.

Is @Context within a constructor injection generally not supported by CDI, or is this just not supported in Quarkus Arc?

So I have to use this to make it work:

public class ApplicationConstructorInjectionResource {
	
        @Context Sse sse
	private Sse sse; // cannot be injected by constructor.

	private final SecurityContext security;
	private final Validator validator;
	
	@Inject
	public ApplicationConstructorInjectionResource(SecurityContext security, Validator validator) {
		log.info("CDI Constructor called");
		this.security = security;
		this.validator = validator;
		log.infov("this.security = {0}", this.security.getClass());
		log.infov("this.validator = {0}", this.validator.getClass());
	}

..
}

@geoand
Copy link
Contributor

geoand commented Mar 4, 2024

Is @context within a constructor injection generally not supported by CDI, or is this just not supported in Quarkus Arc?

Correct, although it works in RESTEasy Reactive (with most types)

@nimo23
Copy link
Contributor Author

nimo23 commented Mar 4, 2024

Correct, ..

If this is a Quarkus Arc limitation, it should be documented here: https://quarkus.io/guides/cdi-reference#supported_features_and_limitations.

.. although it works in RESTEasy Reactive (with most types).

So using @Context in contructor injection works with most types when using RESTEasy Reactive? For the user, without looking into the source code, it is somewhat unclear which types are supported and where.

@geoand
Copy link
Contributor

geoand commented Mar 4, 2024

I'll update the docs soon

@geoand
Copy link
Contributor

geoand commented Mar 5, 2024

If this is a Quarkus Arc limitation, it should be documented here: https://quarkus.io/guides/cdi-reference#supported_features_and_limitations.

This is not really a great place to document this limitation as it's not an Arc limitation - although I understand why one would look there.
@mkouba @manovotn any ideas where to document that using @Context, @PathParam etc. is not supported by Arc and may or may not work in RETEasy Classic depending on exactly is being attempted?

geoand added a commit to geoand/quarkus that referenced this issue Mar 5, 2024
@mkouba
Copy link
Contributor

mkouba commented Mar 5, 2024

If this is a Quarkus Arc limitation, it should be documented here: https://quarkus.io/guides/cdi-reference#supported_features_and_limitations.

This is not really a great place to document this limitation as it's not an Arc limitation - although I understand why one would look there. @mkouba @manovotn any ideas where to document that using @Context, @PathParam etc. is not supported by Arc and may or may not work in RETEasy Classic depending on exactly is being attempted?

Yes, it's not an ArC limitation and it should not be documented there. I think that there should be a dedicated "CDI integratoin" chapter in the RETEasy Classic/Reactive docs and all the details (that I don't know ;-) should be explained there.

@mkouba mkouba removed the area/arc Issue related to ARC (dependency injection) label Mar 5, 2024
@nimo23
Copy link
Contributor Author

nimo23 commented Mar 5, 2024

As @Context is from jakarta.ws.rs.core.context, it could then be documented here:

Of course, a better way would be to remove these restrictions if possible. If there are no longer such restrictions for resteasy-reactive, then perhaps it would be possible to eliminate these restrictions for resteasy as well.

@manovotn
Copy link
Contributor

manovotn commented Mar 5, 2024

If this is a Quarkus Arc limitation, it should be documented here: https://quarkus.io/guides/cdi-reference#supported_features_and_limitations.

This is not really a great place to document this limitation as it's not an Arc limitation - although I understand why one would look there. @mkouba @manovotn any ideas where to document that using @Context, @PathParam etc. is not supported by Arc and may or may not work in RETEasy Classic depending on exactly is being attempted?

A section in https://quarkus.io/guides/resteasy would be IMO best.
It's not Arc limitation as such, more a matter of integration between the two.

geoand added a commit that referenced this issue Mar 5, 2024
Add debugging to resteasy / arc interactions
@nimo23
Copy link
Contributor Author

nimo23 commented Mar 5, 2024

@geoand The following version does also not work:

@Path("/")
@Singleton
@JBossLog
public class ApplicationConstructorInjectionResource {
	
        @Context Sse sse
	private Sse sse; // cannot be injected by constructor or property.

	private final SecurityContext security;
	private final Validator validator;
	
	@Inject
	public ApplicationConstructorInjectionResource(SecurityContext security, Validator validator) {
		log.info("CDI Constructor called");
		this.security = security;
		this.validator = validator;
		log.infov("this.security = {0}", this.security.getClass());
		log.infov("this.validator = {0}", this.validator.getClass());
	}

	@PostConstruct
	public void postConstruct() {
		log.infov("@PostConstruct: {0}", this.getClass().getSimpleName());
		log.infov("this.sse = {0}", this.sse.getClass());
	}

..
}

I get the following error:

java.lang.NullPointerException: Cannot invoke "jakarta.ws.rs.sse.Sse.getClass()" because "this.sse" is null
	at app.rest.MyEndpoint.postConstruct(ServerView.java:111)
	at app.rest.MyEndpoint_Bean.doCreate(Unknown Source)
	at app.rest.MyEndpoint_Bean.create(Unknown Source)
	at app.rest.MyEndpoint_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:32)
	at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
	at io.quarkus.arc.impl.ComputingCacheContextInstances.computeIfAbsent(ComputingCacheContextInstances.java:19)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at app.rest.MyEndpoint_Bean.get(Unknown Source)
	at app.rest.MyEndpoint_Bean.get(Unknown Source)
	at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:554)
	at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:534)
	at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:567)
	at io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
	at io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
	at io.quarkus.resteasy.common.runtime.QuarkusConstructorInjector.construct(QuarkusConstructorInjector.java:52)
	at org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory.createResource(POJOResourceFactory.java:64)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:349)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:70)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:429)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
	at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:229)
	at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:222)
	at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55)
	at io.quarkus.resteasy.runtime.ResteasyServlet.service(ResteasyServlet.java:19)
	at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
	at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
	at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:63)
	at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
	at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
	at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:67)
	at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:133)
	at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
	at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
	at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
	at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:65)
	at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
	at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
	at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
	at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
	at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
	at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
	at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:247)
	at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:56)
	at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:111)
	at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:108)
	at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
	at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$9$1.call(UndertowDeploymentRecorder.java:626)
	at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227)
	at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:152)
	at io.undertow.server.handlers.CanonicalPathHandler.handleRequest(CanonicalPathHandler.java:49)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$1.handleRequest(UndertowDeploymentRecorder.java:125)
	at io.undertow.server.Connectors.executeRootHandler(Connectors.java:284)
	at io.undertow.server.DefaultExchangeHandler.handle(DefaultExchangeHandler.java:18)
	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$5$2.run(UndertowDeploymentRecorder.java:441)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Resulted in: org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException: Cannot invoke "jakarta.ws.rs.sse.Sse.getClass()" because "this.sse" is null
	at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:357)
	at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:205)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:452)
	... 49 more

So it doesn't matter whether @Context is injected through a property or a constructor, injecting @Context as a class variable generally doesn't work in quarkus.

@geoand
Copy link
Contributor

geoand commented Mar 5, 2024

Yeah, you are just trying to work around the problem and that won't work :)

@geoand
Copy link
Contributor

geoand commented Mar 5, 2024

The general advice (for RESTEasy Classic at least) is that anything with @Context should be a method parameter, not a field

@nimo23 nimo23 changed the title RESTEasy @ApplicationScoped resource with constructor injection failing with RESTEASY003190 RESTEasy @ApplicationScoped resource with constructor or field injection failing with RESTEASY003190 Mar 5, 2024
@nimo23 nimo23 changed the title RESTEasy @ApplicationScoped resource with constructor or field injection failing with RESTEASY003190 RESTEasy resource with constructor or field injection failing with RESTEASY003190 Mar 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/resteasy-classic kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants