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

ContextNotActiveException when using SuspendableContainerRequestContext #2981

Closed
csotiriou opened this issue Jun 26, 2019 · 22 comments
Closed
Labels
area/reactive kind/bug Something isn't working
Milestone

Comments

@csotiriou
Copy link

csotiriou commented Jun 26, 2019

I am using Mongo (JVM, I do not care about native) as a pre-request filter, in order to get some data from the MongoDB before it reaches the actual Resource.

Mongo Driver is asynchronous by nature, so I call suspend() before I start making the call. However, when trying to do resume(), I get a javax.enterprise.context.ContextNotActiveException.

The following is written in the public void filter(ContainerRequestContext requestContext) function of a ContainerRequestFilter

SuspendableContainerRequestContext suspendableContainerRequestContext = (SuspendableContainerRequestContext) requestContext;

        suspendableContainerRequestContext.suspend();

        MongoCollection collection = mongoConfig.getAssetCachingCollection(); //this is the mongo collection
        FindPublisher publisher = collection.find(eq("_id", idForSearch)).maxTime(1, TimeUnit.SECONDS);

        Subscriber<Document> subscriber = new Subscriber<Document>() {
            @Override
            public void onSubscribe(Subscription s) {
                s.request(1);
            }

            @Override
            public void onNext(Document document) {
                //this never gets called
            }

            @Override
            public void onError(Throwable t) {
                System.out.println("error: " + t.toString());
                //TODO
            }

            @Override
            public void onComplete() {
                System.out.println("completed");
                suspendableContainerRequestContext.resume(); //CRASHES! Context Not Active?
            }
        };

        publisher.subscribe(subscriber);

When the onComplete() is called, I get the ContextNotActiveException.

javax.enterprise.context.ContextNotActiveException: interface javax.enterprise.context.RequestScoped
@csotiriou csotiriou changed the title ContextNotActiveException when using SuspendableContianerRequestContext ContextNotActiveException when using SuspendableContainerRequestContext Jun 26, 2019
@FroMage
Copy link
Member

FroMage commented Jun 26, 2019

You should use MP-CP. You can inject a ThreadContext object and use its methods to wrap your calls. If your underlying mongo client uses RxJava2 it should work OOTB (if you depend on smallrye-context-propagation-propagators-rxjava2).

I don't think we can use ThreadContext to wrap Publisher because it does not depend on RS, but we could make a case for it in the spec.

@csotiriou
Copy link
Author

I see what you mean. So, as I understand it, I need to propagate the current context so that I can call resume() later, am I right?

Just two notes

  1. The rxJava implementation of MongoDB is deprecated. The Reactive Streams implementation of MongoDB is the only supported async version.
  2. How can I use context propagation in this case? The doc mentions that context propagation works with ReactiveStreams when quarkus-smallrye-reactive-streams-operators is in the pom. However, when importing it I still get the same error.

@FroMage
Copy link
Member

FroMage commented Jun 26, 2019

The Reactive Streams implementation of MongoDB is the only supported async version.

Reactive Streams is not an implementation, it's an API. The implementation is either RxJava2 or Reactor. Do you know which one it is you're using?

How can I use context propagation in this case? The doc mentions that context propagation works with ReactiveStreams when quarkus-smallrye-reactive-streams-operators is in the pom. However, when importing it I still get the same error.

If the Mongo client is using RS-Operators, then it uses RxJava2 and so yes, it should work. Is that the case?

@csotiriou
Copy link
Author

Thanks for the immediate answer.

Actually, this is the driver I am using:
https://mongodb.github.io/mongo-java-driver-reactivestreams/

It exposes APIs only through publishers and subscribers from the org.reactivestreams implementation.

The other ones, like
https://mongodb.github.io/mongo-java-driver-rx/
https://mongodb.github.io/mongo-java-driver/ (This one mentions that the async part of this driver is now deprecated in favor of the Reactive Streams driver)

