Removed AbstractMethodInvokingAdapter and AbstractMessageHandlerAdapter (has been replaced by the new AbstractMessageHandler). The MethodInvokingPayloadTransformer now uses a NameResolvingMethodInvoker.

This commit is contained in:
Mark Fisher
2008-08-14 10:34:26 +00:00
parent a99b03c387
commit 234c87da1d
4 changed files with 57 additions and 302 deletions

View File

@@ -1,141 +0,0 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.handler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.integration.channel.ChannelRegistry;
import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.message.DefaultMessageCreator;
import org.springframework.integration.message.DefaultMessageMapper;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.message.MessageCreator;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.message.MessageHeaders;
import org.springframework.integration.message.MessageMapper;
import org.springframework.integration.util.AbstractMethodInvokingAdapter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* An implementation of the {@link MessageHandler} interface that invokes the specified method and target object. Either
* a {@link Method} reference or a 'methodName' may be provided, but both are not necessary. In fact, while preference
* is given to a {@link Method} reference if available, an Exception will be thrown if a non-matching 'methodName' has
* also been provided. Therefore, to avoid such ambiguity, it is recommended to provide just one or the other.
* <p>
* This handler also accepts an implementation of the {@link MessageMapper} strategy interface which it uses for
* converting from the {@link Message} being handled to an Object prior to invoking the method. Likewise, if the method
* has a non-null return value, a reply message will be generated by the configured implementation of the
* {@link MessageCreator} strategy interface. In both cases, the default implementations will simply consider the
* message's payload.
*
* @author Mark Fisher
*/
public abstract class AbstractMessageHandlerAdapter extends AbstractMethodInvokingAdapter
implements MessageHandler, ChannelRegistryAware {
public static final String OUTPUT_CHANNEL_NAME_KEY = "outputChannelName";
private volatile boolean methodExpectsMessage;
private volatile MessageMapper messageMapper = new DefaultMessageMapper();
private volatile MessageCreator messageCreator = new DefaultMessageCreator();
private volatile ChannelRegistry channelRegistry;
public void setMethodExpectsMessage(boolean methodExpectsMessage) {
this.methodExpectsMessage = methodExpectsMessage;
}
public void setMessageMapper(MessageMapper messageMapper) {
Assert.notNull(messageMapper, "'messageMapper' must not be null");
this.messageMapper = messageMapper;
}
public void setMessageCreator(MessageCreator messageCreator) {
Assert.notNull(messageCreator, "'messageCreator' must not be null");
this.messageCreator = messageCreator;
}
public void setChannelRegistry(ChannelRegistry channelRegistry) {
this.channelRegistry = channelRegistry;
}
protected ChannelRegistry getChannelRegistry() {
return this.channelRegistry;
}
public Message<?> handle(Message<?> message) {
if (!this.isInitialized()) {
this.afterPropertiesSet();
}
if (message == null) {
throw new IllegalArgumentException("message must not be null");
}
Object args[] = null;
Object mappingResult = (this.methodExpectsMessage) ? message : this.messageMapper.mapMessage(message);
if (mappingResult != null && mappingResult.getClass().isArray()
&& (Object.class.isAssignableFrom(mappingResult.getClass().getComponentType()))) {
args = (Object[]) mappingResult;
}
else {
args = new Object[] { mappingResult };
}
try {
Object result = null;
try {
result = this.invokeMethod(args);
}
catch (NoSuchMethodException e) {
result = this.invokeMethod(message);
this.methodExpectsMessage = true;
}
if (result == null) {
return null;
}
return this.handleReturnValue(result, message);
}
catch (InvocationTargetException e) {
throw new MessageHandlingException(message, "Handler method '"
+ this.getMethodName() + "' threw an Exception.", e.getTargetException());
}
catch (Throwable e) {
throw new MessageHandlingException(message, "Failed to invoke handler method '"
+ this.getMethodName() + "' with arguments: " + ObjectUtils.nullSafeToString(args), e);
}
}
protected Message<?> createReplyMessage(Object returnValue, Message<?> originalMessage) {
Message<?> reply = this.messageCreator.createMessage(returnValue);
if (reply == null) {
return null;
}
return MessageBuilder.fromMessage(reply).copyHeadersIfAbsent(originalMessage.getHeaders())
.setHeaderIfAbsent(MessageHeaders.CORRELATION_ID, originalMessage.getHeaders().getId()).build();
}
/**
* Subclasses must implement this method to handle the return value.
*/
protected abstract Message<?> handleReturnValue(Object returnValue, Message<?> originalMessage);
}

View File

