Commit fa542bac authored by Stephane Nicoll's avatar Stephane Nicoll

Translate user-defined exception when invoking JMX operation

This commit makes sure to respect the MBeanServer#invoke contract by
wrapping any user-defined exception in an MBeanException. Also, any
exception not from the JDK is translated, as it may lead to unexpected
issue on the client if that class isn't present. This is consistent
with our operation result mapping strategy.

Closes gh-10448
parent 2204d5f7
......@@ -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<String, Object> 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<String, Object> 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<String, Object> getArguments(String[] parameterNames, Object[] params) {
Map<String, Object> arguments = new HashMap<>();
for (int i = 0; i < params.length; i++) {
......
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment