diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerExceptionMapperGenerator.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerExceptionMapperGenerator.java index 66b4cc1a34da3..13e7668b2ed99 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerExceptionMapperGenerator.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ServerExceptionMapperGenerator.java @@ -2,7 +2,6 @@ import static io.quarkus.gizmo.MethodDescriptor.ofConstructor; import static io.quarkus.gizmo.MethodDescriptor.ofMethod; -import static io.quarkus.resteasy.reactive.common.deployment.QuarkusResteasyReactiveDotNames.*; import static io.quarkus.resteasy.reactive.common.deployment.QuarkusResteasyReactiveDotNames.HTTP_SERVER_REQUEST; import static io.quarkus.resteasy.reactive.server.deployment.GeneratorUtils.paramHandleFromReqContextMethod; import static io.quarkus.resteasy.reactive.server.deployment.GeneratorUtils.routingContextHandler; @@ -18,6 +17,7 @@ import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.URI_INFO; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -63,6 +63,7 @@ final class ServerExceptionMapperGenerator { private static final DotName THROWABLE = DotName.createSimple(Throwable.class.getName()); + private static final DotName EXCEPTION = DotName.createSimple(Exception.class.getName()); private ServerExceptionMapperGenerator() { } @@ -117,6 +118,10 @@ public static Map generatePerClassMapper(MethodInfo targetMethod ClassInfo targetClass = targetMethod.declaringClass(); Type[] handledExceptionTypes = getHandledExceptionTypes(targetMethod); Map result = new HashMap<>(); + + boolean handlesMultipleExceptions = handledExceptionTypes.length > 1; + boolean handledContainsThrowable = Arrays.stream(handledExceptionTypes).anyMatch(t -> t.name().equals(THROWABLE)); + for (Type handledExceptionType : handledExceptionTypes) { String generatedClassName = getGeneratedClassName(targetMethod, handledExceptionType); try (ClassCreator cc = ClassCreator.builder().className(generatedClassName) @@ -145,7 +150,7 @@ public static Map generatePerClassMapper(MethodInfo targetMethod // RESTEasy Reactive toResponse(...) method generateRRResponse(targetMethod, targetClass, handledExceptionType, cc, rrToResponseDescriptor, - returnType, + returnType, handlesMultipleExceptions, handledContainsThrowable, (method, contextHandle) -> { ResultHandle endpointInstanceHandle = method.invokeVirtualMethod( ofMethod(ResteasyReactiveRequestContext.class, "getEndpointInstance", Object.class), @@ -163,7 +168,7 @@ public static Map generatePerClassMapper(MethodInfo targetMethod // RESTEasy Reactive asyncResponse(...) method generateRRUniResponse(targetMethod, targetClass, handledExceptionType, cc, rrAsyncResponseDescriptor, - returnType, + returnType, handlesMultipleExceptions, handledContainsThrowable, (method, contextHandle) -> { ResultHandle endpointInstanceHandle = method.invokeVirtualMethod( ofMethod(ResteasyReactiveRequestContext.class, "getEndpointInstance", Object.class), @@ -238,6 +243,10 @@ public static Map generateGlobalMapper(MethodInfo targetMethod, ClassInfo targetClass = targetMethod.declaringClass(); Type[] handledExceptionTypes = getHandledExceptionTypes(targetMethod); Map result = new HashMap<>(); + + boolean handlesMultipleExceptions = handledExceptionTypes.length > 1; + boolean handledContainsThrowable = Arrays.stream(handledExceptionTypes).anyMatch(t -> t.name().equals(THROWABLE)); + for (Type handledExceptionType : handledExceptionTypes) { String generatedClassName = getGeneratedClassName(targetMethod, handledExceptionType); try (ClassCreator cc = ClassCreator.builder().className(generatedClassName) @@ -276,7 +285,7 @@ public static Map generateGlobalMapper(MethodInfo targetMethod, // RESTEasy Reactive toResponse(...) method generateRRResponse(targetMethod, targetClass, handledExceptionType, cc, rrToResponseDescriptor, - returnType, + returnType, handlesMultipleExceptions, handledContainsThrowable, (method, contextHandle) -> method.readInstanceField(delegateField, method.getThis())); } else if (returnType == ReturnType.UNI_RESPONSE || returnType == ReturnType.UNI_REST_RESPONSE) { MethodDescriptor rrAsyncResponseDescriptor = asyncResponseDescriptor(handledExceptionType, @@ -289,7 +298,7 @@ public static Map generateGlobalMapper(MethodInfo targetMethod, // RESTEasy Reactive asyncResponse(...) method generateRRUniResponse(targetMethod, targetClass, handledExceptionType, cc, rrAsyncResponseDescriptor, - returnType, + returnType, handlesMultipleExceptions, handledContainsThrowable, (method, contextHandle) -> method.readInstanceField(delegateField, method.getThis())); } else { throw new IllegalStateException("ReturnType: '" + returnType + "' is not supported"); @@ -395,7 +404,7 @@ private static void generateRRResponseBridge(Type handledExceptionType, ClassCre private static void generateRRResponse(MethodInfo targetMethod, ClassInfo targetClass, Type handledExceptionType, ClassCreator cc, MethodDescriptor rrToResponseDescriptor, - ReturnType returnType, + ReturnType returnType, boolean handlesMultipleExceptions, boolean handledContainsThrowable, BiFunction targetInstanceHandleCreator) { MethodCreator mc = cc.getMethodCreator(rrToResponseDescriptor); ResultHandle exceptionHandle = mc.getMethodParam(0); @@ -414,7 +423,8 @@ private static void generateRRResponse(MethodInfo targetMethod, ClassInfo target mc.returnValue(resultHandle); } else { TargetMethodParamsInfo targetMethodParamsInfo = getTargetMethodParamsInfo(targetMethod, targetClass, - handledExceptionType, mc, exceptionHandle, contextHandle); + handledExceptionType, mc, exceptionHandle, contextHandle, handlesMultipleExceptions, + handledContainsThrowable); ResultHandle resultHandle = mc.invokeVirtualMethod( ofMethod(targetClass.name().toString(), targetMethod.name(), targetMethod.returnType().name().toString(), targetMethodParamsInfo.getTypes()), @@ -441,7 +451,7 @@ private static void generateRRUniResponseBridge(Type handledExceptionType, Class private static void generateRRUniResponse(MethodInfo targetMethod, ClassInfo targetClass, Type handledExceptionType, ClassCreator cc, MethodDescriptor rrAsyncResponseDescriptor, - ReturnType returnType, + ReturnType returnType, boolean handlesMultipleExceptions, boolean handledContainsThrowable, BiFunction targetInstanceHandleCreator) { MethodCreator mc = cc.getMethodCreator(rrAsyncResponseDescriptor); ResultHandle exceptionHandle = mc.getMethodParam(0); @@ -459,7 +469,8 @@ private static void generateRRUniResponse(MethodInfo targetMethod, ClassInfo tar targetInstanceHandle); } else { TargetMethodParamsInfo targetMethodParamsInfo = getTargetMethodParamsInfo(targetMethod, targetClass, - handledExceptionType, mc, exceptionHandle, contextHandle); + handledExceptionType, mc, exceptionHandle, contextHandle, handlesMultipleExceptions, + handledContainsThrowable); uniHandle = mc.invokeVirtualMethod( ofMethod(targetClass.name().toString(), targetMethod.name(), Uni.class.getName(), targetMethodParamsInfo.getTypes()), @@ -472,7 +483,8 @@ private static void generateRRUniResponse(MethodInfo targetMethod, ClassInfo tar } private static TargetMethodParamsInfo getTargetMethodParamsInfo(MethodInfo targetMethod, ClassInfo targetClass, - Type handledExceptionType, MethodCreator mc, ResultHandle exceptionHandle, ResultHandle contextHandle) { + Type handledExceptionType, MethodCreator mc, ResultHandle exceptionHandle, ResultHandle contextHandle, + boolean handlesMultipleExceptions, boolean handledContainsThrowable) { List parameters = targetMethod.parameters(); ResultHandle[] targetMethodParamHandles = new ResultHandle[parameters.size()]; String[] parameterTypes = new String[parameters.size()]; @@ -481,7 +493,41 @@ private static TargetMethodParamsInfo getTargetMethodParamsInfo(MethodInfo targe Type parameter = parameters.get(i); DotName paramDotName = parameter.name(); parameterTypes[i] = paramDotName.toString(); + String parameterName = targetMethod.parameterName(i); if (paramDotName.equals(handledExceptionType.name())) { + if (handlesMultipleExceptions) { + throw new RuntimeException("Parameter '" + parameterName + "' of method '" + targetMethod.name() + + " of class '" + targetClass.name() + "' cannot be of type '" + handledExceptionType.name() + + "' because the method handles multiple exceptions. You can use '" + + (handledContainsThrowable ? Throwable.class.getName() : Exception.class.getName()) + + "' instead."); + } else { + targetMethodParamHandles[i] = exceptionHandle; + } + } else if (paramDotName.equals(THROWABLE)) { + targetMethodParamHandles[i] = exceptionHandle; + } else if (paramDotName.equals(EXCEPTION)) { + if (handlesMultipleExceptions) { + if (handledContainsThrowable) { + throw new RuntimeException("Parameter '" + parameterName + "' of method '" + targetMethod.name() + + " of class '" + targetClass.name() + + "' cannot be of type '" + handledExceptionType.name() + + "' because the method handles multiple exceptions. You can use '" + Exception.class.getName() + + "' instead."); + } else { + targetMethodParamHandles[i] = exceptionHandle; + } + } else { + if (handledContainsThrowable) { + throw new RuntimeException("Parameter '" + parameterName + "' of method '" + targetMethod.name() + + " of class '" + targetClass.name() + + "' cannot be of type '" + handledExceptionType.name() + "'. You can use '" + + Throwable.class.getName() + "' instead."); + } else { + targetMethodParamHandles[i] = exceptionHandle; + } + } + } else if (paramDotName.equals(handledExceptionType.name())) { targetMethodParamHandles[i] = exceptionHandle; } else if (CONTAINER_REQUEST_CONTEXT.equals(paramDotName) || QUARKUS_REST_CONTAINER_REQUEST_CONTEXT.equals(paramDotName)) { @@ -528,7 +574,6 @@ private static TargetMethodParamsInfo getTargetMethodParamsInfo(MethodInfo targe } else if (ROUTING_CONTEXT.equals(paramDotName)) { targetMethodParamHandles[i] = routingContextHandler(mc, contextHandle); } else { - String parameterName = targetMethod.parameterName(i); throw new RuntimeException("Parameter '" + parameterName + "' of method '" + targetMethod.name() + " of class '" + targetClass.name() + "' is not allowed"); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/CustomExceptionMappersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/CustomExceptionMappersTest.java index 9dfd41a6d7033..68fcf3d9f59c1 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/CustomExceptionMappersTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/CustomExceptionMappersTest.java @@ -21,7 +21,8 @@ public class CustomExceptionMappersTest { public JavaArchive get() { return ShrinkWrap.create(JavaArchive.class) .addClasses(FirstResource.class, SecondResource.class, - MyException.class, MyOtherException.class, UniException.class, ExtendsUniException.class, + MyException.class, MyOtherException.class, UniException.class, + OtherUniException.class, ExtendsUniException.class, MyOtherExceptionMapper.class, UniExceptionMapper.class, SomeBean.class, ExceptionUtil.class); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/FirstResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/FirstResource.java index 53012a9073b9a..59bcada55af2f 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/FirstResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/FirstResource.java @@ -58,8 +58,8 @@ public Uni uni(@RestQuery String name) { } @ServerExceptionMapper({ IllegalStateException.class, IllegalArgumentException.class }) - public Response handleIllegal() { - return Response.status(409).build(); + public Response handleIllegal(Exception e) { + return Response.status(409).entity(e.getMessage()).build(); } @ServerExceptionMapper diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/OtherUniException.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/OtherUniException.java new file mode 100644 index 0000000000000..3378ceb53c025 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/OtherUniException.java @@ -0,0 +1,4 @@ +package io.quarkus.resteasy.reactive.server.test.customexceptions; + +public class OtherUniException extends RuntimeException { +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/UniExceptionMapper.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/UniExceptionMapper.java index 3005fd23e7790..127ccd6dde728 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/UniExceptionMapper.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customexceptions/UniExceptionMapper.java @@ -8,8 +8,8 @@ public class UniExceptionMapper { - @ServerExceptionMapper(UniException.class) - Uni handleUni() { + @ServerExceptionMapper({ UniException.class, OtherUniException.class }) + Uni handleUni(Throwable t) { return Uni.createFrom().deferred(() -> Uni.createFrom().item(Response.status(413).build())); }