@@ -16,15 +16,53 @@
package org.springframework.integration.transformer;
import org.springframework.integration.util.AbstractMethodInvokingAdapter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.util.MethodInvoker;
import org.springframework.integration.util.NameResolvingMethodInvoker;
/**
* @author Mark Fisher
*/
public class MethodInvokingPayloadTransformer extends AbstractMethodInvokingAdapter implements PayloadTransformer<Object, Object> {
public class MethodInvokingPayloadTransformer implements PayloadTransformer<Object, Object>, InitializingBean {
private volatile Object object;
private volatile String methodName;
private volatile MethodInvoker invoker;
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
public void setObject(Object object) {
this.object = object;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void afterPropertiesSet() {
synchronized (this.initializationMonitor) {
if (this.initialized) {
return;
}
if (this.object == null || this.methodName == null) {
throw new ConfigurationException("the 'object' and 'methodName' are required");
}
this.invoker = new NameResolvingMethodInvoker(this.object, this.methodName);
this.initialized = true;
}
}
public Object transform(Object payload) throws Exception {
return this.invokeMethod(payload);
if (!this.initialized) {
this.afterPropertiesSet();
}
return this.invoker.invokeMethod(payload);
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.util;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.integration.ConfigurationException;
/**
* A base class for adapters that invoke a specified method and target object.
* Either a {@link Method} reference or a 'methodName' may be provided, but both
* are not necessary. In fact, while preference is given to a {@link Method}
* reference if available, an Exception will be thrown if a non-matching
* 'methodName' has also been provided. Therefore, to avoid such ambiguity,
* it is recommended to provide just one or the other.
*
* @author Mark Fisher
*/
public abstract class AbstractMethodInvokingAdapter implements MethodInvoker, InitializingBean, Ordered {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile Object object;
private volatile Method method;
private volatile String methodName;
private volatile int order;
private volatile MethodInvoker invoker;
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
public void setObject(Object object) {
this.object = object;
}
protected Object getObject() {
return this.object;
}
public void setMethod(Method method) {
this.method = method;
}
protected Method getMethod() {
return this.method;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
protected String getMethodName() {
return this.methodName;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return order;
}
protected boolean isInitialized() {
return this.initialized;
}
public void afterPropertiesSet() {
synchronized (this.initializationMonitor) {
if (this.initialized) {
return;
}
if (this.object == null) {
throw new ConfigurationException("The target 'object' must not be null.");
}
if (this.method == null && this.methodName == null) {
throw new ConfigurationException("Either a 'method' or 'methodName' is required.");
}
if (this.method != null) {
if (this.methodName != null && !this.methodName.equals(this.method.getName())) {
throw new ConfigurationException("An ambiguity exists between the 'method' and 'methodName' properties. " +
"Note that only one of them is required, but if both are provided they must match.");
}
this.methodName = this.method.getName();
this.invoker = new DefaultMethodInvoker(this.object, this.method);
}
else {
this.invoker = new NameResolvingMethodInvoker(this.object, this.methodName);
}
this.initialized = true;
}
this.initialize();
}
/**
* Subclasses may override this method for custom initialization requirements.
*/
protected void initialize() {
}
public Object invokeMethod(Object ... args) throws Exception {
if (!this.initialized) {
this.afterPropertiesSet();
}
return this.invoker.invokeMethod(args);
}
}

View File

@@ -72,39 +72,26 @@ public class CorrelationIdTests {
}
@Test
public void testCorrelationNotPassedIfAlreadySetByHandler() throws Exception {
public void testCorrelationNotPassedFromRequestHeaderIfAlreadySetByHandler() throws Exception {
Object correlationId = "123-ABC";
Message<String> message = MessageBuilder.fromPayload("test")
.setCorrelationId(correlationId).build();
AbstractMessageHandlerAdapter adapter = new AbstractMessageHandlerAdapter() {
@Override
protected Message<?> handleReturnValue(Object returnValue, Message<?> originalMessage) {
Message<?> resultMessage = this.createReplyMessage(returnValue, originalMessage);
return MessageBuilder.fromMessage(resultMessage)
.setCorrelationId("456-XYZ").build();
}
};
adapter.setObject(new TestBean());
adapter.setMethodName("upperCase");
adapter.afterPropertiesSet();
Message<?> reply = adapter.handle(message);
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestBean());
handler.setMethodName("createMessage");
handler.afterPropertiesSet();
Message<?> reply = handler.handle(message);
assertEquals("456-XYZ", reply.getHeaders().getCorrelationId());
}
@Test
public void testCorrelationNotCopiedIfAlreadySetByHandler() throws Exception {
public void testCorrelationNotCopiedFromRequestMessgeIdIfAlreadySetByHandler() throws Exception {
Message<?> message = new StringMessage("test");
AbstractMessageHandlerAdapter adapter = new AbstractMessageHandlerAdapter() {
@Override
protected Message<?> handleReturnValue(Object returnValue, Message<?> originalMessage) {
Message<?> resultMessage = this.createReplyMessage(returnValue, originalMessage);
return MessageBuilder.fromMessage(resultMessage).setCorrelationId("456-XYZ").build();
}
};
adapter.setObject(new TestBean());
adapter.setMethodName("upperCase");
adapter.afterPropertiesSet();
Message<?> reply = adapter.handle(message);
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestBean());
handler.setMethodName("createMessage");
handler.afterPropertiesSet();
Message<?> reply = handler.handle(message);
assertEquals("456-XYZ", reply.getHeaders().getCorrelationId());
}
@@ -134,6 +121,10 @@ public class CorrelationIdTests {
public String[] split(String input) {
return input.split(",");
}
public Message<?> createMessage(String input) {
return MessageBuilder.fromPayload(input).setCorrelationId("456-XYZ").build();
}
}
}