All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2015 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 io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.adapter;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.AmqpIOException;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.AmqpIllegalStateException;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Message;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.MessageListener;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.MessageProperties;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.support.converter.MessageConverter;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.support.converter.SimpleMessageConverter;
import io.bitsensor.plugins.shaded.org.springframework.util.Assert;
import io.bitsensor.plugins.shaded.org.springframework.util.MethodInvoker;
import io.bitsensor.plugins.shaded.org.springframework.util.ObjectUtils;
import io.bitsensor.plugins.shaded.org.springframework.util.StringUtils;

import io.bitsensor.plugins.shaded.com.rabbitmq.client.Channel;

/**
 * Message listener adapter that delegates the handling of messages to target listener methods via reflection, with
 * flexible message type conversion. Allows listener methods to operate on message content types, completely independent
 * from the Rabbit API.
 *
 * 

* By default, the content of incoming Rabbit messages gets extracted before being passed into the target listener * method, to let the target method operate on message content types such as String or byte array instead of the raw * {@link Message}. Message type conversion is delegated to a Spring AMQ {@link MessageConverter}. By default, a * {@link SimpleMessageConverter} will be used. (If you do not want such automatic message conversion taking place, then * be sure to set the {@link #setMessageConverter MessageConverter} to null.) * *

* If a target listener method returns a non-null object (typically of a message content type such as * String or byte array), it will get wrapped in a Rabbit Message and sent to the exchange of * the incoming message with the routingKey that comes from the Rabbit ReplyTo property or via * {@link #setResponseRoutingKey(String) specified routingKey}). * *

* Note: The sending of response messages is only available when using the {@link ChannelAwareMessageListener} * entry point (typically through a Spring message listener container). Usage as {@link MessageListener} does not * support the generation of response messages. * *

* Find below some examples of method signatures compliant with this adapter class. This first example handles all * Message types and gets passed the contents of each Message type as an argument. No * Message will be sent back as all of these methods return void. * *

 * public interface MessageContentsDelegate {
 * 	void handleMessage(String text);
 *
 * 	void handleMessage(Map map);
 *
 * 	void handleMessage(byte[] bytes);
 *
 * 	void handleMessage(Serializable obj);
 * }
 * 
* * This next example handle a Message type and gets passed the actual (raw) Message as an * argument. Again, no Message will be sent back as all of these methods return void. * *
 * public interface RawMessageDelegate {
 * 	void handleMessage(Message message);
 * }
 * 
* * This next example illustrates a Message delegate that just consumes the String contents of * {@link Message Messages}. Notice also how the name of the Message handling method is different from the * {@link #ORIGINAL_DEFAULT_LISTENER_METHOD original} (this will have to be configured in the attandant bean * definition). Again, no Message will be sent back as the method returns void. * *
 * public interface TextMessageContentDelegate {
 * 	void onMessage(String text);
 * }
 * 
* * This final example illustrates a Message delegate that just consumes the String contents of * {@link Message Messages}. Notice how the return type of this method is String: This will result in the * configured {@link MessageListenerAdapter} sending a {@link Message} in response. * *
 * public interface ResponsiveTextMessageContentDelegate {
 * 	String handleMessage(String text);
 * }
 * 
* * For further examples and discussion please do refer to the Spring reference documentation which describes this class * (and its attendant XML configuration) in detail. * * @author Juergen Hoeller * @author Mark Pollack * @author Mark Fisher * @author Dave Syer * @author Gary Russell * @author Greg Turnquist * * @see #setDelegate * @see #setDefaultListenerMethod * @see #setResponseRoutingKey(String) * @see #setMessageConverter * @see io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.support.converter.SimpleMessageConverter * @see io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.core.ChannelAwareMessageListener * @see io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer#setMessageListener */ public class MessageListenerAdapter extends AbstractAdaptableMessageListener { private final Map queueOrTagToMethodName = new HashMap(); /** * Out-of-the-box value for the default listener method: "handleMessage". */ public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = "handleMessage"; private Object delegate; private String defaultListenerMethod = ORIGINAL_DEFAULT_LISTENER_METHOD; /** * Create a new {@link MessageListenerAdapter} with default settings. */ public MessageListenerAdapter() { this.delegate = this; } /** * Create a new {@link MessageListenerAdapter} for the given delegate. * @param delegate the delegate object */ public MessageListenerAdapter(Object delegate) { doSetDelegate(delegate); } /** * Create a new {@link MessageListenerAdapter} for the given delegate. * @param delegate the delegate object * @param messageConverter the message converter to use */ public MessageListenerAdapter(Object delegate, MessageConverter messageConverter) { doSetDelegate(delegate); super.setMessageConverter(messageConverter); } /** * Create a new {@link MessageListenerAdapter} for the given delegate while also * declaring its POJO method. * @param delegate the delegate object * @param defaultListenerMethod name of the POJO method to call upon message receipt */ public MessageListenerAdapter(Object delegate, String defaultListenerMethod) { this(delegate); this.defaultListenerMethod = defaultListenerMethod; } /** * Set a target object to delegate message listening to. Specified listener methods have to be present on this * target object. *

If no explicit delegate object has been specified, listener methods are expected to present on this adapter * instance, that is, on a custom subclass of this adapter, defining listener methods. * @param delegate The delegate listener or POJO. */ public void setDelegate(Object delegate) { doSetDelegate(delegate); } private void doSetDelegate(Object delegate) { Assert.notNull(delegate, "Delegate must not be null"); this.delegate = delegate; } /** * @return The target object to delegate message listening to. */ protected Object getDelegate() { return this.delegate; } /** * Specify the name of the default listener method to delegate to, for the case where no specific listener method * has been determined. Out-of-the-box value is {@link #ORIGINAL_DEFAULT_LISTENER_METHOD "handleMessage"}. * @param defaultListenerMethod The name of the default listener method. * @see #getListenerMethodName */ public void setDefaultListenerMethod(String defaultListenerMethod) { this.defaultListenerMethod = defaultListenerMethod; } /** * @return The name of the default listener method to delegate to. */ protected String getDefaultListenerMethod() { return this.defaultListenerMethod; } /** * Set the mapping of queue name or consumer tag to method name. The first lookup * is by queue name, if that returns null, we lookup by consumer tag, if that * returns null, the {@link #setDefaultListenerMethod(String) defaultListenerMethod} * is used. * @param queueOrTagToMethodName the map. * @since 1.5 */ public void setQueueOrTagToMethodName(Map queueOrTagToMethodName) { this.queueOrTagToMethodName.putAll(queueOrTagToMethodName); } /** * Add the mapping of a queue name or consumer tag to a method name. The first lookup * is by queue name, if that returns null, we lookup by consumer tag, if that * returns null, the {@link #setDefaultListenerMethod(String) defaultListenerMethod} * is used. * @param queueOrTag The queue name or consumer tag. * @param methodName The method name. * @since 1.5 */ public void addQueueOrTagToMethodName(String queueOrTag, String methodName) { this.queueOrTagToMethodName.put(queueOrTag, methodName); } /** * Remove the mapping of a queue name or consumer tag to a method name. * @param queueOrTag The queue name or consumer tag. * @return the method name that was removed, or null. * @since 1.5 */ public String removeQueueOrTagToMethodName(String queueOrTag) { return this.queueOrTagToMethodName.remove(queueOrTag); } /** * Spring {@link ChannelAwareMessageListener} entry point. *

* Delegates the message to the target listener method, with appropriate conversion of the message argument. If the * target method returns a non-null object, wrap in a Rabbit message and send it back. * @param message the incoming Rabbit message * @param channel the Rabbit channel to operate on * @throws Exception if thrown by Rabbit API methods */ @Override public void onMessage(Message message, Channel channel) throws Exception { // Check whether the delegate is a MessageListener impl itself. // In that case, the adapter will simply act as a pass-through. Object delegate = getDelegate(); if (delegate != this) { if (delegate instanceof ChannelAwareMessageListener) { if (channel != null) { ((ChannelAwareMessageListener) delegate).onMessage(message, channel); return; } else if (!(delegate instanceof MessageListener)) { throw new AmqpIllegalStateException("MessageListenerAdapter cannot handle a " + "ChannelAwareMessageListener delegate if it hasn't been invoked with a Channel itself"); } } if (delegate instanceof MessageListener) { ((MessageListener) delegate).onMessage(message); return; } } // Regular case: find a handler method reflectively. Object convertedMessage = extractMessage(message); String methodName = getListenerMethodName(message, convertedMessage); if (methodName == null) { throw new AmqpIllegalStateException("No default listener method specified: " + "Either specify a non-null value for the 'defaultListenerMethod' property or " + "override the 'getListenerMethodName' method."); } // Invoke the handler method with appropriate arguments. Object[] listenerArguments = buildListenerArguments(convertedMessage); Object result = invokeListenerMethod(methodName, listenerArguments, message); if (result != null) { handleResult(result, message, channel); } else { logger.trace("No result object given - no result to handle"); } } /** * Determine the name of the listener method that will handle the given message. *

* The default implementation first consults the * {@link #setQueueOrTagToMethodName(Map) queueOrTagToMethodName} map looking for a * match on the consumer queue or consumer tag; if no match found, it simply returns * the configured default listener method, or "handleMessage" if not configured. * @param originalMessage the Rabbit request message * @param extractedMessage the converted Rabbit request message, to be passed into the * listener method as argument * @return the name of the listener method (never null) * @throws Exception if thrown by Rabbit API methods * @see #setDefaultListenerMethod * @see #setQueueOrTagToMethodName */ protected String getListenerMethodName(Message originalMessage, Object extractedMessage) throws Exception { if (this.queueOrTagToMethodName.size() > 0) { MessageProperties props = originalMessage.getMessageProperties(); String methodName = this.queueOrTagToMethodName.get(props.getConsumerQueue()); if (methodName == null) { methodName = this.queueOrTagToMethodName.get(props.getConsumerTag()); } if (methodName != null) { return methodName; } } return getDefaultListenerMethod(); } /** * Build an array of arguments to be passed into the target listener method. Allows for multiple method arguments to * be built from a single message object. *

* The default implementation builds an array with the given message object as sole element. This means that the * extracted message will always be passed into a single method argument, even if it is an array, with the * target method having a corresponding single argument of the array's type declared. *

* This can be overridden to treat special message content such as arrays differently, for example passing in each * element of the message array as distinct method argument. * @param extractedMessage the content of the message * @return the array of arguments to be passed into the listener method (each element of the array corresponding to * a distinct method argument) */ protected Object[] buildListenerArguments(Object extractedMessage) { return new Object[] {extractedMessage}; } /** * Invoke the specified listener method. * @param methodName the name of the listener method * @param arguments the message arguments to be passed in * @return the result returned from the listener method * @throws Exception if thrown by Rabbit API methods * @see #getListenerMethodName * @see #buildListenerArguments * @deprecated in favor of {@link #invokeListenerMethod(String, Object[], Message)} */ @Deprecated protected Object invokeListenerMethod(String methodName, Object[] arguments) throws Exception { return this.invokeListenerMethod(methodName, arguments, null); } /** * Invoke the specified listener method. * @param methodName the name of the listener method * @param arguments the message arguments to be passed in * @param originalMessage the original message * @return the result returned from the listener method * @throws Exception if thrown by Rabbit API methods * @see #getListenerMethodName * @see #buildListenerArguments */ protected Object invokeListenerMethod(String methodName, Object[] arguments, Message originalMessage) throws Exception { try { MethodInvoker methodInvoker = new MethodInvoker(); methodInvoker.setTargetObject(getDelegate()); methodInvoker.setTargetMethod(methodName); methodInvoker.setArguments(arguments); methodInvoker.prepare(); return methodInvoker.invoke(); } catch (InvocationTargetException ex) { Throwable targetEx = ex.getTargetException(); if (targetEx instanceof IOException) { throw new AmqpIOException((IOException) targetEx); } else { throw new ListenerExecutionFailedException("Listener method '" + methodName + "' threw exception", targetEx, originalMessage); } } catch (Exception ex) { ArrayList arrayClass = new ArrayList(); if (arguments != null) { for (Object argument : arguments) { arrayClass.add(argument.getClass().toString()); } } throw new ListenerExecutionFailedException("Failed to invoke target method '" + methodName + "' with argument type = [" + StringUtils.collectionToCommaDelimitedString(arrayClass) + "], value = [" + ObjectUtils.nullSafeToString(arguments) + "]", ex, originalMessage); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy