From 4d26302dcd062d9899119f879515fa88d112b10c Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 2 Feb 2010 20:33:25 +0000 Subject: [PATCH] INT-793 Changed 'objectName' to 'defaultObjectName' and added a resolution step so that a value in the request Message's headers (with key JmxHeaders.OBJECT_NAME) takes precedence. --- .../integration/jmx/JmxHeaders.java | 8 +- .../jmx/OperationInvokingHandler.java | 74 +++++++++++++++---- .../jmx/OperationInvokingHandlerTests.java | 4 +- 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/JmxHeaders.java b/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/JmxHeaders.java index dedf1f604c..68e5541e6d 100644 --- a/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/JmxHeaders.java +++ b/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/JmxHeaders.java @@ -19,6 +19,8 @@ package org.springframework.integration.jmx; import org.springframework.integration.core.MessageHeaders; /** + * Constants for JMX related Message Header keys. + * * @author Mark Fisher * @since 2.0 */ @@ -26,10 +28,12 @@ public abstract class JmxHeaders { private static final String PREFIX = MessageHeaders.PREFIX + "jmx_"; - public static final String NOTIFICATION_HANDBACK = PREFIX + "notificationHandback"; + public static final String OBJECT_NAME = PREFIX + "objectName"; + + public static final String OPERATION_NAME = PREFIX + "operationName"; public static final String NOTIFICATION_TYPE = PREFIX + "notificationType"; - public static final String OPERATION_NAME = "operationName"; + public static final String NOTIFICATION_HANDBACK = PREFIX + "notificationHandback"; } diff --git a/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingHandler.java b/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingHandler.java index 253e39f6f1..4b2947af55 100644 --- a/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingHandler.java +++ b/org.springframework.integration.jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingHandler.java @@ -17,6 +17,7 @@ package org.springframework.integration.jmx; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,44 +44,50 @@ import org.springframework.util.ObjectUtils; */ public class OperationInvokingHandler extends AbstractReplyProducingMessageHandler implements InitializingBean { - private volatile ObjectName objectName; - private volatile MBeanServer server; + private volatile ObjectName defaultObjectName; + private volatile String defaultOperationName; - public void setObjectName(String objectName) { + public void setServer(MBeanServer server) { + this.server = server; + } + + public void setDefaultObjectName(String defaultObjectName) { try { - this.objectName = ObjectNameManager.getInstance(objectName); + if (defaultObjectName != null) { + this.defaultObjectName = ObjectNameManager.getInstance(defaultObjectName); + } } catch (MalformedObjectNameException e) { throw new IllegalArgumentException(e); } } - public void setServer(MBeanServer server) { - this.server = server; - } - public void setDefaultOperationName(String defaultOperationName) { this.defaultOperationName = defaultOperationName; } public void afterPropertiesSet() { Assert.notNull(this.server, "MBeanServer is required."); - Assert.notNull(this.objectName, "ObjectName is required."); } @Override protected Object handleRequestMessage(Message requestMessage) { + ObjectName objectName = this.resolveObjectName(requestMessage); String operationName = this.resolveOperationName(requestMessage); Map paramsFromMessage = this.resolveParameters(requestMessage); try { - MBeanOperationInfo[] opInfoArray = this.server.getMBeanInfo(this.objectName).getOperations(); + MBeanOperationInfo[] opInfoArray = this.server.getMBeanInfo(objectName).getOperations(); + boolean hasNoArgOption = false; for (MBeanOperationInfo opInfo : opInfoArray) { if (operationName.equals(opInfo.getName())) { MBeanParameterInfo[] paramInfoArray = opInfo.getSignature(); + if (paramInfoArray.length == 0) { + hasNoArgOption = true; + } if (paramInfoArray.length == paramsFromMessage.size()) { int index = 0; Object values[] = new Object[paramInfoArray.length]; @@ -94,20 +101,52 @@ public class OperationInvokingHandler extends AbstractReplyProducingMessageHandl } } if (index == paramInfoArray.length) { - return this.server.invoke(this.objectName, operationName, values, signature); + return this.server.invoke(objectName, operationName, values, signature); } } } } + if (hasNoArgOption) { + return this.server.invoke(objectName, operationName, null, null); + } throw new MessagingException(requestMessage, "failed to find JMX operation '" - + operationName + "' on MBean [" + this.objectName + "]"); + + operationName + "' on MBean [" + objectName + "]"); } catch (JMException e) { throw new MessageHandlingException(requestMessage, "failed to invoke JMX operation '" + - operationName + "' on MBean [" + this.objectName + "]", e); + operationName + "' on MBean [" + objectName + "]", e); } } + /** + * First checks for the presence of a {@link JmxHeaders#OBJECT_NAME} header, + * then falls back to this handler's {@link #defaultObjectName} if available. + */ + private ObjectName resolveObjectName(Message message) { + ObjectName objectName = null; + Object objectNameHeader = message.getHeaders().get(JmxHeaders.OBJECT_NAME); + if (objectNameHeader instanceof ObjectName) { + objectName = (ObjectName) objectNameHeader; + } + else if (objectNameHeader instanceof String) { + try { + objectName = ObjectNameManager.getInstance(objectNameHeader); + } + catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e); + } + } + else { + objectName = this.defaultObjectName; + } + Assert.notNull(objectName, "Failed to resolve ObjectName."); + return objectName; + } + + /** + * First checks for the presence of a {@link JmxHeaders#OPERATION_NAME} header, + * then falls back to this handler's {@link #defaultOperationName} if available. + */ private String resolveOperationName(Message message) { String operationName = message.getHeaders().get(JmxHeaders.OPERATION_NAME, String.class); if (operationName == null) { @@ -126,11 +165,16 @@ public class OperationInvokingHandler extends AbstractReplyProducingMessageHandl else if (message.getPayload() instanceof List) { map = this.createParameterMapFromList((List) message.getPayload()); } - else if (message.getPayload() != null) { + else if (message.getPayload() != null && message.getPayload().getClass().isArray()) { map = this.createParameterMapFromList( Arrays.asList(ObjectUtils.toObjectArray(message.getPayload()))); } - Assert.notNull(map, "Failed to create parameter Map from message."); + else if (message.getPayload() != null) { + map = this.createParameterMapFromList(Collections.singletonList(message.getPayload())); + } + else { + map = Collections.EMPTY_MAP; + } return map; } diff --git a/org.springframework.integration.jmx/src/test/java/org/springframework/integration/jmx/OperationInvokingHandlerTests.java b/org.springframework.integration.jmx/src/test/java/org/springframework/integration/jmx/OperationInvokingHandlerTests.java index 511c92bf10..53087ad6fc 100644 --- a/org.springframework.integration.jmx/src/test/java/org/springframework/integration/jmx/OperationInvokingHandlerTests.java +++ b/org.springframework.integration.jmx/src/test/java/org/springframework/integration/jmx/OperationInvokingHandlerTests.java @@ -67,7 +67,7 @@ public class OperationInvokingHandlerTests { QueueChannel outputChannel = new QueueChannel(); OperationInvokingHandler handler = new OperationInvokingHandler(); handler.setServer(this.server); - handler.setObjectName(this.objectName); + handler.setDefaultObjectName(this.objectName); handler.setOutputChannel(outputChannel); handler.afterPropertiesSet(); Map params = new HashMap(); @@ -86,7 +86,7 @@ public class OperationInvokingHandlerTests { QueueChannel outputChannel = new QueueChannel(); OperationInvokingHandler handler = new OperationInvokingHandler(); handler.setServer(this.server); - handler.setObjectName(this.objectName); + handler.setDefaultObjectName(this.objectName); handler.setOutputChannel(outputChannel); handler.afterPropertiesSet(); List params = Arrays.asList(new Object[] { "foo", new Integer(123) });