seem to be abandoned officially, as stated by the docs.

@FroMage
Copy link
Member

FroMage commented Jun 26, 2019

OK, so you're right, they implemented their own RS implementation. That's ambitious. So in any case, MP-CP cannot propagate context automatically in this case since it does not know about their implementation.

This should get you working:

        Executor contextExecutor = context.currentContextExecutor();
        Subscriber<Document> subscriber = new Subscriber<Document>() {
            @Override
            public void onSubscribe(Subscription s) {
                contextExecutor.execute(() -> s.request(1));
            }

            @Override
            public void onNext(Document document) {
                //this never gets called
            }

            @Override
            public void onError(Throwable t) {
                System.out.println("error: " + t.toString());
                //TODO
            }

            @Override
            public void onComplete() {
                System.out.println("completed");
                contextExecutor.execute(() -> suspendableContainerRequestContext.resume()); //CRASHES! Context Not Active?
            }
        };

With this thread context injected in your interceptor:

    @Inject
    ThreadContext context;

You can also use programmatic injection if your interceptor is not a bean.

@FroMage
Copy link
Member

FroMage commented Jun 26, 2019

I've filed eclipse/microprofile-context-propagation#166 to add support for this to MP-CP.

@csotiriou
Copy link
Author

@FroMage thank you. But I still have no luck.

        MongoCollection collection = mongoConfig.get().getAssetCachingCollection();
        Publisher<Document> publisher = collection.find(eq("_id", idForSearch));

        Executor contextExecutor = context.currentContextExecutor();
        Subscriber<Document> subscriber = new Subscriber<Document>() {
            @Override
            public void onSubscribe(Subscription s) {
                contextExecutor.execute(() -> s.request(1));
            }

            @Override
            public void onNext(Document document) {
                //this never gets called
            }

            @Override
            public void onError(Throwable t) {
                System.out.println("error: " + t.toString());
                //TODO
            }

            @Override
            public void onComplete() {
                System.out.println("completed");
                contextExecutor.execute(() -> suspendableContainerRequestContext.resume()); //CRASHES! Context Not Active?
            }
        };

        publisher.subscribe(subscriber);

Error:
java.lang.IllegalStateException: UT000048: No request is currently active

The error seems to have changed a little, but the root of the problem is probably the same, am I right?

@csotiriou
Copy link
Author

I apologize. Your solution worked, and I thank you very much, @FroMage . The next error originated by somewhere else in my code, where I hadn't used MP-CP (where I also should).

@FroMage
Copy link
Member

FroMage commented Jun 26, 2019

OK, excellent, this is great news :)

Let me know if you have success in that other part of your code.

@csotiriou
Copy link
Author

@FroMage I stumbled across a discovery, actually.

The other error was inside my interceptor, where I also wanted to do some logging of the inbound request. This is a method interceptor - so no filter there.

It turns out that when using the method suggested, the HTTPServletRequest cannot be injected anymore.

Sample code exhibiting the problem:

 Executor contextExecutor = context.currentContextExecutor();
        suspendableContainerRequestContext.suspend();

        MongoCollection collection = mongoConfig.get().getAssetCachingCollection();
        Publisher<Document> publisher = collection.find(eq("_id", idForSearch));

        HttpServletRequest request = this.request;
        Subscriber<Document> subscriber = new Subscriber<Document>() {
            @Override
            public void onSubscribe(Subscription s) {
                contextExecutor.execute(() -> s.request(1));
            }

            @Override
            public void onNext(Document document) {
                //this never gets called
            }

            @Override
            public void onError(Throwable t) {
                System.out.println("error: " + t.toString());
                //TODO
            }

            @Override
            public void onComplete() {
                System.out.println("completed");
//                request.getHeaderNames(); //this doesn't work - no request active
                contextExecutor.execute(() -> {
                    System.out.println("request" + request.getHeaderNames()); // error: no request active!
                });
                contextExecutor.execute(() -> suspendableContainerRequestContext.resume());
            }
        };

        publisher.subscribe(subscriber);

