Skip to content

Commit

Permalink
Merge pull request #30270 from geoand/#29796
Browse files Browse the repository at this point in the history
Support for Method parameter in methods annotated with @ClientExceptionMapper
  • Loading branch information
geoand authored Jan 10, 2023
2 parents 54901e3 + a2ef3a8 commit 8ce4d3f
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 12 deletions.
2 changes: 2 additions & 0 deletions docs/src/main/asciidoc/rest-client-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,8 @@ public interface ExtensionsService {

Naturally this handling is per REST Client. `@ClientExceptionMapper` uses the default priority if the `priority` attribute is not set and the normal rules of invoking all handlers in turn apply.

NOTE: Methods annotated with `@ClientExceptionMapper` can also take a `java.lang.reflect.Method` parameter which is useful if the exception mapping code needs to know the REST Client method that was invoked and caused the exception mapping code to engage.

[[multipart]]
== Multipart Form support

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.quarkus.rest.client.reactive.deployment;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;

import javax.ws.rs.Priorities;
import javax.ws.rs.core.Response;
Expand All @@ -12,12 +14,15 @@
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;
import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames;

import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.ClassOutput;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.rest.client.reactive.runtime.ResteasyReactiveResponseExceptionMapper;
import io.quarkus.runtime.util.HashUtil;

/**
Expand All @@ -26,6 +31,10 @@
*/
class ClientExceptionMapperHandler {

private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final ResultHandle[] EMPTY_RESULT_HANDLES_ARRAY = new ResultHandle[0];
private static final MethodDescriptor GET_INVOKED_METHOD =
MethodDescriptor.ofMethod(RestClientRequestContext.class, "getInvokedMethod", Method.class);
private final ClassOutput classOutput;

ClientExceptionMapperHandler(ClassOutput classOutput) {
Expand All @@ -37,8 +46,8 @@ class ClientExceptionMapperHandler {
*
* <pre>
* {@code
* public class SomeService_map_ResponseExceptionMapper_a8fb70beeef2a54b80151484d109618eed381626 implements ResponseExceptionMapper {
* public Throwable toThrowable(Response var1) {
* public class SomeService_map_ResponseExceptionMapper_a8fb70beeef2a54b80151484d109618eed381626 implements ResteasyReactiveResponseExceptionMapper {
* public Throwable toThrowable(Response var1, RestClientRequestContext var2) {
* // simply call the static method of interface
* return SomeService.map(var1);
* }
Expand Down Expand Up @@ -97,15 +106,32 @@ Result generateResponseExceptionMapper(AnnotationInstance instance) {
String generatedClassName = restClientInterfaceClassInfo.name().toString() + "_" + targetMethod.name() + "_"
+ "ResponseExceptionMapper" + "_" + HashUtil.sha1(sigBuilder.toString());
try (ClassCreator cc = ClassCreator.builder().classOutput(classOutput).className(generatedClassName)
.interfaces(ResponseExceptionMapper.class).build()) {
MethodCreator toThrowable = cc.getMethodCreator("toThrowable", Throwable.class, Response.class);
.interfaces(ResteasyReactiveResponseExceptionMapper.class).build()) {
MethodCreator toThrowable = cc.getMethodCreator("toThrowable", Throwable.class, Response.class,
RestClientRequestContext.class);
LinkedHashMap<String, ResultHandle> targetMethodParams = new LinkedHashMap<>();
for (Type paramType : targetMethod.parameterTypes()) {
ResultHandle targetMethodParamHandle;
if (paramType.name().equals(ResteasyReactiveDotNames.RESPONSE)) {
targetMethodParamHandle = toThrowable.getMethodParam(0);
} else if (paramType.name().equals(DotNames.METHOD)) {
targetMethodParamHandle = toThrowable.invokeVirtualMethod(GET_INVOKED_METHOD, toThrowable.getMethodParam(1));
} else {
String message = DotNames.CLIENT_EXCEPTION_MAPPER + " can only take parameters of type '" + ResteasyReactiveDotNames.RESPONSE + "' or '" + DotNames.METHOD + "'"
+ " Offending instance is '" + targetMethod.declaringClass().name().toString() + "#"
+ targetMethod.name() + "'";
throw new IllegalStateException(message);
}
targetMethodParams.put(paramType.name().toString(), targetMethodParamHandle);
}

ResultHandle resultHandle = toThrowable.invokeStaticInterfaceMethod(
MethodDescriptor.ofMethod(
restClientInterfaceClassInfo.name().toString(),
targetMethod.name(),
targetMethod.returnType().name().toString(),
targetMethod.parameterType(0).name().toString()),
toThrowable.getMethodParam(0));
targetMethodParams.keySet().toArray(EMPTY_STRING_ARRAY)),
targetMethodParams.values().toArray(EMPTY_RESULT_HANDLES_ARRAY));
toThrowable.returnValue(resultHandle);

if (priority != Priorities.USER) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.rest.client.reactive.deployment;

import java.lang.reflect.Method;

import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseFilter;

Expand Down Expand Up @@ -28,6 +30,8 @@ public class DotNames {
public static final DotName CLIENT_RESPONSE_FILTER = DotName.createSimple(ClientResponseFilter.class.getName());
public static final DotName CLIENT_EXCEPTION_MAPPER = DotName.createSimple(ClientExceptionMapper.class.getName());

static final DotName METHOD = DotName.createSimple(Method.class.getName());

private DotNames() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.lang.reflect.Method;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Priorities;
Expand Down Expand Up @@ -97,8 +99,8 @@ public interface ClientNoProviders {
Dto get400();

@ClientExceptionMapper
static DummyException map(Response response) {
if (response.getStatus() == 404) {
static DummyException map(Response response, Method method) {
if ((response.getStatus() == 404) && method.getName().equals("get404")) {
return new DummyException();
}
return null;
Expand All @@ -118,8 +120,8 @@ public interface ClientWithRegisteredLowPriorityMapper {
Dto get400();

@ClientExceptionMapper
static DummyException map(Response response) {
if (response.getStatus() == 404) {
static DummyException map(Method method, Response response) {
if ((response.getStatus() == 404) && method.getName().equals("get404")) {
return new DummyException();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* <pre>
* {@code
* &#64;ClientExceptionMapper
* static DummyException map(Response response) {
* static DummyException map(Response response, Method method) {
* if (response.getStatus() == 404) {
* return new DummyException();
* }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ public void filter(ClientRequestContext requestContext, ClientResponseContext re
RestClientRequestContext restClientContext = ((ClientRequestContextImpl) requestContext)
.getRestClientRequestContext();
ResponseImpl response = ClientResponseCompleteRestHandler.mapToResponse(restClientContext, false);
Throwable throwable = exceptionMapper.toThrowable(response);
Throwable throwable;
if (exceptionMapper instanceof ResteasyReactiveResponseExceptionMapper) {
throwable = ((ResteasyReactiveResponseExceptionMapper) exceptionMapper).toThrowable(response,
restClientContext);
} else {
throwable = exceptionMapper.toThrowable(response);
}
if (throwable != null) {
throw new UnwrappableException(throwable);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.quarkus.rest.client.reactive.runtime;

import javax.ws.rs.core.Response;

import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;

public interface ResteasyReactiveResponseExceptionMapper<T extends Throwable> extends ResponseExceptionMapper<T> {

T toThrowable(Response response, RestClientRequestContext context);

@Override
default T toThrowable(Response response) {
throw new IllegalStateException("should never be invoked");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ public void abort() {
restart(abortHandlerChain);
}

public Method getInvokedMethod() {
Object o = properties.get(MP_INVOKED_METHOD_PROP);
if (o instanceof Method) {
return (Method) o;
}
return null;
}

@Override
protected Throwable unwrapException(Throwable t) {
var res = super.unwrapException(t);
Expand Down

0 comments on commit 8ce4d3f

Please sign in to comment.