Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UserTransaction jndi lookup failed when using CompletableFuture

I have a code which does context lookup to get UserTransaction JNDI as ctx.lookup("java:comp/UserTransaction").

When I run this code without using CompletableFuture, it works as expected.

When working with CompletableFuture in async thread, it gives exception saying jndi lookup failed.

I tried to check if I can get the required JNDI from global scope, but no luck.

like image 399
Code Arrow Avatar asked Dec 17 '25 14:12

Code Arrow


2 Answers

CompletableFutures often run on the JDK's ForkJoinPool rather than application server managed threads, and so lack access to services provided by the application server. MicroProfile Context Propagation (available in Liberty) solves this problem by giving you a way to create CompletableFutures that run on the Liberty thread pool and with access to application component context.

In server.xml,

<featureManager>
  <feature>mpContextPropagation-1.2</feature> <!-- 1.0 is also valid -->
  <feature>jndi-1.0</feature>
  <feature>jdbc-4.2</feature> <!-- or some other feature that participates in transactions -->
  ... other features
</featureManager>

In your application,

import org.eclipse.microprofile.context.ManagedExecutor;
import org.eclipse.microprofile.context.ThreadContext;
...
ManagedExecutor executor = ManagedExecutor.builder()
                           .propagate(ThreadContext.APPLICATION)
                           .build();

CompletableFuture<?> f = executor.supplyAsync(() -> {
    UserTransaction tx = InitialContext.doLookup("java:comp/UserTransaction");
    ...
});

...
executor.shutdown();

If you don't want to construct a new ManagedExecutor, Liberty will also let you cast an EE Concurrency ManagedExecutorService to ManagedExecutor and use that. For example,

ManagedExecutor executor = InitialContext.doLookup("java:comp/DefaultManagedExecutor");

It should also be noted that with a ManagedExecutor, the application context is made available to dependent stages as well as the initial stage, allowing you to perform the lookup in a dependent stage such as the following if you prefer:

executor.supplyAsync(supplier).thenApplyAsync(v -> {
    UserTransaction tx = InitialContext.doLookup("java:comp/UserTransaction");
    ...
});
like image 170
njr Avatar answered Dec 20 '25 07:12

njr


The problem seems to be that the JNDI context is not propagated to the async thread, so when the CompletionStage attempts to execute the JNDI lookup, it has no context, so it doesn't know which component it is in and thus fails.

There is a very detailed explanation of context propagation and how to do it effectively in Open Liberty (which is the underlying product for WebSphere Liberty) at https://openliberty.io/docs/21.0.0.8/microprofile-context-propagation.html - I'd highly suggest reading it.

Certain Java/Jakarta/MicroProfile APIs will allow you to specify the async service (ExecutorService) to use for the async operation. If possible, you can pass it an instance of ManagedExecutorService which should propagate contexts (like JNDI, security, classloading, etc.) to the async thread. Otherwise, you may need to specify the managed executor service when constructing your CompletionStage.

like image 25
Andy McCright Avatar answered Dec 20 '25 08:12

Andy McCright



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!