Skip to content

Commit 1070e3b

Browse files
manovotngeoand
authored andcommitted
Make rest-client invocation context implement ArcInvocationContext
1 parent 1e3a64b commit 1070e3b

File tree

2 files changed

+214
-10
lines changed

2 files changed

+214
-10
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package io.quarkus.restclient.runtime;
2+
3+
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.Constructor;
5+
import java.lang.reflect.InvocationTargetException;
6+
import java.lang.reflect.Method;
7+
import java.util.ArrayList;
8+
import java.util.Collections;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.Set;
13+
import java.util.concurrent.CompletionException;
14+
15+
import jakarta.enterprise.inject.spi.InterceptionType;
16+
import jakarta.enterprise.inject.spi.Interceptor;
17+
import jakarta.interceptor.InvocationContext;
18+
import jakarta.ws.rs.client.ResponseProcessingException;
19+
20+
import org.jboss.resteasy.microprofile.client.ExceptionMapping;
21+
22+
import io.quarkus.arc.ArcInvocationContext;
23+
24+
/**
25+
* A Quarkus copy of {@link org.jboss.resteasy.microprofile.client.InvocationContextImpl} which makes it implement
26+
* {@link ArcInvocationContext} instead so that it's compatible with Quarkus interceptors.
27+
*/
28+
public class QuarkusInvocationContextImpl implements ArcInvocationContext {
29+
30+
private final Object target;
31+
32+
private final Method method;
33+
34+
private Object[] args;
35+
36+
private final int position;
37+
38+
private final Map<String, Object> contextData;
39+
40+
private final List<QuarkusInvocationContextImpl.InterceptorInvocation> chain;
41+
42+
private final Set<Annotation> interceptorBindings;
43+
44+
public QuarkusInvocationContextImpl(final Object target, final Method method, final Object[] args,
45+
final List<QuarkusInvocationContextImpl.InterceptorInvocation> chain, Set<Annotation> interceptorBindings) {
46+
this(target, method, args, chain, 0, interceptorBindings);
47+
}
48+
49+
private QuarkusInvocationContextImpl(final Object target, final Method method, final Object[] args,
50+
final List<QuarkusInvocationContextImpl.InterceptorInvocation> chain, final int position,
51+
Set<Annotation> interceptorBindings) {
52+
this.target = target;
53+
this.method = method;
54+
this.args = args;
55+
this.interceptorBindings = interceptorBindings == null ? Collections.emptySet() : interceptorBindings;
56+
this.contextData = new HashMap<>();
57+
// put in bindings under Arc's specific key
58+
this.contextData.put(ArcInvocationContext.KEY_INTERCEPTOR_BINDINGS, interceptorBindings);
59+
this.position = position;
60+
this.chain = chain;
61+
}
62+
63+
boolean hasNextInterceptor() {
64+
return position < chain.size();
65+
}
66+
67+
protected Object invokeNext() throws Exception {
68+
return chain.get(position).invoke(nextContext());
69+
}
70+
71+
private InvocationContext nextContext() {
72+
return new QuarkusInvocationContextImpl(target, method, args, chain, position + 1, interceptorBindings);
73+
}
74+
75+
protected Object interceptorChainCompleted() throws Exception {
76+
try {
77+
return method.invoke(target, args);
78+
} catch (InvocationTargetException e) {
79+
Throwable cause = e.getCause();
80+
if (cause instanceof CompletionException) {
81+
cause = cause.getCause();
82+
}
83+
if (cause instanceof ExceptionMapping.HandlerException) {
84+
((ExceptionMapping.HandlerException) cause).mapException(method);
85+
}
86+
if (cause instanceof ResponseProcessingException) {
87+
ResponseProcessingException rpe = (ResponseProcessingException) cause;
88+
// Note that the default client engine leverages a single connection
89+
// MP FT: we need to close the response otherwise we would not be able to retry if the method returns jakarta.ws.rs.core.Response
90+
rpe.getResponse().close();
91+
cause = rpe.getCause();
92+
if (cause instanceof RuntimeException) {
93+
throw (RuntimeException) cause;
94+
}
95+
}
96+
throw e;
97+
}
98+
}
99+
100+
@Override
101+
public Object proceed() throws Exception {
102+
try {
103+
if (hasNextInterceptor()) {
104+
return invokeNext();
105+
} else {
106+
return interceptorChainCompleted();
107+
}
108+
} catch (InvocationTargetException e) {
109+
Throwable cause = e.getCause();
110+
if (cause instanceof Error) {
111+
throw (Error) cause;
112+
}
113+
if (cause instanceof Exception) {
114+
throw (Exception) cause;
115+
}
116+
throw new RuntimeException(cause);
117+
}
118+
}
119+
120+
@Override
121+
public Object getTarget() {
122+
return target;
123+
}
124+
125+
@Override
126+
public Method getMethod() {
127+
return method;
128+
}
129+
130+
@Override
131+
public Constructor<?> getConstructor() {
132+
return null;
133+
}
134+
135+
@Override
136+
public Object[] getParameters() throws IllegalStateException {
137+
return args;
138+
}
139+
140+
@Override
141+
public void setParameters(Object[] params) throws IllegalStateException, IllegalArgumentException {
142+
this.args = params;
143+
}
144+
145+
@Override
146+
public Map<String, Object> getContextData() {
147+
return contextData;
148+
}
149+
150+
@Override
151+
public Object getTimer() {
152+
return null;
153+
}
154+
155+
@Override
156+
public Set<Annotation> getInterceptorBindings() {
157+
return interceptorBindings;
158+
}
159+
160+
@Override
161+
public <T extends Annotation> T findIterceptorBinding(Class<T> annotationType) {
162+
for (Annotation annotation : getInterceptorBindings()) {
163+
if (annotation.annotationType().equals(annotationType)) {
164+
return (T) annotation;
165+
}
166+
}
167+
return null;
168+
}
169+
170+
@Override
171+
public <T extends Annotation> List<T> findIterceptorBindings(Class<T> annotationType) {
172+
List<T> found = new ArrayList<>();
173+
for (Annotation annotation : getInterceptorBindings()) {
174+
if (annotation.annotationType().equals(annotationType)) {
175+
found.add((T) annotation);
176+
}
177+
}
178+
return found;
179+
}
180+
181+
public static class InterceptorInvocation {
182+
183+
@SuppressWarnings("rawtypes")
184+
private final Interceptor interceptor;
185+
186+
private final Object interceptorInstance;
187+
188+
public InterceptorInvocation(final Interceptor<?> interceptor, final Object interceptorInstance) {
189+
this.interceptor = interceptor;
190+
this.interceptorInstance = interceptorInstance;
191+
}
192+
193+
@SuppressWarnings("unchecked")
194+
Object invoke(InvocationContext ctx) throws Exception {
195+
return interceptor.intercept(InterceptionType.AROUND_INVOKE, interceptorInstance, ctx);
196+
}
197+
}
198+
}

extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/QuarkusProxyInvocationHandler.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.jboss.logging.Logger;
3131
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
3232
import org.jboss.resteasy.microprofile.client.ExceptionMapping;
33-
import org.jboss.resteasy.microprofile.client.InvocationContextImpl;
3433
import org.jboss.resteasy.microprofile.client.RestClientProxy;
3534
import org.jboss.resteasy.microprofile.client.header.ClientHeaderFillingException;
3635

@@ -52,7 +51,9 @@ public class QuarkusProxyInvocationHandler implements InvocationHandler {
5251

5352
private final Set<Object> providerInstances;
5453

55-
private final Map<Method, List<InvocationContextImpl.InterceptorInvocation>> interceptorChains;
54+
private final Map<Method, List<QuarkusInvocationContextImpl.InterceptorInvocation>> interceptorChains;
55+
56+
private final Map<Method, Set<Annotation>> interceptorBindingsMap;
5657

5758
private final ResteasyClient client;
5859

@@ -70,10 +71,13 @@ public QuarkusProxyInvocationHandler(final Class<?> restClientInterface,
7071
this.closed = new AtomicBoolean();
7172
if (beanManager != null) {
7273
this.creationalContext = beanManager.createCreationalContext(null);
73-
this.interceptorChains = initInterceptorChains(beanManager, creationalContext, restClientInterface);
74+
this.interceptorBindingsMap = new HashMap<>();
75+
this.interceptorChains = initInterceptorChains(beanManager, creationalContext, restClientInterface,
76+
interceptorBindingsMap);
7477
} else {
7578
this.creationalContext = null;
7679
this.interceptorChains = Collections.emptyMap();
80+
this.interceptorBindingsMap = Collections.emptyMap();
7781
}
7882
}
7983

@@ -152,10 +156,10 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
152156
args = argsReplacement;
153157
}
154158

