INT-4573: Fix logic in the OperationInvokingMH
JIRA: https://jira.spring.io/browse/INT-4573 * Fix check order for headers first in the `resolveObjectName()` and `resolveOperationName()`. Then fallback to defaults configured on the `OperationInvokingMessageHandler` * Remove also a deprecated API in the `OperationInvokingMessageHandler` * Remove requirements for `object-name` and `operation-name` XML attributes since those options can be provided in the request message headers * Fix `OperationInvokingChannelAdapterParserTests` for proper headers resolution * Mention `JmxHeaders.OBJECT_NAME` and `JmxHeaders.OPERATION_NAME` headers in the `jmx.adoc`
This commit is contained in:
committed by
Gary Russell
parent
0ce6d1cef8
commit
4f34d922c1
@@ -65,7 +65,7 @@ import org.springframework.util.ObjectUtils;
|
|||||||
*/
|
*/
|
||||||
public class OperationInvokingMessageHandler extends AbstractReplyProducingMessageHandler implements InitializingBean {
|
public class OperationInvokingMessageHandler extends AbstractReplyProducingMessageHandler implements InitializingBean {
|
||||||
|
|
||||||
private MBeanServerConnection server;
|
private final MBeanServerConnection server;
|
||||||
|
|
||||||
private ObjectName defaultObjectName;
|
private ObjectName defaultObjectName;
|
||||||
|
|
||||||
@@ -73,17 +73,6 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa
|
|||||||
|
|
||||||
private boolean expectReply = true;
|
private boolean expectReply = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an instance with no arguments; for backward compatibility.
|
|
||||||
* The {@link #setServer(MBeanServerConnection)} must be used as well.
|
|
||||||
* The {@link #OperationInvokingMessageHandler(MBeanServerConnection)}
|
|
||||||
* is a preferred way for instantiation.
|
|
||||||
* @since 4.3.20
|
|
||||||
* @deprecated since 4.3.20
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public OperationInvokingMessageHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an instance based on the provided {@link MBeanServerConnection}.
|
* Construct an instance based on the provided {@link MBeanServerConnection}.
|
||||||
@@ -95,17 +84,6 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa
|
|||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a reference to the MBeanServer within which the MBean
|
|
||||||
* target for operation invocation has been registered.
|
|
||||||
* @param server The MBean server connection.
|
|
||||||
* @deprecated since 4.3.20 in favor of {@link #OperationInvokingMessageHandler(MBeanServerConnection)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setServer(MBeanServerConnection server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a default ObjectName to use when no such header is
|
* Specify a default ObjectName to use when no such header is
|
||||||
* available on the Message being handled.
|
* available on the Message being handled.
|
||||||
@@ -146,11 +124,6 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa
|
|||||||
return this.expectReply ? "jmx:operation-invoking-outbound-gateway" : "jmx:operation-invoking-channel-adapter";
|
return this.expectReply ? "jmx:operation-invoking-outbound-gateway" : "jmx:operation-invoking-channel-adapter";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doInit() {
|
|
||||||
Assert.notNull(this.server, "MBeanServer is required.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||||
ObjectName objectName = resolveObjectName(requestMessage);
|
ObjectName objectName = resolveObjectName(requestMessage);
|
||||||
@@ -245,35 +218,36 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First checks if defaultObjectName is set, otherwise falls back on {@link JmxHeaders#OBJECT_NAME} header.
|
* First checks {@link JmxHeaders#OBJECT_NAME} header in the request message,
|
||||||
|
* otherwise falls back to {@link #defaultObjectName}.
|
||||||
*/
|
*/
|
||||||
private ObjectName resolveObjectName(Message<?> message) {
|
private ObjectName resolveObjectName(Message<?> message) {
|
||||||
ObjectName objectName = this.defaultObjectName;
|
ObjectName objectName;
|
||||||
if (objectName == null) {
|
Object objectNameHeader = message.getHeaders().get(JmxHeaders.OBJECT_NAME);
|
||||||
Object objectNameHeader = message.getHeaders().get(JmxHeaders.OBJECT_NAME);
|
if (objectNameHeader != null) {
|
||||||
if (objectNameHeader instanceof ObjectName) {
|
try {
|
||||||
objectName = (ObjectName) objectNameHeader;
|
objectName = ObjectNameManager.getInstance(objectNameHeader);
|
||||||
}
|
}
|
||||||
else if (objectNameHeader instanceof String) {
|
catch (MalformedObjectNameException e) {
|
||||||
try {
|
throw new IllegalArgumentException(e);
|
||||||
objectName = ObjectNameManager.getInstance(objectNameHeader);
|
|
||||||
}
|
|
||||||
catch (MalformedObjectNameException e) {
|
|
||||||
throw new IllegalArgumentException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert.notNull(objectName, "Failed to resolve ObjectName.");
|
else {
|
||||||
|
objectName = this.defaultObjectName;
|
||||||
|
|
||||||
|
}
|
||||||
|
Assert.notNull(objectName, "Failed to resolve ObjectName: either from headers or 'defaultObjectName'.");
|
||||||
return objectName;
|
return objectName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First checks if defaultOperationName is set, otherwise falls back on {@link JmxHeaders#OPERATION_NAME} header.
|
* First checks if {@link JmxHeaders#OPERATION_NAME} header,
|
||||||
|
* otherwise falls back to {@link #operationName}.
|
||||||
*/
|
*/
|
||||||
private String resolveOperationName(Message<?> message) {
|
private String resolveOperationName(Message<?> message) {
|
||||||
String operation = this.operationName;
|
String operation = message.getHeaders().get(JmxHeaders.OPERATION_NAME, String.class);
|
||||||
if (operation == null) {
|
if (operation == null) {
|
||||||
operation = message.getHeaders().get(JmxHeaders.OPERATION_NAME, String.class);
|
operation = this.operationName;
|
||||||
}
|
}
|
||||||
Assert.notNull(operation, "Failed to resolve operation name.");
|
Assert.notNull(operation, "Failed to resolve operation name.");
|
||||||
return operation;
|
return operation;
|
||||||
|
|||||||
@@ -318,8 +318,8 @@
|
|||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
<xsd:complexContent>
|
<xsd:complexContent>
|
||||||
<xsd:extension base="mbeanServerIdentifierType">
|
<xsd:extension base="mbeanServerIdentifierType">
|
||||||
<xsd:attribute name="object-name" type="xsd:string" use="required" />
|
<xsd:attribute name="object-name" type="xsd:string" />
|
||||||
<xsd:attribute name="operation-name" type="xsd:string" use="required" />
|
<xsd:attribute name="operation-name" type="xsd:string" />
|
||||||
</xsd:extension>
|
</xsd:extension>
|
||||||
</xsd:complexContent>
|
</xsd:complexContent>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|||||||
@@ -43,4 +43,6 @@
|
|||||||
operation-name="testWithReturn"/>
|
operation-name="testWithReturn"/>
|
||||||
</si:chain>
|
</si:chain>
|
||||||
|
|
||||||
|
<jmx:operation-invoking-channel-adapter id="input2"/>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.DirectFieldAccessor;
|
import org.springframework.beans.DirectFieldAccessor;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||||
@@ -56,21 +55,21 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MessageChannel input;
|
private MessageChannel input;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageChannel input2;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MessageChannel operationWithNonNullReturn;
|
private MessageChannel operationWithNonNullReturn;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MessageChannel operationInvokingWithinChain;
|
private MessageChannel operationInvokingWithinChain;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MessageChannel operationWithinChainWithNonNullReturn;
|
private MessageChannel operationWithinChainWithNonNullReturn;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TestBean testBean;
|
private TestBean testBean;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private BeanFactory beanFactory;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("operationWithNonNullReturn.handler")
|
@Qualifier("operationWithNonNullReturn.handler")
|
||||||
private OperationInvokingMessageHandler operationWithNonNullReturnHandler;
|
private OperationInvokingMessageHandler operationWithNonNullReturnHandler;
|
||||||
@@ -79,7 +78,7 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
@Qualifier("chainWithOperation$child.operationWithinChainWithNonNullReturnHandler.handler")
|
@Qualifier("chainWithOperation$child.operationWithinChainWithNonNullReturnHandler.handler")
|
||||||
private OperationInvokingMessageHandler operationWithinChainWithNonNullReturnHandler;
|
private OperationInvokingMessageHandler operationWithinChainWithNonNullReturnHandler;
|
||||||
|
|
||||||
private static volatile int adviceCalled;
|
private static int adviceCalled;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void resetLists() {
|
public void resetLists() {
|
||||||
@@ -119,9 +118,9 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
// Headers should be ignored
|
// Headers should be ignored
|
||||||
public void adapterWitJmxHeaders() {
|
public void adapterWitJmxHeaders() {
|
||||||
assertThat(testBean.messages.size()).isEqualTo(0);
|
assertThat(testBean.messages.size()).isEqualTo(0);
|
||||||
input.send(this.createMessage("1"));
|
this.input2.send(createMessage("1"));
|
||||||
input.send(this.createMessage("2"));
|
this.input2.send(createMessage("2"));
|
||||||
input.send(this.createMessage("3"));
|
this.input2.send(createMessage("3"));
|
||||||
assertThat(testBean.messages.size()).isEqualTo(3);
|
assertThat(testBean.messages.size()).isEqualTo(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +133,8 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testOperationWithinChainWithNonNullReturn() {
|
public void testOperationWithinChainWithNonNullReturn() {
|
||||||
Log logger =
|
Log logger =
|
||||||
spy(TestUtils.getPropertyValue(this.operationWithinChainWithNonNullReturnHandler, "logger", Log.class));
|
spy(TestUtils.getPropertyValue(this.operationWithinChainWithNonNullReturnHandler, "logger",
|
||||||
|
Log.class));
|
||||||
|
|
||||||
willReturn(true)
|
willReturn(true)
|
||||||
.given(logger)
|
.given(logger)
|
||||||
@@ -142,7 +142,7 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
|
|
||||||
new DirectFieldAccessor(this.operationWithinChainWithNonNullReturnHandler)
|
new DirectFieldAccessor(this.operationWithinChainWithNonNullReturnHandler)
|
||||||
.setPropertyValue("logger", logger);
|
.setPropertyValue("logger", logger);
|
||||||
this.operationWithinChainWithNonNullReturn.send(new GenericMessage<>("test1"));
|
this.operationWithinChainWithNonNullReturn.send(new GenericMessage<>("test1"));
|
||||||
verify(logger).warn("This component doesn't expect a reply. " +
|
verify(logger).warn("This component doesn't expect a reply. " +
|
||||||
"The MBean operation 'testWithReturn' result '[test1]' for " +
|
"The MBean operation 'testWithReturn' result '[test1]' for " +
|
||||||
"'org.springframework.integration.jmx.config:type=TestBean,name=testBeanAdapter' is ignored.");
|
"'org.springframework.integration.jmx.config:type=TestBean,name=testBeanAdapter' is ignored.");
|
||||||
@@ -150,8 +150,10 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
|
|
||||||
private Message<?> createMessage(String payload) {
|
private Message<?> createMessage(String payload) {
|
||||||
return MessageBuilder.withPayload(payload)
|
return MessageBuilder.withPayload(payload)
|
||||||
.setHeader(JmxHeaders.OBJECT_NAME, "org.springframework.integration.jmx.config:type=TestBean,name=foo")
|
.setHeader(JmxHeaders.OBJECT_NAME,
|
||||||
.setHeader(JmxHeaders.OPERATION_NAME, "blah").build();
|
"org.springframework.integration.jmx.config:type=TestBean,name=testBeanAdapter")
|
||||||
|
.setHeader(JmxHeaders.OPERATION_NAME, "test")
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||||
@@ -163,4 +165,5 @@ public class OperationInvokingChannelAdapterParserTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ You can also implement your own filter.
|
|||||||
|
|
||||||
The operation-invoking channel adapter enables message-driven invocation of any managed operation exposed by an MBean.
|
The operation-invoking channel adapter enables message-driven invocation of any managed operation exposed by an MBean.
|
||||||
Each invocation requires the operation name to be invoked and the object name of the target MBean.
|
Each invocation requires the operation name to be invoked and the object name of the target MBean.
|
||||||
Both of these must be explicitly provided by adapter configuration, as the following example shows:
|
Both of these must be explicitly provided by adapter configuration or via `JmxHeaders.OBJECT_NAME` and `JmxHeaders.OPERATION_NAME` message headers, respectively:
|
||||||
|
|
||||||
====
|
====
|
||||||
[source,xml]
|
[source,xml]
|
||||||
|
|||||||
Reference in New Issue
Block a user