001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jms.listener.adapter;
018
019import javax.jms.JMSException;
020import javax.jms.Session;
021
022import org.springframework.core.MethodParameter;
023import org.springframework.jms.support.JmsHeaderMapper;
024import org.springframework.jms.support.converter.MessageConversionException;
025import org.springframework.messaging.Message;
026import org.springframework.messaging.MessagingException;
027import org.springframework.messaging.core.AbstractMessageSendingTemplate;
028import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
029import org.springframework.messaging.support.MessageBuilder;
030
031/**
032 * A {@link javax.jms.MessageListener} adapter that invokes a configurable
033 * {@link InvocableHandlerMethod}.
034 *
035 * <p>Wraps the incoming {@link javax.jms.Message} to Spring's {@link Message}
036 * abstraction, copying the JMS standard headers using a configurable
037 * {@link JmsHeaderMapper}.
038 *
039 * <p>The original {@link javax.jms.Message} and the {@link javax.jms.Session}
040 * are provided as additional arguments so that these can be injected as
041 * method arguments if necessary.
042 *
043 * @author Stephane Nicoll
044 * @since 4.1
045 * @see Message
046 * @see JmsHeaderMapper
047 * @see InvocableHandlerMethod
048 */
049public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageListener {
050
051        private InvocableHandlerMethod handlerMethod;
052
053
054        /**
055         * Set the {@link InvocableHandlerMethod} to use to invoke the method
056         * processing an incoming {@link javax.jms.Message}.
057         */
058        public void setHandlerMethod(InvocableHandlerMethod handlerMethod) {
059                this.handlerMethod = handlerMethod;
060        }
061
062
063        @Override
064        public void onMessage(javax.jms.Message jmsMessage, Session session) throws JMSException {
065                Message<?> message = toMessagingMessage(jmsMessage);
066                if (logger.isDebugEnabled()) {
067                        logger.debug("Processing [" + message + "]");
068                }
069                Object result = invokeHandler(jmsMessage, session, message);
070                if (result != null) {
071                        handleResult(result, jmsMessage, session);
072                }
073                else {
074                        logger.trace("No result object given - no result to handle");
075                }
076        }
077
078        @Override
079        protected Object preProcessResponse(Object result) {
080                MethodParameter returnType = this.handlerMethod.getReturnType();
081                if (result instanceof Message) {
082                        return MessageBuilder.fromMessage((Message<?>) result)
083                                        .setHeader(AbstractMessageSendingTemplate.CONVERSION_HINT_HEADER, returnType).build();
084                }
085                return MessageBuilder.withPayload(result).setHeader(
086                                AbstractMessageSendingTemplate.CONVERSION_HINT_HEADER, returnType).build();
087        }
088
089        protected Message<?> toMessagingMessage(javax.jms.Message jmsMessage) {
090                try {
091                        return (Message<?>) getMessagingMessageConverter().fromMessage(jmsMessage);
092                }
093                catch (JMSException ex) {
094                        throw new MessageConversionException("Could not convert JMS message", ex);
095                }
096        }
097
098        /**
099         * Invoke the handler, wrapping any exception to a {@link ListenerExecutionFailedException}
100         * with a dedicated error message.
101         */
102        private Object invokeHandler(javax.jms.Message jmsMessage, Session session, Message<?> message) {
103                try {
104                        return this.handlerMethod.invoke(message, jmsMessage, session);
105                }
106                catch (MessagingException ex) {
107                        throw new ListenerExecutionFailedException(
108                                        createMessagingErrorMessage("Listener method could not be invoked with incoming message"), ex);
109                }
110                catch (Exception ex) {
111                        throw new ListenerExecutionFailedException("Listener method '" +
112                                        this.handlerMethod.getMethod().toGenericString() + "' threw exception", ex);
113                }
114        }
115
116        private String createMessagingErrorMessage(String description) {
117                StringBuilder sb = new StringBuilder(description).append("\n")
118                                .append("Endpoint handler details:\n")
119                                .append("Method [").append(this.handlerMethod.getMethod()).append("]\n")
120                                .append("Bean [").append(this.handlerMethod.getBean()).append("]\n");
121                return sb.toString();
122        }
123
124}