155-
List<InvocationContextImpl.InterceptorInvocation> chain = interceptorChains.get(method);
159+
List<QuarkusInvocationContextImpl.InterceptorInvocation> chain = interceptorChains.get(method);
156160
if (chain != null) {
157161
// Invoke business method interceptors
158-
return new InvocationContextImpl(target, method, args, chain).proceed();
162+
return new QuarkusInvocationContextImpl(target, method, args, chain, interceptorBindingsMap.get(method)).proceed();
159163
} else {
160164
try {
161165
return method.invoke(target, args);
@@ -245,10 +249,11 @@ private static BeanManager getBeanManager(Class<?> restClientInterface) {
245249
}
246250
}
247251

248-
private static Map<Method, List<InvocationContextImpl.InterceptorInvocation>> initInterceptorChains(
249-
BeanManager beanManager, CreationalContext<?> creationalContext, Class<?> restClientInterface) {
252+
private static Map<Method, List<QuarkusInvocationContextImpl.InterceptorInvocation>> initInterceptorChains(
253+
BeanManager beanManager, CreationalContext<?> creationalContext, Class<?> restClientInterface,
254+
Map<Method, Set<Annotation>> interceptorBindingsMap) {
250255

251-
Map<Method, List<InvocationContextImpl.InterceptorInvocation>> chains = new HashMap<>();
256+
Map<Method, List<QuarkusInvocationContextImpl.InterceptorInvocation>> chains = new HashMap<>();
252257
// Interceptor as a key in a map is not entirely correct (custom interceptors) but should work in most cases
253258
Map<Interceptor<?>, Object> interceptorInstances = new HashMap<>();
254259

@@ -267,12 +272,13 @@ private static Map<Method, List<InvocationContextImpl.InterceptorInvocation>> in
267272
List<Interceptor<?>> interceptors = beanManager.resolveInterceptors(InterceptionType.AROUND_INVOKE,
268273
interceptorBindings);
269274
if (!interceptors.isEmpty()) {
270-
List<InvocationContextImpl.InterceptorInvocation> chain = new ArrayList<>();
275+
List<QuarkusInvocationContextImpl.InterceptorInvocation> chain = new ArrayList<>();
271276
for (Interceptor<?> interceptor : interceptors) {
272-
chain.add(new InvocationContextImpl.InterceptorInvocation(interceptor,
277+
chain.add(new QuarkusInvocationContextImpl.InterceptorInvocation(interceptor,
273278
interceptorInstances.computeIfAbsent(interceptor,
274279
i -> beanManager.getReference(i, i.getBeanClass(), creationalContext))));
275280
}
281+
interceptorBindingsMap.put(method, Set.of(interceptorBindings));
276282
chains.put(method, chain);
277283
}
278284
}

0 commit comments

Comments
 (0)