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

Quarkus startup fails, if jaxrs filter injects a mp reactive messaging emitter #10159

Closed
TobiWeier opened this issue Jun 22, 2020 · 11 comments
Closed
Labels
kind/bug Something isn't working triage/out-of-date This issue/PR is no longer valid or relevant

Comments

@TobiWeier
Copy link

Describe the bug
If Quarkus uses MicroProfile Reactive Messaging with incoming and outgoing topics and a JAXRS javax.ws.rs.container.ContainerRequestFilter injects (indirectly) an org.eclipse.microprofile.reactive.messaging.Emitter, than startup fails with java.lang.ExceptionInInitializerError caused by 'Unable to find a emitter with the name <topicname>, available emitters are: []'.

Expected behavior
Successful quarkus startup.

Actual behavior
Exception in thread "main" java.lang.ExceptionInInitializerError

To Reproduce
Steps to reproduce the behavior:

  1. Clone project https://github.com/TobiWeier/quarkus-emitter-jaxrsfilter-failure.git
  2. Build project mvn clean package
  3. Start kafka cluster on minikube (described here https://strimzi.io/quickstarts/) or with docker-compose (described here https://github.com/wurstmeister/kafka-docker)
  4. Start quarkus java -jar target/emitter-jaxrsfilter-1.0-SNAPSHOT-runner.jar

Configuration

kafka.bootstrap.servers=localhost:9092
mp.messaging.outgoing.outtopic.connector=smallrye-kafka
mp.messaging.outgoing.outtopic.value.serializer=org.apache.kafka.common.serialization.StringSerializer
mp.messaging.incoming.intopic.connector=smallrye-kafka
mp.messaging.incoming.intopic.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
mp.messaging.incoming.intopic.group.id=emitter-jaxrsfilter

Screenshots

java -jar target/emitter-jaxrsfilter-1.0-SNAPSHOT-runner.jar
Exception in thread "main" java.lang.ExceptionInInitializerError
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at java.base/java.lang.Class.newInstance(Class.java:584)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:60)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:38)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:106)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
Caused by: java.lang.RuntimeException: Failed to start quarkus
        at io.quarkus.runner.ApplicationImpl.(ApplicationImpl.zig:166)
        ... 9 more
Caused by: java.lang.RuntimeException: Error injecting org.acme.messaging.controller.MessageSender org.acme.messaging.filter.RequestFilter.controller
        at org.acme.messaging.filter.RequestFilter_Bean.create(RequestFilter_Bean.zig:148)
        at org.acme.messaging.filter.RequestFilter_Bean.create(RequestFilter_Bean.zig:171)
        at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:79)
        at io.quarkus.arc.impl.ComputingCache$CacheFunction.lambda$apply$0(ComputingCache.java:99)
        at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
        at io.quarkus.arc.impl.ComputingCache.getValue(ComputingCache.java:41)
        at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:25)
        at org.acme.messaging.filter.RequestFilter_Bean.get(RequestFilter_Bean.zig:203)
        at org.acme.messaging.filter.RequestFilter_Bean.get(RequestFilter_Bean.zig:219)
        at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:380)
        at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:393)
        at io.quarkus.arc.impl.ArcContainerImpl$1.get(ArcContainerImpl.java:244)
        at io.quarkus.arc.impl.ArcContainerImpl$1.get(ArcContainerImpl.java:241)
        at io.quarkus.arc.runtime.ArcRecorder$2$1.create(ArcRecorder.java:84)
        at io.quarkus.resteasy.common.runtime.QuarkusConstructorInjector.construct(QuarkusConstructorInjector.java:39)
        at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.injectedInstance(ResteasyProviderFactoryImpl.java:1398)
        at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$AbstractInterceptorFactory.createInterceptor(JaxrsInterceptorRegistryImpl.java:150)
        at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$OnDemandInterceptorFactory.initialize(JaxrsInterceptorRegistryImpl.java:168)
        at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$OnDemandInterceptorFactory.checkInitialize(JaxrsInterceptorRegistryImpl.java:183)
        at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$OnDemandInterceptorFactory.getInterceptor(JaxrsInterceptorRegistryImpl.java:194)
        at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$AbstractInterceptorFactory.postMatch(JaxrsInterceptorRegistryImpl.java:124)
        at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl.postMatch(JaxrsInterceptorRegistryImpl.java:283)
        at org.jboss.resteasy.core.interception.jaxrs.ContainerRequestFilterRegistryImpl.postMatch(ContainerRequestFilterRegistryImpl.java:30)
        at org.jboss.resteasy.core.interception.jaxrs.ContainerRequestFilterRegistryImpl.postMatch(ContainerRequestFilterRegistryImpl.java:12)
        at org.jboss.resteasy.core.ResourceMethodInvoker.(ResourceMethodInvoker.java:139)
        at org.jboss.resteasy.core.ResourceMethodRegistry.processMethod(ResourceMethodRegistry.java:382)
        at org.jboss.resteasy.core.ResourceMethodRegistry.register(ResourceMethodRegistry.java:309)
        at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:260)
        at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:227)
        at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:208)
        at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:192)
        at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:175)
        at org.jboss.resteasy.core.ResourceMethodRegistry.addPerRequestResource(ResourceMethodRegistry.java:87)
        at org.jboss.resteasy.core.ResteasyDeploymentImpl.registerResources(ResteasyDeploymentImpl.java:513)
        at org.jboss.resteasy.core.ResteasyDeploymentImpl.registration(ResteasyDeploymentImpl.java:470)
        at org.jboss.resteasy.core.ResteasyDeploymentImpl.startInternal(ResteasyDeploymentImpl.java:160)
        at org.jboss.resteasy.core.ResteasyDeploymentImpl.start(ResteasyDeploymentImpl.java:117)
        at io.quarkus.resteasy.runtime.standalone.ResteasyStandaloneRecorder.staticInit(ResteasyStandaloneRecorder.java:86)
        at io.quarkus.deployment.steps.ResteasyStandaloneBuildStep$staticInit-210558872.deploy_0(ResteasyStandaloneBuildStep$staticInit-210558872.zig:749)
        at io.quarkus.deployment.steps.ResteasyStandaloneBuildStep$staticInit-210558872.deploy(ResteasyStandaloneBuildStep$staticInit-210558872.zig:36)
        at io.quarkus.runner.ApplicationImpl.(ApplicationImpl.zig:141)
        ... 9 more
