Skip to content

Commit 959b4e7

Browse files
authoredOct 3, 2019
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>
1 parent 1c3b8d5 commit 959b4e7

File tree

5 files changed

+88
-43
lines changed

5 files changed

+88
-43
lines changed
 

‎ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ExecutorServiceWrapper.java

+21-10
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,12 @@
3535
*/
3636
class ExecutorServiceWrapper implements ExecutorService {
3737

38+
static final ThreadLocal<List<AsyncInvocationInterceptor>> asyncInterceptors = new ThreadLocal<>();
39+
3840
private final ExecutorService wrapped;
39-
private final List<AsyncInvocationInterceptor> asyncInterceptors;
4041

41-
ExecutorServiceWrapper(ExecutorService wrapped,
42-
List<AsyncInvocationInterceptor> asyncInterceptors) {
42+
ExecutorServiceWrapper(ExecutorService wrapped) {
4343
this.wrapped = wrapped;
44-
this.asyncInterceptors = asyncInterceptors;
4544
}
4645

4746
@Override
@@ -111,24 +110,36 @@ public void execute(Runnable command) {
111110
wrapped.execute(wrap(command));
112111
}
113112

114-
private <T> Callable<T> wrap(Callable<T> task) {
113+
private static <T> Callable<T> wrap(Callable<T> task) {
114+
List<AsyncInvocationInterceptor> asyncInvocationInterceptors = asyncInterceptors.get();
115+
asyncInterceptors.remove();
115116
return () -> {
116-
asyncInterceptors.forEach(AsyncInvocationInterceptor::applyContext);
117+
applyContextOnInterceptors(asyncInvocationInterceptors);
117118
return task.call();
118119
};
119120
}
120121

121-
private Runnable wrap(Runnable task) {
122+
private static Runnable wrap(Runnable task) {
123+
List<AsyncInvocationInterceptor> asyncInvocationInterceptors = asyncInterceptors.get();
124+
asyncInterceptors.remove();
122125
return () -> {
123-
asyncInterceptors.forEach(AsyncInvocationInterceptor::applyContext);
126+
applyContextOnInterceptors(asyncInvocationInterceptors);
124127
task.run();
125128
};
126129
}
127130

131+
private static void applyContextOnInterceptors(List<AsyncInvocationInterceptor> asyncInvocationInterceptors) {
132+
if (asyncInvocationInterceptors != null) {
133+
//applyContext methods need to be called in reverse ordering of priority
134+
for (int i = asyncInvocationInterceptors.size(); i-- > 0; ) {
135+
asyncInvocationInterceptors.get(i).applyContext();
136+
}
137+
}
138+
}
128139

129-
private <T> Collection<? extends Callable<T>> wrap(Collection<? extends Callable<T>> tasks) {
140+
private static <T> Collection<? extends Callable<T>> wrap(Collection<? extends Callable<T>> tasks) {
130141
return tasks.stream()
131-
.map(this::wrap)
142+
.map(ExecutorServiceWrapper::wrap)
132143
.collect(Collectors.toList());
133144
}
134145
}

‎ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/InterfaceModel.java

+14-13
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
3939
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
4040
import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor;
41+
import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory;
4142
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
4243
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
4344
import org.glassfish.jersey.client.inject.ParameterUpdater;
@@ -64,7 +65,7 @@ class InterfaceModel {
6465
private final CreationalContext<?> creationalContext;
6566

6667
private final List<ClientHeaderParamModel> clientHeaders;
67-
private final List<AsyncInvocationInterceptor> asyncInterceptors;
68+
private final List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories;
6869
private final Set<ResponseExceptionMapper> responseExceptionMappers;
6970
private final Set<ParamConverterProvider> paramConverterProviders;
7071
private final Set<Annotation> interceptorAnnotations;
@@ -73,23 +74,23 @@ class InterfaceModel {
7374
/**
7475
* Creates new model based on interface class. Interface is parsed according to specific annotations.
7576
*
76-
* @param restClientClass interface class
77-
* @param responseExceptionMappers registered exception mappers
78-
* @param paramConverterProviders registered parameter providers
79-
* @param asyncInterceptors async interceptors
77+
* @param restClientClass interface class
78+
* @param responseExceptionMappers registered exception mappers
79+
* @param paramConverterProviders registered parameter providers
80+
* @param asyncInterceptorFactories async interceptor factories
8081
* @param injectionManager
8182
* @return new model instance
8283
*/
8384
static InterfaceModel from(Class<?> restClientClass,
8485
Set<ResponseExceptionMapper> responseExceptionMappers,
8586
Set<ParamConverterProvider> paramConverterProviders,
86-
List<AsyncInvocationInterceptor> asyncInterceptors,
87+
List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories,
8788
InjectionManager injectionManager,
8889
BeanManager beanManager) {
8990
return new Builder(restClientClass,
9091
responseExceptionMappers,
9192
paramConverterProviders,
92-
asyncInterceptors,
93+
asyncInterceptorFactories,
9394
injectionManager,
9495
beanManager).build();
9596
}
@@ -106,7 +107,7 @@ private InterfaceModel(Builder builder) {
106107
this.paramConverterProviders = builder.paramConverterProviders;
107108
this.interceptorAnnotations = builder.interceptorAnnotations;
108109
this.creationalContext = builder.creationalContext;
109-
this.asyncInterceptors = builder.asyncInterceptors;
110+
this.asyncInterceptorFactories = builder.asyncInterceptorFactories;
110111
this.beanManager = builder.beanManager;
111112
}
112113

@@ -169,8 +170,8 @@ List<ClientHeaderParamModel> getClientHeaders() {
169170
*
170171
* @return registered async interceptors
171172
*/
172-
List<AsyncInvocationInterceptor> getAsyncInterceptors() {
173-
return asyncInterceptors;
173+
List<AsyncInvocationInterceptorFactory> getAsyncInterceptorFactories() {
174+
return asyncInterceptorFactories;
174175
}
175176

176177
/**
@@ -251,22 +252,22 @@ private static class Builder {
251252
private ClientHeadersFactory clientHeadersFactory;
252253
private CreationalContext<?> creationalContext;
253254
private List<ClientHeaderParamModel> clientHeaders;
254-
private List<AsyncInvocationInterceptor> asyncInterceptors;
255+
private List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories;
255256
private Set<ResponseExceptionMapper> responseExceptionMappers;
256257
private Set<ParamConverterProvider> paramConverterProviders;
257258
private Set<Annotation> interceptorAnnotations;
258259

259260
private Builder(Class<?> restClientClass,
260261
Set<ResponseExceptionMapper> responseExceptionMappers,
261262
Set<ParamConverterProvider> paramConverterProviders,
262-
List<AsyncInvocationInterceptor> asyncInterceptors,
263+
List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories,
263264
InjectionManager injectionManager,
264265
BeanManager beanManager) {
265266
this.injectionManager = injectionManager;
266267
this.restClientClass = restClientClass;
267268
this.responseExceptionMappers = responseExceptionMappers;
268269
this.paramConverterProviders = paramConverterProviders;
269-
this.asyncInterceptors = asyncInterceptors;
270+
this.asyncInterceptorFactories = asyncInterceptorFactories;
270271
this.beanManager = beanManager;
271272
filterAllInterceptorAnnotations();
272273
}

‎ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/MethodModel.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
7070
import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
7171
import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor;
72+
import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory;
7273
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
7374

7475
/**
@@ -121,7 +122,7 @@ private MethodModel(Builder builder) {
121122
subResourceModel = RestClientModel.from(returnType.getRawType(),
122123
interfaceModel.getResponseExceptionMappers(),
123124
interfaceModel.getParamConverterProviders(),
124-
interfaceModel.getAsyncInterceptors(),
125+
interfaceModel.getAsyncInterceptorFactories(),
125126
interfaceModel.getInjectionManager(),
126127
interfaceModel.getBeanManager());
127128
} else {
@@ -243,6 +244,14 @@ private CompletableFuture asynchronousCall(Invocation.Builder builder,
243244
Object entity,
244245
Method method,
245246
MultivaluedMap<String, Object> customHeaders) {
247+
248+
//AsyncInterceptors initialization
249+
List<AsyncInvocationInterceptor> asyncInterceptors = interfaceModel.getAsyncInterceptorFactories().stream()
250+
.map(AsyncInvocationInterceptorFactory::newInterceptor)
251+
.collect(Collectors.toList());
252+
asyncInterceptors.forEach(AsyncInvocationInterceptor::prepareContext);
253+
ExecutorServiceWrapper.asyncInterceptors.set(asyncInterceptors);
254+
246255
CompletableFuture<Object> result = new CompletableFuture<>();
247256
Future<Response> theFuture;
248257
if (entity != null
@@ -255,7 +264,7 @@ private CompletableFuture asynchronousCall(Invocation.Builder builder,
255264

256265
CompletableFuture<Response> completableFuture = (CompletableFuture<Response>) theFuture;
257266
completableFuture.thenAccept(response -> {
258-
interfaceModel.getAsyncInterceptors().forEach(AsyncInvocationInterceptor::removeContext);
267+
asyncInterceptors.forEach(AsyncInvocationInterceptor::removeContext);
259268
try {
260269
evaluateResponse(response, method);
261270
if (returnType.getType().equals(Void.class)) {
@@ -269,7 +278,7 @@ private CompletableFuture asynchronousCall(Invocation.Builder builder,
269278
result.completeExceptionally(e);
270279
}
271280
}).exceptionally(throwable -> {
272-
interfaceModel.getAsyncInterceptors().forEach(AsyncInvocationInterceptor::removeContext);
281+
asyncInterceptors.forEach(AsyncInvocationInterceptor::removeContext);
273282
result.completeExceptionally(throwable);
274283
return null;
275284
});

‎ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java

+38-14
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.security.AccessController;
2626
import java.security.KeyStore;
2727
import java.util.ArrayList;
28+
import java.util.Comparator;
2829
import java.util.HashMap;
2930
import java.util.HashSet;
3031
import java.util.List;
@@ -36,7 +37,6 @@
3637
import java.util.concurrent.Executors;
3738
import java.util.concurrent.TimeUnit;
3839
import java.util.function.Supplier;
39-
import java.util.stream.Collectors;
4040

4141
import javax.annotation.Priority;
4242
import javax.net.ssl.HostnameVerifier;
@@ -81,7 +81,7 @@ class RestClientBuilderImpl implements RestClientBuilder {
8181

8282
private final Set<ResponseExceptionMapper> responseExceptionMappers;
8383
private final Set<ParamConverterProvider> paramConverterProviders;
84-
private final List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories;
84+
private final List<AsyncInvocationInterceptorFactoryPriorityWrapper> asyncInterceptorFactories;
8585
private final Config config;
8686
private final ConfigWrapper configWrapper;
8787
private URI uri;
@@ -153,14 +153,10 @@ public <T> T build(Class<T> interfaceClass) throws IllegalStateException, RestCl
153153

154154
//We need to check first if default exception mapper was not disabled by property on builder.
155155
registerExceptionMapper();
156+
//sort all AsyncInvocationInterceptorFactory by priority
157+
asyncInterceptorFactories.sort(Comparator.comparingInt(AsyncInvocationInterceptorFactoryPriorityWrapper::getPriority));
156158

157-
//AsyncInterceptors initialization
158-
List<AsyncInvocationInterceptor> asyncInterceptors = asyncInterceptorFactories.stream()
159-
.map(AsyncInvocationInterceptorFactory::newInterceptor)
160-
.collect(Collectors.toList());
161-
asyncInterceptors.forEach(AsyncInvocationInterceptor::prepareContext);
162-
163-
clientBuilder.executorService(new ExecutorServiceWrapper(executorService.get(), asyncInterceptors));
159+
clientBuilder.executorService(new ExecutorServiceWrapper(executorService.get()));
164160

165161
if (null != sslContext) {
166162
clientBuilder.sslContext(sslContext);
@@ -187,7 +183,7 @@ public <T> T build(Class<T> interfaceClass) throws IllegalStateException, RestCl
187183
RestClientModel restClientModel = RestClientModel.from(interfaceClass,
188184
responseExceptionMappers,
189185
paramConverterProviders,
190-
asyncInterceptors,
186+
new ArrayList<>(asyncInterceptorFactories),
191187
injectionManagerExposer.injectionManager,
192188
CdiUtil.getBeanManager());
193189

@@ -338,11 +334,11 @@ public RestClientBuilder register(Class<?> componentClass, Map<Class<?>, Integer
338334
public RestClientBuilder register(Object component) {
339335
if (component instanceof ResponseExceptionMapper) {
340336
ResponseExceptionMapper mapper = (ResponseExceptionMapper) component;
341-
registerCustomProvider(component, -1);
337+
registerCustomProvider(component, mapper.getPriority());
342338
clientBuilder.register(mapper, mapper.getPriority());
343339
} else {
344340
clientBuilder.register(component);
345-
registerCustomProvider(component, -1);
341+
registerCustomProvider(component, null);
346342
}
347343
return this;
348344
}
@@ -384,7 +380,7 @@ private boolean isSupportedCustomProvider(Class<?> providerClass) {
384380
|| AsyncInvocationInterceptorFactory.class.isAssignableFrom(providerClass);
385381
}
386382

387-
private void registerCustomProvider(Object instance, int priority) {
383+
private void registerCustomProvider(Object instance, Integer priority) {
388384
if (!isSupportedCustomProvider(instance.getClass())) {
389385
return;
390386
}
@@ -399,7 +395,9 @@ private void registerCustomProvider(Object instance, int priority) {
399395
paramConverterProviders.add((ParamConverterProvider) instance);
400396
}
401397
if (instance instanceof AsyncInvocationInterceptorFactory) {
402-
asyncInterceptorFactories.add((AsyncInvocationInterceptorFactory) instance);
398+
asyncInterceptorFactories
399+
.add(new AsyncInvocationInterceptorFactoryPriorityWrapper((AsyncInvocationInterceptorFactory) instance,
400+
priority));
403401
}
404402
}
405403

@@ -417,4 +415,30 @@ public boolean configure(FeatureContext context) {
417415
}
418416
}
419417

418+
private static class AsyncInvocationInterceptorFactoryPriorityWrapper
419+
implements AsyncInvocationInterceptorFactory {
420+
421+
private AsyncInvocationInterceptorFactory factory;
422+
private Integer priority;
423+
424+
AsyncInvocationInterceptorFactoryPriorityWrapper(AsyncInvocationInterceptorFactory factory, Integer priority) {
425+
this.factory = factory;
426+
this.priority = priority;
427+
}
428+
429+
@Override
430+
public AsyncInvocationInterceptor newInterceptor() {
431+
return factory.newInterceptor();
432+
}
433+
434+
Integer getPriority() {
435+
if (priority == null) {
436+
priority = Optional.ofNullable(factory.getClass().getAnnotation(Priority.class))
437+
.map(Priority::value)
438+
.orElse(Priorities.USER);
439+
}
440+
return priority;
441+
}
442+
}
443+
420444
}

‎ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientModel.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import javax.ws.rs.client.WebTarget;
2929
import javax.ws.rs.ext.ParamConverterProvider;
3030

31-
import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptor;
31+
import org.eclipse.microprofile.rest.client.ext.AsyncInvocationInterceptorFactory;
3232
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
3333
import org.glassfish.jersey.internal.inject.InjectionManager;
3434

@@ -56,13 +56,13 @@ class RestClientModel {
5656
static RestClientModel from(Class<?> restClientClass,
5757
Set<ResponseExceptionMapper> responseExceptionMappers,
5858
Set<ParamConverterProvider> paramConverterProviders,
59-
List<AsyncInvocationInterceptor> asyncInterceptors,
59+
List<AsyncInvocationInterceptorFactory> asyncInterceptorFactories,
6060
InjectionManager injectionManager,
6161
BeanManager beanManager) {
6262
InterfaceModel interfaceModel = InterfaceModel.from(restClientClass,
6363
responseExceptionMappers,
6464
paramConverterProviders,
65-
asyncInterceptors,
65+
asyncInterceptorFactories,
6666
injectionManager,
6767
beanManager);
6868
return new Builder()

0 commit comments

Comments
 (0)