diff --git a/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java b/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java
index 197d4948..edae5a3a 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java
@@ -24,6 +24,7 @@
import io.github.belgif.rest.problem.api.Input;
import io.github.belgif.rest.problem.api.Problem;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemSupport;
+import io.github.belgif.rest.problem.ee.resteasy.client.ResteasyProblemSupport;
import io.github.belgif.rest.problem.i18n.I18N;
import io.github.belgif.rest.problem.it.model.Bean;
import io.github.belgif.rest.problem.it.model.ChildModel;
@@ -48,8 +49,8 @@ public class FrontendImpl implements Frontend {
private final jakarta.ws.rs.client.Client resteasyClient =
ProblemSupport.enable(new ResteasyClientBuilderImpl().build());
- private final Backend resteasyProxyClient = ProblemSupport.enable(
- new ResteasyClientBuilderImpl().build().target(BASE_URI).proxy(Backend.class));
+ private final Backend resteasyProxyClient = ResteasyProblemSupport.proxy(
+ new ResteasyClientBuilderImpl().build().target(BASE_URI), Backend.class);
@EJB
private EJBService ejb;
diff --git a/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java b/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java
index a35e777d..97e45ed4 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/FrontendImpl.java
@@ -24,6 +24,7 @@
import io.github.belgif.rest.problem.api.Input;
import io.github.belgif.rest.problem.api.Problem;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemSupport;
+import io.github.belgif.rest.problem.ee.resteasy.client.ResteasyProblemSupport;
import io.github.belgif.rest.problem.i18n.I18N;
import io.github.belgif.rest.problem.it.model.Bean;
import io.github.belgif.rest.problem.it.model.ChildModel;
@@ -47,8 +48,8 @@ public class FrontendImpl implements Frontend {
private final javax.ws.rs.client.Client resteasyClient = ProblemSupport.enable(new ResteasyClientBuilder().build());
- private final Backend resteasyProxyClient = ProblemSupport.enable(
- new ResteasyClientBuilder().build().target(BASE_URI).proxy(Backend.class));
+ private final Backend resteasyProxyClient =
+ ResteasyProblemSupport.proxy(new ResteasyClientBuilder().build().target(BASE_URI), Backend.class);
@EJB
private EJBService ejb;
diff --git a/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemClientResponseFilter.java b/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemClientResponseFilter.java
index e0714f89..ad38ea4c 100644
--- a/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemClientResponseFilter.java
+++ b/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemClientResponseFilter.java
@@ -7,7 +7,6 @@
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.core.MediaType;
-import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,7 +24,6 @@
* @see ClientResponseFilter
* @see ProblemWrapper
*/
-@Provider
public class ProblemClientResponseFilter implements ClientResponseFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(ProblemClientResponseFilter.class);
diff --git a/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupport.java b/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupport.java
index 55644071..ba16fd29 100644
--- a/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupport.java
+++ b/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupport.java
@@ -41,23 +41,6 @@ public static Client enable(Client client) {
return createProxy(Client.class, new ClientInvocationHandler(client));
}
- /**
- * Enable problem support on the given client (e.g. RESTEasy proxy client).
- *
- *
- * This causes the client to throw Problem exceptions instead of ProblemWrapper exceptions.
- *
- *
- * @param client the client
- * @param the client type
- * @return the problem-enabled client
- */
- @SuppressWarnings("unchecked")
- public static T enable(T client) {
- return (T) Proxy.newProxyInstance(ProblemSupport.class.getClassLoader(),
- client.getClass().getInterfaces(), new ProxyInvocationHandler(client));
- }
-
/**
* JDK Dynamic Proxy InvocationHandler for JAX-RS Client.
*/
@@ -109,31 +92,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
}
}
- /**
- * JDK Dynamic Proxy InvocationHandler for proxy clients.
- */
- static final class ProxyInvocationHandler implements InvocationHandler {
-
- private final Object target;
-
- ProxyInvocationHandler(Object target) {
- this.target = target;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- return method.invoke(target, args);
- } catch (InvocationTargetException e) {
- if (e.getTargetException() instanceof ProblemWrapper) {
- throw ((ProblemWrapper) e.getTargetException()).getProblem();
- }
- throw e.getTargetException();
- }
- }
-
- }
-
@SuppressWarnings("unchecked")
private static T createProxy(Class intf, InvocationHandler invocationHandler) {
return (T) Proxy.newProxyInstance(ProblemSupport.class.getClassLoader(),
diff --git a/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/resteasy/client/ResteasyProblemSupport.java b/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/resteasy/client/ResteasyProblemSupport.java
new file mode 100644
index 00000000..4a8ff850
--- /dev/null
+++ b/belgif-rest-problem-java-ee-client/src/main/java/io/github/belgif/rest/problem/ee/resteasy/client/ResteasyProblemSupport.java
@@ -0,0 +1,66 @@
+package io.github.belgif.rest.problem.ee.resteasy.client;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemClientResponseFilter;
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemSupport;
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemWrapper;
+
+/**
+ * Utility class for enabling problem support on RESTEasy Clients.
+ */
+public class ResteasyProblemSupport {
+
+ private ResteasyProblemSupport() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ /**
+ * Create a problem-enabled RESTEasy proxy client.
+ *
+ * @param target the ResteasyWebTarget
+ * @param proxyInterface the service interface
+ * @return the problem-enabled RESTEasy proxy client
+ * @param the service interface type
+ */
+ @SuppressWarnings("unchecked")
+ public static T proxy(ResteasyWebTarget target, Class proxyInterface) {
+ if (!target.getConfiguration().isRegistered(ProblemClientResponseFilter.class)) {
+ target.register(ProblemClientResponseFilter.class);
+ }
+ T client = target.proxy(proxyInterface);
+ return (T) Proxy.newProxyInstance(ProblemSupport.class.getClassLoader(),
+ client.getClass().getInterfaces(), new ProxyInvocationHandler(client));
+ }
+
+ /**
+ * JDK Dynamic Proxy InvocationHandler for RESTEasy proxy clients.
+ */
+ static final class ProxyInvocationHandler implements InvocationHandler {
+
+ private final Object target;
+
+ ProxyInvocationHandler(Object target) {
+ this.target = target;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(target, args);
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof ProblemWrapper) {
+ throw ((ProblemWrapper) e.getTargetException()).getProblem();
+ }
+ throw e.getTargetException();
+ }
+ }
+
+ }
+
+}
diff --git a/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupportTest.java b/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupportTest.java
index 28e2d38c..d756579e 100644
--- a/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupportTest.java
+++ b/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/jaxrs/client/ProblemSupportTest.java
@@ -20,10 +20,8 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-import io.github.belgif.rest.problem.BadGatewayProblem;
import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemSupport.ClientInvocationHandler;
-import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemSupport.ProxyInvocationHandler;
@ExtendWith(MockitoExtension.class)
class ProblemSupportTest {
@@ -34,12 +32,8 @@ class ProblemSupportTest {
@Mock(strictness = Mock.Strictness.LENIENT)
private Configuration configuration;
- interface Service {
- String test();
- }
-
@BeforeEach
- public void mockConfiguration() {
+ void mockConfiguration() {
when(client.getConfiguration()).thenReturn(configuration);
when(configuration.isRegistered(ProblemClientResponseFilter.class)).thenReturn(true);
}
@@ -217,42 +211,6 @@ void exceptionCreatingProxiedReturnType() {
.withMessage("oops");
}
- @Test
- void unwrapProblemWrapperInProxyClient() {
- Service service = mock(Service.class);
- Service result = ProblemSupport.enable(service);
- assertThat(Proxy.isProxyClass(result.getClass())).isTrue();
- assertThat(Proxy.getInvocationHandler(result)).isInstanceOf(ProxyInvocationHandler.class);
-
- doThrow(new ProblemWrapper(new BadGatewayProblem())).when(service).test();
-
- assertThatExceptionOfType(BadGatewayProblem.class).isThrownBy(result::test);
- }
-
- @Test
- void normalResponseFromProxyClient() {
- Service service = mock(Service.class);
- Service result = ProblemSupport.enable(service);
- assertThat(Proxy.isProxyClass(result.getClass())).isTrue();
- assertThat(Proxy.getInvocationHandler(result)).isInstanceOf(ProxyInvocationHandler.class);
-
- when(service.test()).thenReturn("OK");
-
- assertThat(result.test()).isEqualTo("OK");
- }
-
- @Test
- void otherExceptionInProxyClient() {
- Service service = mock(Service.class);
- Service result = ProblemSupport.enable(service);
- assertThat(Proxy.isProxyClass(result.getClass())).isTrue();
- assertThat(Proxy.getInvocationHandler(result)).isInstanceOf(ProxyInvocationHandler.class);
-
- doThrow(new RuntimeException("other")).when(service).test();
-
- assertThatRuntimeException().isThrownBy(result::test).withMessage("other");
- }
-
@Test
void configurable() {
Client result = ProblemSupport.enable(client);
diff --git a/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/resteasy/client/ResteasyProblemSupportTest.java b/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/resteasy/client/ResteasyProblemSupportTest.java
new file mode 100644
index 00000000..1c7a84fa
--- /dev/null
+++ b/belgif-rest-problem-java-ee-client/src/test/java/io/github/belgif/rest/problem/ee/resteasy/client/ResteasyProblemSupportTest.java
@@ -0,0 +1,86 @@
+package io.github.belgif.rest.problem.ee.resteasy.client;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.lang.reflect.Proxy;
+
+import javax.ws.rs.core.Configuration;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import io.github.belgif.rest.problem.BadGatewayProblem;
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemClientResponseFilter;
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemWrapper;
+
+@ExtendWith(MockitoExtension.class)
+class ResteasyProblemSupportTest {
+
+ @Mock
+ private ResteasyWebTarget target;
+
+ @Mock
+ private Configuration configuration;
+
+ @Mock
+ private Service serviceMock;
+
+ interface Service {
+ String test();
+ }
+
+ @BeforeEach
+ void mockConfiguration() {
+ when(target.getConfiguration()).thenReturn(configuration);
+ when(configuration.isRegistered(ProblemClientResponseFilter.class)).thenReturn(true);
+ }
+
+ @Test
+ void proxy() {
+ when(configuration.isRegistered(ProblemClientResponseFilter.class)).thenReturn(false);
+ when(target.proxy(Service.class)).thenReturn(serviceMock);
+ Service service = ResteasyProblemSupport.proxy(target, Service.class);
+
+ assertThat(Proxy.isProxyClass(service.getClass())).isTrue();
+ assertThat(Proxy.getInvocationHandler(service))
+ .isInstanceOf(ResteasyProblemSupport.ProxyInvocationHandler.class);
+
+ verify(target).register(ProblemClientResponseFilter.class);
+ }
+
+ @Test
+ void normalResponseFromProxyClient() {
+ when(target.proxy(Service.class)).thenReturn(serviceMock);
+ Service service = ResteasyProblemSupport.proxy(target, Service.class);
+
+ when(serviceMock.test()).thenReturn("OK");
+
+ assertThat(service.test()).isEqualTo("OK");
+ }
+
+ @Test
+ void unwrapProblemWrapperInProxyClient() {
+ when(target.proxy(Service.class)).thenReturn(serviceMock);
+ Service service = ResteasyProblemSupport.proxy(target, Service.class);
+
+ doThrow(new ProblemWrapper(new BadGatewayProblem())).when(serviceMock).test();
+
+ assertThatExceptionOfType(BadGatewayProblem.class).isThrownBy(service::test);
+ }
+
+ @Test
+ void otherExceptionInProxyClient() {
+ when(target.proxy(Service.class)).thenReturn(serviceMock);
+ Service service = ResteasyProblemSupport.proxy(target, Service.class);
+
+ doThrow(new RuntimeException("other")).when(serviceMock).test();
+
+ assertThatRuntimeException().isThrownBy(service::test).withMessage("other");
+ }
+
+}
diff --git a/belgif-rest-problem-quarkus-client-deployment/src/main/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessor.java b/belgif-rest-problem-quarkus-client-deployment/src/main/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessor.java
index 24216978..8b65d220 100644
--- a/belgif-rest-problem-quarkus-client-deployment/src/main/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessor.java
+++ b/belgif-rest-problem-quarkus-client-deployment/src/main/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessor.java
@@ -12,6 +12,7 @@
import org.eclipse.microprofile.rest.client.spi.RestClientListener;
import io.github.belgif.rest.problem.ee.jaxrs.ProblemObjectMapperContextResolver;
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemClientResponseFilter;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemResponseExceptionMapper;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemRestClientListener;
import io.quarkus.deployment.annotations.BuildStep;
@@ -55,7 +56,8 @@ ReflectiveClassBuildItem registerRestClientListenerForReflection() {
ProblemRestClientListener.class.getName(),
ProblemRestClientListener.ClientProblemObjectMapperContextResolver.class.getName(),
ProblemObjectMapperContextResolver.class.getName(),
- ProblemResponseExceptionMapper.class.getName())
+ ProblemResponseExceptionMapper.class.getName(),
+ ProblemClientResponseFilter.class.getName())
.constructors().methods().fields()
.build();
}
diff --git a/belgif-rest-problem-quarkus-client-deployment/src/test/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessorTest.java b/belgif-rest-problem-quarkus-client-deployment/src/test/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessorTest.java
index 05dac55b..fdf10ea0 100644
--- a/belgif-rest-problem-quarkus-client-deployment/src/test/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessorTest.java
+++ b/belgif-rest-problem-quarkus-client-deployment/src/test/java/io/github/belgif/rest/problem/quarkus/deployment/client/ProblemExtensionClientProcessorTest.java
@@ -14,6 +14,7 @@
import org.junit.jupiter.api.Test;
import io.github.belgif.rest.problem.ee.jaxrs.ProblemObjectMapperContextResolver;
+import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemClientResponseFilter;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemResponseExceptionMapper;
import io.github.belgif.rest.problem.ee.jaxrs.client.ProblemRestClientListener;
import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem;
@@ -56,7 +57,8 @@ void registerRestClientListenerForReflection() {
ProblemRestClientListener.class.getName(),
ProblemRestClientListener.ClientProblemObjectMapperContextResolver.class.getName(),
ProblemObjectMapperContextResolver.class.getName(),
- ProblemResponseExceptionMapper.class.getName());
+ ProblemResponseExceptionMapper.class.getName(),
+ ProblemClientResponseFilter.class.getName());
assertThat(result.isConstructors()).isTrue();
assertThat(result.isMethods()).isTrue();
assertThat(result.isFields()).isTrue();
diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc
index 92e1b7b1..a8021d42 100644
--- a/src/main/asciidoc/index.adoc
+++ b/src/main/asciidoc/index.adoc
@@ -303,7 +303,13 @@ Problem support is automatically enabled for MicroProfile REST Client.
==== RESTEasy Proxy Framework
-Problem support must be manually enabled for https://docs.jboss.org/resteasy/docs/6.2.10.Final/userguide/#_client_proxies[RESTEasy Proxy Framework] by wrapping the proxy with `client = ProblemSupport.enable(client)`.
+Problem support must be manually enabled for https://docs.resteasy.dev/7.0/userguide/#_client_proxies[RESTEasy Proxy Framework] by constructing the proxy with `ResteasyProblemSupport.proxy()`.
+
+[source,java]
+----
+ResteasyWebTarget webTarget = (ResteasyWebTarget) client.target(endpoint);
+MyService myService = ResteasyProblemSupport.proxy(webTarget, MyService.class);
+----
[[getting-started-quarkus]]
=== Quarkus
@@ -865,7 +871,7 @@ Otherwise, routes to DefaultExceptionMapper.
WARNING: In accordance with the spec, any exception thrown by a JAX-RS ClientResponseFilter gets wrapped in a ResponseProcessingException, unless the exception itself is a ResponseProcessingException.
For that reason, the ProblemClientResponseFilter wraps the Problem exception in a "ProblemWrapper" class that extends ResponseProcessingException.
-** *ProblemSupport:* for programmatically enabling unwrapping of ProblemWrapper to Problem exceptions on a JAX-RS Client (also works for RESTEasy Proxy Framework clients).
+** *ProblemSupport:* for programmatically enabling unwrapping of ProblemWrapper to Problem exceptions on a JAX-RS Client
+
[source,java]
----
@@ -885,6 +891,17 @@ public void setClient(Client client) {
* *MicroProfile REST Client integration:*
** *ProblemResponseExceptionMapper:* a ResponseExceptionMapper that converts problem responses to Problem exceptions.
** *ProblemRestClientListener:* a RestClientListener that registers the ProblemObjectMapperContextResolver and ProblemResponseExceptionMapper.
+
+* *RESTEasy Proxy Framework integration:*
+
+** *ResteasyProblemSupport:* for creating a problem-enabled RESTEasy client proxy
++
+[source,java]
+----
+ResteasyWebTarget webTarget = (ResteasyWebTarget) client.target(endpoint);
+MyService myService = ResteasyProblemSupport.proxy(webTarget, MyService.class);
+----
+
* *Jakarta EE 9+*: the main belgif-rest-problem-java-ee artifact targets Java EE (javax package namespace).
A secondary artifact that targets Jakarta EE 9+ (jakarta package namespace) is available with `jakarta`.
diff --git a/src/main/asciidoc/release-notes.adoc b/src/main/asciidoc/release-notes.adoc
index eb10bf4e..269d9db7 100644
--- a/src/main/asciidoc/release-notes.adoc
+++ b/src/main/asciidoc/release-notes.adoc
@@ -78,6 +78,11 @@ Note that you can keep using `belgif-rest-problem-java-ee` as before when you wa
*Breaking change:* The `jakarta` variant of `belgif-rest-problem-java-ee` no longer exists and should be replaced by `belgif-rest-problem-jakarta-ee`.
====
+Changes impacting RESTEasy clients:
+
+* Remove `@Provider` annotation on `ProblemClientResponseFilter` so it no longer gets auto-registered for RESTEasy clients
+* Introduce `ResteasyProblemSupport` class for creating a problem-enabled RESTEasy proxy client
+
*belgif-rest-problem-quarkus:*
Provide separate modules in case you specifically only want to use problems either client-side or server-side.