Caused by: java.lang.RuntimeException: Error injecting org.eclipse.microprofile.reactive.messaging.Emitter org.acme.messaging.controller.MessageSender.emitter
        at org.acme.messaging.controller.MessageSender_Bean.create(MessageSender_Bean.zig:248)
        at org.acme.messaging.controller.MessageSender_Bean.get(MessageSender_Bean.zig:286)
        at org.acme.messaging.controller.MessageSender_Bean.get(MessageSender_Bean.zig:321)
        at org.acme.messaging.filter.RequestFilter_Bean.create(RequestFilter_Bean.zig:131)
        ... 49 more
Caused by: java.lang.IllegalStateException: Unable to find a emitter with the name outtopic, available emitters are: []
        at io.smallrye.reactive.messaging.extension.ChannelProducer.getEmitter(ChannelProducer.java:191)
        at io.smallrye.reactive.messaging.extension.ChannelProducer.produceEmitter(ChannelProducer.java:139)
        at io.smallrye.reactive.messaging.extension.ChannelProducer_ProducerMethod_produceEmitter_86c8bb1e4975aaf33dff96bec0d2fc8fa44920ab_Bean.create(ChannelProducer_ProducerMethod_produceEmitter_86c8bb1e4975aaf33dff96bec0d2fc8fa44920ab_Bean.zig:247)
        at io.smallrye.reactive.messaging.extension.ChannelProducer_ProducerMethod_produceEmitter_86c8bb1e4975aaf33dff96bec0d2fc8fa44920ab_Bean.get(ChannelProducer_ProducerMethod_produceEmitter_86c8bb1e4975aaf33dff96bec0d2fc8fa44920ab_Bean.zig:277)
        at io.smallrye.reactive.messaging.extension.ChannelProducer_ProducerMethod_produceEmitter_86c8bb1e4975aaf33dff96bec0d2fc8fa44920ab_Bean.get(ChannelProducer_ProducerMethod_produceEmitter_86c8bb1e4975aaf33dff96bec0d2fc8fa44920ab_Bean.zig:312)
        at io.quarkus.arc.impl.CurrentInjectionPointProvider.get(CurrentInjectionPointProvider.java:53)
        at org.acme.messaging.controller.MessageSender_Bean.create(MessageSender_Bean.zig:231)
        ... 52 more

Environment (please complete the following information):

  • Output of uname -a or ver: Linux 5.4.0-37-generic Arc - implement CDI inheritance rules properly #41-Ubuntu SMP Wed Jun 3 18:57:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
  • Output of java -version: openjdk version "11.0.7" 2020-04-14
    OpenJDK Runtime Environment (build 11.0.7+10-post-Ubuntu-3ubuntu1)
    OpenJDK 64-Bit Server VM (build 11.0.7+10-post-Ubuntu-3ubuntu1, mixed mode, sharing)
  • GraalVM version (if different from Java):
  • Quarkus version or git rev: 1.5.2.Final
  • Build tool (ie. output of mvnw --version or gradlew --version): Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)

