Home > Software design >  ContextNotActiveException after running a Task in a new Thread on StartupEvent
ContextNotActiveException after running a Task in a new Thread on StartupEvent

Time:08-18

I think my question has not been asked in this way before:

I want to fill the Cache of my Project on Startup. The documentation on the Quarkus Website tells me, that one way to do this is to add the "@Observes StartupEvent e" construct into the method I am trying to run. When filling the cache, I am pulling data from a REST API of which I am injecting the corresponding client into the class where my StartupMethod resides (so that I can store the result in its respective field)

Because this data-fetching takes very long, I want to set it off onto another thread which is created shortly after the StartupEvent method has been triggered. This is what my construct looks like:

public class CacheClass {

  private List<SomeObject> cacheData;

  @Inject
  SomeRestClient someRestClient;

  public void init(@Observes StartupEvent e) {
    //Open a new Thread for the cache fill method
    Thread thread = new Thread(() -> fetchDataForCache());
    thread.start();
    //After that, open more threads which shall fill different Caches
  }

  public void fetchDataForCache() {
    List<SomeObject> tmpData = someRestClient.fetchSomeData();
    //Do some validation and write the result into "dataCache"
  }

  //Getter, Setter and other stuff

}

The thread that is supposed to be opened will successfully do so, however, as soon as the method call to the REST Client is made, I get a ContextNotActiveException which looks like this:

Exception in thread "Thread-68" javax.enterprise.context.ContextNotActiveException
at io.quarkus.arc.impl.ClientProxies.getDelegate(ClientProxies.java:46)
at someRestPackage.SomeRestClient_ClientProxy.arc$delegate(Unknown Source)
at someRestPackage.SomeRestClient_ClientProxy.findLatestEntry(Unknown Source)
at somePackage.CacheClass(CacheClass.java:15)
at somePackage.CacheClass_Subclass.fetchDataForCache$$superforward1(Unknown Source)
at somePackage.CacheClass_Subclass$$function$$3.apply(Unknown Source)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:51)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
at somePackage.CacheClass_Subclass.fetchDataForCache(Unknown Source)
at somePackage.CacheClass$1.run(CacheClass.java:11)
at java.base/java.lang.Thread.run(Thread.java:829)

However, when removing the Thread part and just calling "fetchDataForCache()" on the main thread, the method gets executed without any problems, so it must have to do something with the multithreading

Now maybe I am missing something really obvious and I would greatly appreciate someone explain to me why this happens and how to fix it or, maybe to give me some alternatives to how an asynchronous StartupEvent (with the needed Context) can be accomplished.

CodePudding user response:

Your newly created thread has no (CDI) context active as it tries to access CDI bean (someRestClient). You need to propagate the context to the new thread, when you start it.

Use following extension to do it for you

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-context-propagation</artifactId>
</dependency>

Afterwards you can create ManagedExecutor provided by this extension

ManagedExecutor executor = ManagedExecutor.builder()
                                            .maxAsync(1)
                                            .propagated(ThreadContext.CDI, ThreadContext.APPLICATION)
                                            .build();

and let it execute your async work. (Don't forget to close it, to release any resources when it isn't needed anymore)

More information https://github.com/eclipse/microprofile-context-propagation/, https://quarkus.io/guides/context-propagation

  • Related