diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java index f9f848aece..2335d0b140 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java @@ -93,13 +93,12 @@ public class EndpointMBean implements DynamicMBean { return invoke(operation, params); } - private Object invoke(JmxOperation operation, Object[] params) { + private Object invoke(JmxOperation operation, Object[] params) throws MBeanException { try { String[] parameterNames = operation.getParameters().stream() .map(JmxOperationParameter::getName).toArray(String[]::new); Map arguments = getArguments(parameterNames, params); - Object result = operation - .invoke(new InvocationContext(SecurityContext.NONE, arguments)); + Object result = invokeOperation(operation, arguments); if (REACTOR_PRESENT) { result = ReactiveHandler.handle(result); } @@ -110,6 +109,26 @@ public class EndpointMBean implements DynamicMBean { } } + private Object invokeOperation(JmxOperation operation, + Map arguments) throws MBeanException { + try { + return operation.invoke(new InvocationContext(SecurityContext.NONE, + arguments)); + } + catch (Exception ex) { + throw new MBeanException(translateIfNecessary(ex), ex.getMessage()); + } + } + + private Exception translateIfNecessary(Exception exception) { + if (exception.getClass().getName().startsWith("java.")) { + return exception; + } + else { + return new IllegalStateException(exception.getMessage()); + } + } + private Map getArguments(String[] parameterNames, Object[] params) { Map arguments = new HashMap<>(); for (int i = 0; i < params.length; i++) { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java index dc70b4cd3b..4c0cd5d32a 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java @@ -29,6 +29,8 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import reactor.core.publisher.Mono; +import org.springframework.beans.FatalBeanException; + import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.instanceOf; import static org.mockito.Mockito.mock; @@ -84,6 +86,34 @@ public class EndpointMBeanTests { assertThat(result).isEqualTo("result"); } + @Test + public void invokeWhenOperationFailedShouldTranslateException() + throws MBeanException, ReflectionException { + TestExposableJmxEndpoint endpoint = new TestExposableJmxEndpoint( + new TestJmxOperation((arguments) -> { + throw new FatalBeanException("test failure"); + })); + EndpointMBean bean = new EndpointMBean(this.responseMapper, endpoint); + this.thrown.expect(MBeanException.class); + this.thrown.expectCause(instanceOf(IllegalStateException.class)); + this.thrown.expectMessage("test failure"); + bean.invoke("testOperation", NO_PARAMS, NO_SIGNATURE); + } + + @Test + public void invokeWhenOperationFailedWithJdkExceptionShouldReuseException() + throws MBeanException, ReflectionException { + TestExposableJmxEndpoint endpoint = new TestExposableJmxEndpoint( + new TestJmxOperation((arguments) -> { + throw new UnsupportedOperationException("test failure"); + })); + EndpointMBean bean = new EndpointMBean(this.responseMapper, endpoint); + this.thrown.expect(MBeanException.class); + this.thrown.expectCause(instanceOf(UnsupportedOperationException.class)); + this.thrown.expectMessage("test failure"); + bean.invoke("testOperation", NO_PARAMS, NO_SIGNATURE); + } + @Test public void invokeWhenActionNameIsNotAnOperationShouldThrowException() throws MBeanException, ReflectionException {