Additional context

  • If there is no receiver method with @Incoming annotation, quarkus starts without error.
  • If JAXRS ContainerRequestFilter don't inject a controller with injected MicroProfile Messaging Emitter, quarkus starts without error.
@TobiWeier TobiWeier added the kind/bug Something isn't working label Jun 22, 2020
@kenfinnigan
Copy link
Member

What's the use case for using an Emitter inside a Filter?

It's not a use case we've planned for

@TobiWeier
Copy link
Author

Hi Ken,
thanks for your answer. We are not injecting the emitter directly in the jaxrs filter. In our application users are working in a special business section. Each user has a default section, saved in the database. The jaxrs filter set the actual section for each request. Client applications can set the actual section via a special http header. If there is no header the jaxrs filter needs to read the default section for the user. For this it uses an injected controller. This controller has an injected emitter - for other purposes.

The quarkus will not start if an emitter is injected anywhere in a class injected into a Jaxrs filter or a dependency thereon.

For example, following will fail:

@Provider
public class MyContainerRequestFilter implements ContainerRequestFilter {
  @Inject
  ControllerA controllerA;
} 

@Dependent
public class ControllerA {
  @Inject
  ControllerB controllerB;
}

@Dependent
public class ControllerB {
  @Inject
  @Channel("mytopic")
  Emitter emitter;
}

@kenfinnigan
Copy link
Member

kenfinnigan commented Jun 29, 2020

I'm not 100% certain, but the issue might be that JAX-RS providers by default are not CDI Beans?

@TobiWeier
Copy link
Author

@kenfinnigan
Copy link
Member

Right, so the provider isn't a JAX-RS Resource so injection won't work

@TobiWeier
Copy link
Author

So, CDI injection in JAX-RS providers are not supported in Quarkus?

@kenfinnigan
Copy link
Member

@mkouba can confirm, but I don't think so.

@mkouba
Copy link
Contributor

mkouba commented Jun 30, 2020

I think that JAX-RS providers do support injection. If you look at the stack trace it seems that ArC fails to create a bean instance of org.acme.messaging.filter.RequestFilter because of:

Caused by: java.lang.IllegalStateException: Unable to find a emitter with the name outtopic, available emitters are: []
        at io.smallrye.reactive.messaging.extension.ChannelProducer.getEmitter(ChannelProducer.java:191)
        at io.smallrye.reactive.messaging.extension.ChannelProducer.produceEmitter(ChannelProducer.java:139)

We need to find out why there are no emmiters registered...

@mkouba
Copy link
Contributor

mkouba commented Jun 30, 2020

So I think that the problem is that JAX-RS filters are instantiated before io.quarkus.smallrye.reactivemessaging.deployment.SmallRyeReactiveMessagingProcessor.build() registers the emitters.

A workaround could be to change the scope of org.acme.messaging.controller.MessageSender from @Dependent to @ApplicationScoped (so that it's instantiated lazily, when first needed).

Another workaround would be use javax.enterprise.inject.Instance. Note that we have to use two fields because for a @Dependent bean Instance#get() always returns a new instance which it not very efficient.

@MessageHeader
@Provider
public class RequestFilter implements ContainerRequestFilter {
    
    private static final String X_MSG = "x-message";
    
    @Inject
    Instance<MessageSender> instance;
   
    volatile MessageSender controller;

    @Override
    public void filter(ContainerRequestContext crc) throws IOException {
        if (controller == null) { // not atomic but good enough, we'll create few more instance at worst ;-)
           controller = instance.get();
        }
        String msg = crc.getHeaderString(X_MSG);
        if (msg != null) {
            controller.send(msg);
        }
    }
  

UPDATE: if using the quarkus master branch you can replace the snippet above with:

@MessageHeader
@Provider
public class RequestFilter implements ContainerRequestFilter {
    
    private static final String X_MSG = "x-message";
    
    @WithCaching
    @Inject
    Instance<MessageSender> instance;

    @Override
    public void filter(ContainerRequestContext crc) throws IOException {
        String msg = crc.getHeaderString(X_MSG);
        if (msg != null) {
            instance.get().send(msg);
        }
    }
  

@geoand
Copy link
Contributor

geoand commented Jul 14, 2020

So can we close this one as a proper workaround exists? Maybe we need to add some sort of documentation?

@geoand
Copy link
Contributor

geoand commented Oct 8, 2021

I am going to close this because for RESTEasy Classic the workaround Martin mentions above works and for RESTEasy Reactive there is no such limitation as the JAX-RS Filter lifecycle is completely controlled by CDI

@geoand geoand closed this as completed Oct 8, 2021
@geoand geoand added the triage/out-of-date This issue/PR is no longer valid or relevant label Oct 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working triage/out-of-date This issue/PR is no longer valid or relevant
Projects
None yet
Development

No branches or pull requests

4 participants