AsyncInvocationinverceptors not properly created for each request (#4272)

* Fixes #4264 - AsyncInvocationinverceptors not properly created for each request

Signed-off-by: David Kral <david.k.kral@oracle.com>
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 54599e8..479b1fb 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 {
 
-    private final ExecutorService wrapped;
-    private final List<AsyncInvocationInterceptor> asyncInterceptors;
+    static final ThreadLocal<List<AsyncInvocationInterceptor>> asyncInterceptors = new ThreadLocal<>();
 
-    ExecutorServiceWrapper(ExecutorService wrapped,
-                           List<AsyncInvocationInterceptor> asyncInterceptors) {
+    private final ExecutorService wrapped;
+
+    ExecutorServiceWrapper(ExecutorService wrapped) {
         this.wrapped = wrapped;
-        this.asyncInterceptors = asyncInterceptors;
     }
 
     @Override
@@ -111,24 +110,36 @@
         wrapped.execute(wrap(command));
     }
 
-    private <T> Callable<T> wrap(Callable<T> task) {
+    private static <T> Callable<T> wrap(Callable<T> task) {
+        List<AsyncInvocationInterceptor> asyncInvocationInterceptors = asyncInterceptors.get();
+        asyncInterceptors.remove();
         return () -> {
-            asyncInterceptors.forEach(AsyncInvocationInterceptor::applyContext);
+            applyContextOnInterceptors(asyncInvocationInterceptors);
             return task.call();
         };
     }
 
-    private Runnable wrap(Runnable task) {
+    private static Runnable wrap(Runnable task) {
+        List<AsyncInvocationInterceptor> asyncInvocationInterceptors = asyncInterceptors.get();
+        asyncInterceptors.remove();
         return () -> {
-            asyncInterceptors.forEach(AsyncInvocationInterceptor::applyContext);
+            applyContextOnInterceptors(asyncInvocationInterceptors);
             task.run();
         };
     }
 
+    private static void applyContextOnInterceptors(List<AsyncInvocationInterceptor> 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 <T> Collection<? extends Callable<T>> wrap(Collection<? extends Callable<T>> tasks) {
+    private static <T> Collection<? extends Callable<T>> wrap(Collection<? extends Callable<T>> tasks) {
         return tasks.stream()
-                .map(this::wrap)
+                .map(ExecutorServiceWrapper::wrap)
                 .collect(Collectors.toList());
     }
 }
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 1a045ee..6bd0e95 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 @@
     private final CreationalContext<?> creationalContext;
 
     private final List<ClientHeaderParamModel> clientHeaders;
-    private final List<AsyncInvocationInterceptor> asyncInterceptors;
+    private final List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories;
     private final Set<ResponseExceptionMapper> responseExceptionMappers;
     private final Set<ParamConverterProvider> paramConverterProviders;
     private final Set<Annotation> interceptorAnnotations;
@@ -73,23 +74,23 @@
     /**
      * 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<ResponseExceptionMapper> responseExceptionMappers,
                                Set<ParamConverterProvider> paramConverterProviders,
-                               List<AsyncInvocationInterceptor> asyncInterceptors,
+                               List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories,
                                InjectionManager injectionManager,
                                BeanManager beanManager) {
         return new Builder(restClientClass,
                            responseExceptionMappers,
                            paramConverterProviders,
-                           asyncInterceptors,
+                           asyncInterceptorFactories,
                            injectionManager,
                            beanManager).build();
     }
@@ -106,7 +107,7 @@
         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 @@
      *
      * @return registered async interceptors
      */
-    List<AsyncInvocationInterceptor> getAsyncInterceptors() {
-        return asyncInterceptors;
+    List<AsyncInvocationInterceptorFactory> getAsyncInterceptorFactories() {
+        return asyncInterceptorFactories;
     }
 
     /**
@@ -251,7 +252,7 @@
         private ClientHeadersFactory clientHeadersFactory;
         private CreationalContext<?> creationalContext;
         private List<ClientHeaderParamModel> clientHeaders;
-        private List<AsyncInvocationInterceptor> asyncInterceptors;
+        private List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories;
         private Set<ResponseExceptionMapper> responseExceptionMappers;
         private Set<ParamConverterProvider> paramConverterProviders;
         private Set<Annotation> interceptorAnnotations;
@@ -259,14 +260,14 @@
         private Builder(Class<?> restClientClass,
                         Set<ResponseExceptionMapper> responseExceptionMappers,
                         Set<ParamConverterProvider> paramConverterProviders,
-                        List<AsyncInvocationInterceptor> asyncInterceptors,
+                        List<AsyncInvocationInterceptorFactory> 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 4e738a0..d106f0c 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 @@
             subResourceModel = RestClientModel.from(returnType.getRawType(),
                                                     interfaceModel.getResponseExceptionMappers(),
                                                     interfaceModel.getParamConverterProviders(),
-                                                    interfaceModel.getAsyncInterceptors(),
+                                                    interfaceModel.getAsyncInterceptorFactories(),
                                                     interfaceModel.getInjectionManager(),
                                                     interfaceModel.getBeanManager());
         } else {
@@ -243,6 +244,14 @@
                                                Object entity,
                                                Method method,
                                                MultivaluedMap<String, Object> customHeaders) {
+
+        //AsyncInterceptors initialization
+        List<AsyncInvocationInterceptor> asyncInterceptors = interfaceModel.getAsyncInterceptorFactories().stream()
+                .map(AsyncInvocationInterceptorFactory::newInterceptor)
+                .collect(Collectors.toList());
+        asyncInterceptors.forEach(AsyncInvocationInterceptor::prepareContext);
+        ExecutorServiceWrapper.asyncInterceptors.set(asyncInterceptors);
+
         CompletableFuture<Object> result = new CompletableFuture<>();
         Future<Response> theFuture;
         if (entity != null
@@ -255,7 +264,7 @@
 
         CompletableFuture<Response> completableFuture = (CompletableFuture<Response>) 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 @@
                 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 ded8ec5..e963850 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 @@
 
     private final Set<ResponseExceptionMapper> responseExceptionMappers;
     private final Set<ParamConverterProvider> paramConverterProviders;
-    private final List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories;
+    private final List<AsyncInvocationInterceptorFactoryPriorityWrapper> asyncInterceptorFactories;
     private final Config config;
     private final ConfigWrapper configWrapper;
     private URI uri;
@@ -153,14 +153,10 @@
 
         //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));
 
-        //AsyncInterceptors initialization
-        List<AsyncInvocationInterceptor> 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 +183,7 @@
         RestClientModel restClientModel = RestClientModel.from(interfaceClass,
                                                                responseExceptionMappers,
                                                                paramConverterProviders,
-                                                               asyncInterceptors,
+                                                               new ArrayList<>(asyncInterceptorFactories),
                                                                injectionManagerExposer.injectionManager,
                                                                CdiUtil.getBeanManager());
 
@@ -338,11 +334,11 @@
     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;
     }
@@ -384,7 +380,7 @@
                 || AsyncInvocationInterceptorFactory.class.isAssignableFrom(providerClass);
     }
 
-    private void registerCustomProvider(Object instance, int priority) {
+    private void registerCustomProvider(Object instance, Integer priority) {
         if (!isSupportedCustomProvider(instance.getClass())) {
             return;
         }
@@ -399,7 +395,9 @@
             paramConverterProviders.add((ParamConverterProvider) instance);
         }
         if (instance instanceof AsyncInvocationInterceptorFactory) {
-            asyncInterceptorFactories.add((AsyncInvocationInterceptorFactory) instance);
+            asyncInterceptorFactories
+                    .add(new AsyncInvocationInterceptorFactoryPriorityWrapper((AsyncInvocationInterceptorFactory) instance,
+                                                                              priority));
         }
     }
 
@@ -417,4 +415,30 @@
         }
     }
 
+    private static class AsyncInvocationInterceptorFactoryPriorityWrapper
+            implements AsyncInvocationInterceptorFactory {
+
+        private AsyncInvocationInterceptorFactory factory;
+        private Integer priority;
+
+        AsyncInvocationInterceptorFactoryPriorityWrapper(AsyncInvocationInterceptorFactory factory, Integer priority) {
+            this.factory = factory;
+            this.priority = priority;
+        }
+
+        @Override
+        public AsyncInvocationInterceptor newInterceptor() {
+            return factory.newInterceptor();
+        }
+
+        Integer getPriority() {
+            if (priority == null) {
+                priority = Optional.ofNullable(factory.getClass().getAnnotation(Priority.class))
+                        .map(Priority::value)
+                        .orElse(Priorities.USER);
+            }
+            return priority;
+        }
+    }
+
 }
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 4e81ca3..e0ff634 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 @@
     static RestClientModel from(Class<?> restClientClass,
                                 Set<ResponseExceptionMapper> responseExceptionMappers,
                                 Set<ParamConverterProvider> paramConverterProviders,
-                                List<AsyncInvocationInterceptor> asyncInterceptors,
+                                List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories,
                                 InjectionManager injectionManager,
                                 BeanManager beanManager) {
         InterfaceModel interfaceModel = InterfaceModel.from(restClientClass,
                                                             responseExceptionMappers,
                                                             paramConverterProviders,
-                                                            asyncInterceptors,
+                                                            asyncInterceptorFactories,
                                                             injectionManager,
                                                             beanManager);
         return new Builder()