From cbb11fccbcf0c01e9d2d4eb5dafec1a30f9aa323 Mon Sep 17 00:00:00 2001 From: David Kral Date: Tue, 24 Sep 2019 10:45:51 +0200 Subject: [PATCH 1/5] Fixes #4264 - AsyncInvocationinverceptors not properly created for each request Signed-off-by: David Kral --- .../restclient/ExecutorServiceWrapper.java | 17 +++++++----- .../restclient/InterfaceModel.java | 27 ++++++++++--------- .../microprofile/restclient/MethodModel.java | 15 ++++++++--- .../restclient/RestClientBuilderImpl.java | 10 ++----- .../restclient/RestClientModel.java | 6 ++--- 5 files changed, 42 insertions(+), 33 deletions(-) diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java index 54599e8d94..e3f8ee846e 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java @@ -35,13 +35,12 @@ */ class ExecutorServiceWrapper implements ExecutorService { + static final ThreadLocal> asyncInterceptors = new ThreadLocal<>(); + private final ExecutorService wrapped; - private final List asyncInterceptors; - ExecutorServiceWrapper(ExecutorService wrapped, - List asyncInterceptors) { + ExecutorServiceWrapper(ExecutorService wrapped) { this.wrapped = wrapped; - this.asyncInterceptors = asyncInterceptors; } @Override @@ -112,15 +111,21 @@ public void execute(Runnable command) { } private Callable wrap(Callable task) { + List asyncInvocationInterceptors = asyncInterceptors.get(); return () -> { - asyncInterceptors.forEach(AsyncInvocationInterceptor::applyContext); + if (asyncInvocationInterceptors != null) { + asyncInvocationInterceptors.forEach(AsyncInvocationInterceptor::applyContext); + } return task.call(); }; } private Runnable wrap(Runnable task) { + List asyncInvocationInterceptors = asyncInterceptors.get(); return () -> { - asyncInterceptors.forEach(AsyncInvocationInterceptor::applyContext); + if (asyncInvocationInterceptors != null) { + asyncInvocationInterceptors.forEach(AsyncInvocationInterceptor::applyContext); + } task.run(); }; } diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/InterfaceModel.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/InterfaceModel.java index 1a045eede4..6bd0e9531a 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/InterfaceModel.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/InterfaceModel.java @@ -38,6 +38,7 @@ import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor; +import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory; import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; import org.glassfish.jersey.client.inject.ParameterUpdater; @@ -64,7 +65,7 @@ class InterfaceModel { private final CreationalContext creationalContext; private final List clientHeaders; - private final List asyncInterceptors; + private final List asyncInterceptorFactories; private final Set responseExceptionMappers; private final Set paramConverterProviders; private final Set interceptorAnnotations; @@ -73,23 +74,23 @@ class InterfaceModel { /** * Creates new model based on interface class. Interface is parsed according to specific annotations. * - * @param restClientClass interface class - * @param responseExceptionMappers registered exception mappers - * @param paramConverterProviders registered parameter providers - * @param asyncInterceptors async interceptors + * @param restClientClass interface class + * @param responseExceptionMappers registered exception mappers + * @param paramConverterProviders registered parameter providers + * @param asyncInterceptorFactories async interceptor factories * @param injectionManager * @return new model instance */ static InterfaceModel from(Class restClientClass, Set responseExceptionMappers, Set paramConverterProviders, - List asyncInterceptors, + List asyncInterceptorFactories, InjectionManager injectionManager, BeanManager beanManager) { return new Builder(restClientClass, responseExceptionMappers, paramConverterProviders, - asyncInterceptors, + asyncInterceptorFactories, injectionManager, beanManager).build(); } @@ -106,7 +107,7 @@ private InterfaceModel(Builder builder) { this.paramConverterProviders = builder.paramConverterProviders; this.interceptorAnnotations = builder.interceptorAnnotations; this.creationalContext = builder.creationalContext; - this.asyncInterceptors = builder.asyncInterceptors; + this.asyncInterceptorFactories = builder.asyncInterceptorFactories; this.beanManager = builder.beanManager; } @@ -169,8 +170,8 @@ List getClientHeaders() { * * @return registered async interceptors */ - List getAsyncInterceptors() { - return asyncInterceptors; + List getAsyncInterceptorFactories() { + return asyncInterceptorFactories; } /** @@ -251,7 +252,7 @@ private static class Builder { private ClientHeadersFactory clientHeadersFactory; private CreationalContext creationalContext; private List clientHeaders; - private List asyncInterceptors; + private List asyncInterceptorFactories; private Set responseExceptionMappers; private Set paramConverterProviders; private Set interceptorAnnotations; @@ -259,14 +260,14 @@ private static class Builder { private Builder(Class restClientClass, Set responseExceptionMappers, Set paramConverterProviders, - List asyncInterceptors, + List asyncInterceptorFactories, InjectionManager injectionManager, BeanManager beanManager) { this.injectionManager = injectionManager; this.restClientClass = restClientClass; this.responseExceptionMappers = responseExceptionMappers; this.paramConverterProviders = paramConverterProviders; - this.asyncInterceptors = asyncInterceptors; + this.asyncInterceptorFactories = asyncInterceptorFactories; this.beanManager = beanManager; filterAllInterceptorAnnotations(); } diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/MethodModel.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/MethodModel.java index 4e738a0342..d106f0cfc5 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/MethodModel.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/MethodModel.java @@ -69,6 +69,7 @@ import org.eclipse.microprofile.rest.client.RestClientDefinitionException; import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor; +import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; /** @@ -121,7 +122,7 @@ private MethodModel(Builder builder) { subResourceModel = RestClientModel.from(returnType.getRawType(), interfaceModel.getResponseExceptionMappers(), interfaceModel.getParamConverterProviders(), - interfaceModel.getAsyncInterceptors(), + interfaceModel.getAsyncInterceptorFactories(), interfaceModel.getInjectionManager(), interfaceModel.getBeanManager()); } else { @@ -243,6 +244,14 @@ private CompletableFuture asynchronousCall(Invocation.Builder builder, Object entity, Method method, MultivaluedMap customHeaders) { + + //AsyncInterceptors initialization + List asyncInterceptors = interfaceModel.getAsyncInterceptorFactories().stream() + .map(AsyncInvocationInterceptorFactory::newInterceptor) + .collect(Collectors.toList()); + asyncInterceptors.forEach(AsyncInvocationInterceptor::prepareContext); + ExecutorServiceWrapper.asyncInterceptors.set(asyncInterceptors); + CompletableFuture result = new CompletableFuture<>(); Future theFuture; if (entity != null @@ -255,7 +264,7 @@ private CompletableFuture asynchronousCall(Invocation.Builder builder, CompletableFuture completableFuture = (CompletableFuture) theFuture; completableFuture.thenAccept(response -> { - interfaceModel.getAsyncInterceptors().forEach(AsyncInvocationInterceptor::removeContext); + asyncInterceptors.forEach(AsyncInvocationInterceptor::removeContext); try { evaluateResponse(response, method); if (returnType.getType().equals(Void.class)) { @@ -269,7 +278,7 @@ private CompletableFuture asynchronousCall(Invocation.Builder builder, result.completeExceptionally(e); } }).exceptionally(throwable -> { - interfaceModel.getAsyncInterceptors().forEach(AsyncInvocationInterceptor::removeContext); + asyncInterceptors.forEach(AsyncInvocationInterceptor::removeContext); result.completeExceptionally(throwable); return null; }); diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java index ded8ec5e0c..43e00b69ed 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java @@ -154,13 +154,7 @@ public T build(Class interfaceClass) throws IllegalStateException, RestCl //We need to check first if default exception mapper was not disabled by property on builder. registerExceptionMapper(); - //AsyncInterceptors initialization - List asyncInterceptors = asyncInterceptorFactories.stream() - .map(AsyncInvocationInterceptorFactory::newInterceptor) - .collect(Collectors.toList()); - asyncInterceptors.forEach(AsyncInvocationInterceptor::prepareContext); - - clientBuilder.executorService(new ExecutorServiceWrapper(executorService.get(), asyncInterceptors)); + clientBuilder.executorService(new ExecutorServiceWrapper(executorService.get())); if (null != sslContext) { clientBuilder.sslContext(sslContext); @@ -187,7 +181,7 @@ public T build(Class interfaceClass) throws IllegalStateException, RestCl RestClientModel restClientModel = RestClientModel.from(interfaceClass, responseExceptionMappers, paramConverterProviders, - asyncInterceptors, + asyncInterceptorFactories, injectionManagerExposer.injectionManager, CdiUtil.getBeanManager()); diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientModel.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientModel.java index 4e81ca3833..e0ff634191 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientModel.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientModel.java @@ -28,7 +28,7 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.ext.ParamConverterProvider; -import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor; +import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; import org.glassfish.jersey.internal.inject.InjectionManager; @@ -56,13 +56,13 @@ class RestClientModel { static RestClientModel from(Class restClientClass, Set responseExceptionMappers, Set paramConverterProviders, - List asyncInterceptors, + List asyncInterceptorFactories, InjectionManager injectionManager, BeanManager beanManager) { InterfaceModel interfaceModel = InterfaceModel.from(restClientClass, responseExceptionMappers, paramConverterProviders, - asyncInterceptors, + asyncInterceptorFactories, injectionManager, beanManager); return new Builder() From 45cf1700bd629ee45cf096127c7a98193db8c007 Mon Sep 17 00:00:00 2001 From: David Kral Date: Tue, 24 Sep 2019 15:42:27 +0200 Subject: [PATCH 2/5] AsyncInvocationInterceptorFactory should now be properly sorted by their priority. Signed-off-by: David Kral --- .../restclient/ExecutorServiceWrapper.java | 16 +++++--- .../restclient/RestClientBuilderImpl.java | 38 +++++++++++++++++-- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java index e3f8ee846e..2e116b69c9 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java @@ -113,9 +113,7 @@ public void execute(Runnable command) { private Callable wrap(Callable task) { List asyncInvocationInterceptors = asyncInterceptors.get(); return () -> { - if (asyncInvocationInterceptors != null) { - asyncInvocationInterceptors.forEach(AsyncInvocationInterceptor::applyContext); - } + applyContextOnInterceptors(asyncInvocationInterceptors); return task.call(); }; } @@ -123,13 +121,19 @@ private Callable wrap(Callable task) { private Runnable wrap(Runnable task) { List asyncInvocationInterceptors = asyncInterceptors.get(); return () -> { - if (asyncInvocationInterceptors != null) { - asyncInvocationInterceptors.forEach(AsyncInvocationInterceptor::applyContext); - } + applyContextOnInterceptors(asyncInvocationInterceptors); task.run(); }; } + private void applyContextOnInterceptors(List asyncInvocationInterceptors) { + if (asyncInvocationInterceptors != null) { + //applyContext methods need to be called in reverse ordering of priority + for (int i = asyncInvocationInterceptors.size(); i-- > 0; ) { + asyncInvocationInterceptors.get(i).applyContext(); + } + } + } private Collection> wrap(Collection> tasks) { return tasks.stream() diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java index 43e00b69ed..f53f8e2d92 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java @@ -25,6 +25,7 @@ import java.security.AccessController; import java.security.KeyStore; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -36,7 +37,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import java.util.stream.Collectors; import javax.annotation.Priority; import javax.net.ssl.HostnameVerifier; @@ -81,7 +81,7 @@ class RestClientBuilderImpl implements RestClientBuilder { private final Set responseExceptionMappers; private final Set paramConverterProviders; - private final List asyncInterceptorFactories; + private final List asyncInterceptorFactories; private final Config config; private final ConfigWrapper configWrapper; private URI uri; @@ -153,6 +153,8 @@ public T build(Class interfaceClass) throws IllegalStateException, RestCl //We need to check first if default exception mapper was not disabled by property on builder. registerExceptionMapper(); + //sort all AsyncInvocationInterceptorFactory by priority + asyncInterceptorFactories.sort(Comparator.comparingInt(AsyncInvocationInterceptorFactoryPriorityWrapper::getPriority)); clientBuilder.executorService(new ExecutorServiceWrapper(executorService.get())); @@ -181,7 +183,7 @@ public T build(Class interfaceClass) throws IllegalStateException, RestCl RestClientModel restClientModel = RestClientModel.from(interfaceClass, responseExceptionMappers, paramConverterProviders, - asyncInterceptorFactories, + new ArrayList<>(asyncInterceptorFactories), injectionManagerExposer.injectionManager, CdiUtil.getBeanManager()); @@ -393,7 +395,9 @@ private void registerCustomProvider(Object instance, int priority) { paramConverterProviders.add((ParamConverterProvider) instance); } if (instance instanceof AsyncInvocationInterceptorFactory) { - asyncInterceptorFactories.add((AsyncInvocationInterceptorFactory) instance); + asyncInterceptorFactories + .add(new AsyncInvocationInterceptorFactoryPriorityWrapper((AsyncInvocationInterceptorFactory) instance, + priority)); } } @@ -411,4 +415,30 @@ public boolean configure(FeatureContext context) { } } + private static class AsyncInvocationInterceptorFactoryPriorityWrapper + implements AsyncInvocationInterceptorFactory { + + private AsyncInvocationInterceptorFactory factory; + private int priority; + + AsyncInvocationInterceptorFactoryPriorityWrapper(AsyncInvocationInterceptorFactory factory, int priority) { + this.factory = factory; + this.priority = priority; + } + + @Override + public AsyncInvocationInterceptor newInterceptor() { + return factory.newInterceptor(); + } + + int getPriority() { + if (priority <= 0) { + priority = Optional.ofNullable(factory.getClass().getAnnotation(Priority.class)) + .map(Priority::value) + .orElse(Priorities.USER); + } + return priority; + } + } + } From 7419ec6e4134843ee08cc509559dcc293d8105ea Mon Sep 17 00:00:00 2001 From: David Kral Date: Tue, 24 Sep 2019 16:49:30 +0200 Subject: [PATCH 3/5] Priority of registered provider by builder updated Signed-off-by: David Kral --- .../restclient/RestClientBuilderImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java index f53f8e2d92..e963850edb 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java @@ -334,11 +334,11 @@ public RestClientBuilder register(Class componentClass, Map, Integer public RestClientBuilder register(Object component) { if (component instanceof ResponseExceptionMapper) { ResponseExceptionMapper mapper = (ResponseExceptionMapper) component; - registerCustomProvider(component, -1); + registerCustomProvider(component, mapper.getPriority()); clientBuilder.register(mapper, mapper.getPriority()); } else { clientBuilder.register(component); - registerCustomProvider(component, -1); + registerCustomProvider(component, null); } return this; } @@ -380,7 +380,7 @@ private boolean isSupportedCustomProvider(Class providerClass) { || AsyncInvocationInterceptorFactory.class.isAssignableFrom(providerClass); } - private void registerCustomProvider(Object instance, int priority) { + private void registerCustomProvider(Object instance, Integer priority) { if (!isSupportedCustomProvider(instance.getClass())) { return; } @@ -419,9 +419,9 @@ private static class AsyncInvocationInterceptorFactoryPriorityWrapper implements AsyncInvocationInterceptorFactory { private AsyncInvocationInterceptorFactory factory; - private int priority; + private Integer priority; - AsyncInvocationInterceptorFactoryPriorityWrapper(AsyncInvocationInterceptorFactory factory, int priority) { + AsyncInvocationInterceptorFactoryPriorityWrapper(AsyncInvocationInterceptorFactory factory, Integer priority) { this.factory = factory; this.priority = priority; } @@ -431,8 +431,8 @@ public AsyncInvocationInterceptor newInterceptor() { return factory.newInterceptor(); } - int getPriority() { - if (priority <= 0) { + Integer getPriority() { + if (priority == null) { priority = Optional.ofNullable(factory.getClass().getAnnotation(Priority.class)) .map(Priority::value) .orElse(Priorities.USER); From 7025b9ac140c85045b3560bb01a64a2aeeef0141 Mon Sep 17 00:00:00 2001 From: David Kral Date: Tue, 1 Oct 2019 12:12:35 +0200 Subject: [PATCH 4/5] private methods changed to static Signed-off-by: David Kral --- .../restclient/ExecutorServiceWrapper.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java index 2e116b69c9..ecc01e21a7 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java @@ -110,7 +110,7 @@ public void execute(Runnable command) { wrapped.execute(wrap(command)); } - private Callable wrap(Callable task) { + private static Callable wrap(Callable task) { List asyncInvocationInterceptors = asyncInterceptors.get(); return () -> { applyContextOnInterceptors(asyncInvocationInterceptors); @@ -118,7 +118,7 @@ private Callable wrap(Callable task) { }; } - private Runnable wrap(Runnable task) { + private static Runnable wrap(Runnable task) { List asyncInvocationInterceptors = asyncInterceptors.get(); return () -> { applyContextOnInterceptors(asyncInvocationInterceptors); @@ -126,7 +126,7 @@ private Runnable wrap(Runnable task) { }; } - private void applyContextOnInterceptors(List asyncInvocationInterceptors) { + private static void applyContextOnInterceptors(List asyncInvocationInterceptors) { if (asyncInvocationInterceptors != null) { //applyContext methods need to be called in reverse ordering of priority for (int i = asyncInvocationInterceptors.size(); i-- > 0; ) { @@ -135,9 +135,9 @@ private void applyContextOnInterceptors(List asyncIn } } - private Collection> wrap(Collection> tasks) { + private static Collection> wrap(Collection> tasks) { return tasks.stream() - .map(this::wrap) + .map(ExecutorServiceWrapper::wrap) .collect(Collectors.toList()); } } From ff4287d19b6da9eb53d3c90145ce0895b59be974 Mon Sep 17 00:00:00 2001 From: David Kral Date: Wed, 2 Oct 2019 16:06:07 +0200 Subject: [PATCH 5/5] Async interceptors cleared from ThreadLocal Signed-off-by: David Kral --- .../jersey/microprofile/restclient/ExecutorServiceWrapper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java index ecc01e21a7..479b1fb038 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java @@ -112,6 +112,7 @@ public void execute(Runnable command) { private static Callable wrap(Callable task) { List asyncInvocationInterceptors = asyncInterceptors.get(); + asyncInterceptors.remove(); return () -> { applyContextOnInterceptors(asyncInvocationInterceptors); return task.call(); @@ -120,6 +121,7 @@ private static Callable wrap(Callable task) { private static Runnable wrap(Runnable task) { List asyncInvocationInterceptors = asyncInterceptors.get(); + asyncInterceptors.remove(); return () -> { applyContextOnInterceptors(asyncInvocationInterceptors); task.run();