I have injected the HTTP Servlet Request via @Inject HTTPServletRequest request in the controller.

The request is also inactive inside the interceptor. So, I don't know why this is caused or how can I get the HTTPServletRequest from inside an interceptor (it worked while using sync filters).

@FroMage
Copy link
Member

FroMage commented Jun 27, 2019

So, probably HTTPServletRequest is a proxy which looks up the current request via some context. It's not RESTEasy I think, so propbably undertow, right @stuartwdouglas ?

@csotiriou
Copy link
Author

Maybe this has something to do with this problem?

eclipse/microprofile-context-propagation#134

Is the current request being treated a different way? If yes, how can I gain access to the current request inside an interceptor when async filters are used in Quarkus?

@FroMage
Copy link
Member

FroMage commented Jun 27, 2019

No, it's probably that we need a context provider for undertow. I just have to find what injects that servlet request.

@FroMage
Copy link
Member

FroMage commented Jun 27, 2019

So what's your exact stack trace?

@FroMage
Copy link
Member

FroMage commented Jun 27, 2019

It looks like ServletRequestContext is what I have to look at.

@csotiriou
Copy link
Author

csotiriou commented Jun 27, 2019

Most probably you are right.

Here is a capture of the stacktrace. The exception is caused by accessing the injected HTTPServletRequest inside an interceptor (medhod annotated with @AroundInvoke).

The relevant piece of code inside the handler of @AroundInvoke:

   returnValue = context.proceed();
            async = returnValue instanceof CompletionStage;
            if (async) {
                final CompletionStage<?> result = (CompletionStage) returnValue;
                threadContext.withContextCapture(result).handle((o, throwable) -> {
                    after(o, throwable, logpointUserContext);
                    return o;
                });
            }
            return returnValue;

The after() function contains a piece of code accessing an injected HTTPServlerRequest. Thisis where the exception happened.

Stacktrace Capture:

Capture

FroMage added a commit to FroMage/quarkus that referenced this issue Jun 27, 2019
@FroMage
Copy link
Member

FroMage commented Jun 27, 2019

OK, I should have fixed it with #3006, can you try it out?

@csotiriou
Copy link
Author

@FroMage thank you very much. I will try it later today, since I need to setup my environment to compile quarkus, and use specific versions, etc. I will post back with my findings.

@FroMage
Copy link
Member

FroMage commented Jun 27, 2019

OK thanks.

@csotiriou
Copy link
Author

@FroMage I can verify that your fork works.

I build the entire Quarkus from source (your fork) and I added 999-SNAPSHOT as a dependency. I can see that HTTPServletRequest is usable again, after the asynchronous filter ends.

Thank you very much for your support and your efforts. I will wait until this gets merged into an official release.

stuartwdouglas added a commit that referenced this issue Jun 27, 2019
@FroMage
Copy link
Member

FroMage commented Jun 28, 2019

OK, let's close this issue then, thanks!

@FroMage FroMage closed this as completed Jun 28, 2019
@FroMage FroMage added kind/bug Something isn't working area/reactive labels Jun 28, 2019
@FroMage FroMage added this to the 0.19.0 milestone Jun 28, 2019
dmlloyd pushed a commit to dmlloyd/quarkus that referenced this issue Jul 11, 2019
@MotaOcimar
Copy link

Hello, I recently had to make changes to the dependencies in my Quarkus project. I am now using both Resteasy Reactive and Resteasy Classic in different parts of the project. However, since the changes, I have been receiving the error message java.lang.IllegalStateException: UT000048: No request is currently active in an area that was previously working well. I am unsure of the cause of this issue, but I have noticed that @csotiriou had a similar problem and was able to resolve it by using MicroProfile Context Propagation. Are there any suggestions on how I can fix this problem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/reactive kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants