Java CompletableFuture + Resteasy
Asked Answered
B

1

8

I have been using Java's CompletableFuture like this

CompletableFuture.runAsync(() -> {//Some code here });

When I try to use a Resteasy Client inside this block of code I get a

javax.ws.rs.ProcessingException: Unable to find a MessageBodyReader of content-type application/json;charset=utf-8 and type class java.lang.String

If i use the client outside of the completablefuture it works. The Resteasy code looks something like this

        ResteasyClient client = new ResteasyClientBuilder().build();
        client.register(new AcceptEncodingFilter("gzip"));
        ResteasyWebTarget target = client.target(exampleURL);

        target = target.queryParam("1", 1)
                .queryParam("2", "1")
                .queryParam("3", 3)
                .queryParam("4", 4)
                .queryParam("5", "5");

        Response response = target.request().get();
        resultString = response.readEntity(String.class);

I will run the resteasy code outisde of the completablefuture to "fix" the problem but would like to understand why this happens.

The resteasy code inside the CompletableFuture looks like this:

CompletableFuture.runAsync(() -> {
            try {
                ResteasyClient client = new ResteasyClientBuilder().build();
                client.register(new AcceptEncodingFilter("gzip"));
                ResteasyWebTarget target = client.target("http://test.com");

                target = target.queryParam("1", "1")
                        .queryParam("2", "2")
                        .queryParam("3", "3")
                        .queryParam("4", "4")
                        .queryParam("5", "5");

                Response response = target.request().get();
                String resultString = response.readEntity(String.class);

                response.close();
                client.close();
            } catch (Exception e){
                e.printStackTrace();
            }
        });

the same code outside the CompletableFuture works

Bcd answered 14/4, 2015 at 16:57 Comment(8)
Can you show the code with the Resteasy code inside the runAsync?Hautboy
Hi, I added the Resteasy code inside the runAsync, thanks for taking the time to look at itBcd
Where do you run the code? Do you use an application server?Aeroscope
i use play framework, under the hood play uses nettyBcd
CompleteableFuture uses the common ForkJoinPool for selecting threads to ansychronously work in. Maybe there is some Context missing within those threads. I don't know the play framework/netty, but if one of them provides a ThreadFactory or a Threadpool with Threads in its context, you may try the runAsync(Runnable, Executor) method with those to provide the context.Aeroscope
I thought the same and used a thredpoolexecutor (not from the framework but one I instantiated with new) it works with it, it also works if i just assign everything to a new runnable and then execute the run method. The problem is not that the code will not run inside a thread but that it will not run inside the completablefuture (with no threadpool) I would like to understand whyBcd
If using arbitrary threads/executors work, than it might not be the absence of associated information but the fact that the F/J default thread pool is shared and thus, wrongly associated information dangling from another, previous use of the same thread is present…Baronage
I tried your code with 3.5.0 RestEasy bits I was able to run your code without any problem, for 3.5.0 you need to use AcceptEncodingGZIPFilterNert
S
1

I had this exact issue in Wildfly 26.1.3, which I believe uses RestEasy 4.7.7. If RestEasy was used in a "normal" method, it worked. If it was used in a thread created by CompletableFuture.runAsync, it failed with the "Unable to find a MessageBodyReader" error message.

I used a debugger to see what was happening and I found that the RestEasy client was not properly configured when it was created in the thread. I was able to work around the problem by creating the client as follows:

Configuration configuration = LocalResteasyProviderFactory.getInstance().getConfiguration();
Client client = ClientBuilder.newClient(configuration);

Adding this code resulted in a warning in the Wildfly server.log:

RESTEASY002155: Provider class org.jboss.resteasy.plugins.providers.jackson.PatchMethodFilter is already registered.  2nd registration is being ignored

EDIT: after further research, the correct answer is to use ManagedScheduledExecutorService to execute the asynchronous task, not CompletableFuture. This ensures that the RestEasy configuration is correctly initialized.

Sorghum answered 30/6, 2023 at 17:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.