Describe the bug
When extending a class which implements ContainerRequestFilter, the extended class gets @Inject'ed null in its fields.
Expected behavior
The injection is not null.
Actual behavior
lug 29, 2019 2:10:04 PM io.quarkus.undertow.runtime.QuarkusExceptionHandler handleThrowable
ERROR: Exception handling request b02cdeeb-cb97-478e-b2ee-dd0a7aad42cf-2 to /test/father
org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:381)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:209)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:496)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:252)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:249)
at io.quarkus.resteasy.runtime.ResteasyFilter$ResteasyResponseWrapper.sendError(ResteasyFilter.java:57)
at io.undertow.servlet.handlers.DefaultServlet.doGet(DefaultServlet.java:175)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at io.quarkus.resteasy.runtime.ResteasyFilter.doFilter(ResteasyFilter.java:28)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
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.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
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:64)
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.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:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
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$8$1$1.call(UndertowDeploymentRecorder.java:483)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:364)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1538)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1429)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:32)
at java.lang.Thread.run(Thread.java:748)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: java.lang.NullPointerException
at org.example.filter.inheritance.FatherFilter.filter(FatherFilter.java:19)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:311)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:410)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:379)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invoke$1(ResourceMethodInvoker.java:353)
at java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:981)
at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2124)
at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:110)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:353)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:477)
... 49 more
To Reproduce
Steps to reproduce the behavior:
git clone https://github.com/antoniomacri/quarkus-npe-filter-inheritance.gitmvn testEnvironment (please complete the following information):
java -version: 1.8Additional context
If I replace
public class SonFilter extends FatherFilter {
with
public class SonFilter implements ContainerRequestFilter {
it works.
I just checked and this issue is still present when using master
So I looked at this a little and although I understand the rationale of what you are trying to do, I'm not sure it makes sense from a CDI perspective (@mkouba or @manovotn would know more).
I propose you do something like the following:
public interface CustomFilter extends ContainerRequestFilter {
ExampleService getExampleService();
default void filter(ContainerRequestContext requestContext) {
getExampleService().doSomething();
}
}
@Provider
@ApplicationScoped
public class FatherFilter implements CustomFilter {
@Inject
ExampleService exampleService;
@Override
public ExampleService getExampleService() {
return exampleService;
}
}
@Provider
@ApplicationScoped
public class SonFilter implements CustomFilter {
@Inject
ExampleService exampleService;
@Override
public ExampleService getExampleService() {
return exampleService;
}
}
You can even simplify things even more in CustomFilter by doing something like:
default ExampleService getExampleService() {
return Arc.container().instance(ExampleService.class).get();
}
That way the classes that extend CustomFilter don't even need to use @Inject ExampleService exampleService.
Note that however in this case ExampleService needs to be annotated with @Unremovable
Hm, I've just checked the generated beans and the code looks correct. However, when I tried to debug the filters I've noticed that for SonFilter a client proxy is correcly used but for FatherFilter RESTEasy works directly with an uninitialized instance of a bean class. I'll need to verify the RESTEasy integration code...
I've found the problem. When RESTEasy attempts to find a bean for the FatherFilter the lookup fails with ambiguous dependency exception because there are two beans for this type: FatherFilter and SonFilter. As a result, RESTEasy is using a fallback and instantiates the FatherFilter without injection. The easies workaround is to annotate the SonFilter class with @Typed(SonFilter.class). This restricts the set of bean types for the SonFilter bean.
By the way, you don't need to re-declare the exampleService injected field because it's package-private and both FatherFilter and SonFilter are located in the same package.
Yes, of course, it was just for the purpose of showing the error.
As a result, RESTEasy is using a fallback and instantiates the FatherFilter without injection.
That sounds very awkward, is there is reason this is preferred to blowing up with ambig. dependency?
Alternatively, RESTEasy could instantiate the object manually and then let arc inject into it. If arc supports injection target factory that is, @mkouba will surely know from the top of his head ;-)
Alternatively, RESTEasy could instantiate the object manually and then let arc inject into it. If arc supports injection target factory that is...
It doesn't.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you!
We are doing this automatically to ensure out-of-date issues does not stay around indefinitely.
If you believe this issue is still relevant please put a comment on it on why and if it truly needs to stay request or add 'pinned' label.
Most helpful comment
I've found the problem. When RESTEasy attempts to find a bean for the
FatherFilterthe lookup fails with ambiguous dependency exception because there are two beans for this type:FatherFilterandSonFilter. As a result, RESTEasy is using a fallback and instantiates theFatherFilterwithout injection. The easies workaround is to annotate theSonFilterclass with@Typed(SonFilter.class). This restricts the set of bean types for theSonFilterbean.