001/*
002 * Copyright 2002-2018 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.config;
018
019import java.lang.reflect.Method;
020import java.util.Arrays;
021
022import org.springframework.aop.framework.AopProxyUtils;
023import org.springframework.aop.support.AopUtils;
024import org.springframework.beans.factory.BeanFactory;
025import org.springframework.beans.factory.BeanFactoryAware;
026import org.springframework.beans.factory.config.ConfigurableBeanFactory;
027import org.springframework.beans.factory.config.EmbeddedValueResolver;
028import org.springframework.core.annotation.AnnotatedElementUtils;
029import org.springframework.jms.listener.MessageListenerContainer;
030import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter;
031import org.springframework.jms.support.converter.MessageConverter;
032import org.springframework.jms.support.destination.DestinationResolver;
033import org.springframework.messaging.handler.annotation.SendTo;
034import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
035import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
036import org.springframework.util.Assert;
037import org.springframework.util.StringUtils;
038import org.springframework.util.StringValueResolver;
039
040/**
041 * A {@link JmsListenerEndpoint} providing the method to invoke to process
042 * an incoming message for this endpoint.
043 *
044 * @author Stephane Nicoll
045 * @author Juergen Hoeller
046 * @since 4.1
047 */
048public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint implements BeanFactoryAware {
049
050        private Object bean;
051
052        private Method method;
053
054        private Method mostSpecificMethod;
055
056        private MessageHandlerMethodFactory messageHandlerMethodFactory;
057
058        private StringValueResolver embeddedValueResolver;
059
060
061        /**
062         * Set the actual bean instance to invoke this endpoint method on.
063         */
064        public void setBean(Object bean) {
065                this.bean = bean;
066        }
067
068        public Object getBean() {
069                return this.bean;
070        }
071
072        /**
073         * Set the method to invoke for processing a message managed by this endpoint.
074         */
075        public void setMethod(Method method) {
076                this.method = method;
077        }
078
079        public Method getMethod() {
080                return this.method;
081        }
082
083        /**
084         * Set the most specific method known for this endpoint's declaration.
085         * <p>In case of a proxy, this will be the method on the target class
086         * (if annotated itself, that is, if not just annotated in an interface).
087         * @since 4.2.3
088         */
089        public void setMostSpecificMethod(Method mostSpecificMethod) {
090                this.mostSpecificMethod = mostSpecificMethod;
091        }
092
093        public Method getMostSpecificMethod() {
094                if (this.mostSpecificMethod != null) {
095                        return this.mostSpecificMethod;
096                }
097                Method method = getMethod();
098                if (method != null) {
099                        Object bean = getBean();
100                        if (AopUtils.isAopProxy(bean)) {
101                                Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
102                                method = AopUtils.getMostSpecificMethod(method, targetClass);
103                        }
104                }
105                return method;
106        }
107
108        /**
109         * Set the {@link MessageHandlerMethodFactory} to use to build the
110         * {@link InvocableHandlerMethod} responsible to manage the invocation
111         * of this endpoint.
112         */
113        public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
114                this.messageHandlerMethodFactory = messageHandlerMethodFactory;
115        }
116
117        /**
118         * Set a value resolver for embedded placeholders and expressions.
119         */
120        public void setEmbeddedValueResolver(StringValueResolver embeddedValueResolver) {
121                this.embeddedValueResolver = embeddedValueResolver;
122        }
123
124        /**
125         * Set the {@link BeanFactory} to use to resolve expressions (may be {@code null}).
126         */
127        @Override
128        public void setBeanFactory(BeanFactory beanFactory) {
129                if (this.embeddedValueResolver == null && beanFactory instanceof ConfigurableBeanFactory) {
130                        this.embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory);
131                }
132        }
133
134
135        @Override
136        protected MessagingMessageListenerAdapter createMessageListener(MessageListenerContainer container) {
137                Assert.state(this.messageHandlerMethodFactory != null,
138                                "Could not create message listener - MessageHandlerMethodFactory not set");
139                MessagingMessageListenerAdapter messageListener = createMessageListenerInstance();
140                InvocableHandlerMethod invocableHandlerMethod =
141                                this.messageHandlerMethodFactory.createInvocableHandlerMethod(getBean(), getMethod());
142                messageListener.setHandlerMethod(invocableHandlerMethod);
143                String responseDestination = getDefaultResponseDestination();
144                if (StringUtils.hasText(responseDestination)) {
145                        if (container.isReplyPubSubDomain()) {
146                                messageListener.setDefaultResponseTopicName(responseDestination);
147                        }
148                        else {
149                                messageListener.setDefaultResponseQueueName(responseDestination);
150                        }
151                }
152                MessageConverter messageConverter = container.getMessageConverter();
153                if (messageConverter != null) {
154                        messageListener.setMessageConverter(messageConverter);
155                }
156                DestinationResolver destinationResolver = container.getDestinationResolver();
157                if (destinationResolver != null) {
158                        messageListener.setDestinationResolver(destinationResolver);
159                }
160                return messageListener;
161        }
162
163        /**
164         * Create an empty {@link MessagingMessageListenerAdapter} instance.
165         * @return a new {@code MessagingMessageListenerAdapter} or subclass thereof
166         */
167        protected MessagingMessageListenerAdapter createMessageListenerInstance() {
168                return new MessagingMessageListenerAdapter();
169        }
170
171        /**
172         * Return the default response destination, if any.
173         */
174        protected String getDefaultResponseDestination() {
175                Method specificMethod = getMostSpecificMethod();
176                SendTo ann = getSendTo(specificMethod);
177                if (ann != null) {
178                        Object[] destinations = ann.value();
179                        if (destinations.length != 1) {
180                                throw new IllegalStateException("Invalid @" + SendTo.class.getSimpleName() + " annotation on '" +
181                                                specificMethod + "' one destination must be set (got " + Arrays.toString(destinations) + ")");
182                        }
183                        return resolve((String) destinations[0]);
184                }
185                return null;
186        }
187
188        private SendTo getSendTo(Method specificMethod) {
189                SendTo ann = AnnotatedElementUtils.findMergedAnnotation(specificMethod, SendTo.class);
190                if (ann == null) {
191                        ann = AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), SendTo.class);
192                }
193                return ann;
194        }
195
196        private String resolve(String value) {
197                return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
198        }
199
200
201        @Override
202        protected StringBuilder getEndpointDescription() {
203                return super.getEndpointDescription()
204                                .append(" | bean='").append(this.bean).append("'")
205                                .append(" | method='").append(this.method).append("'");
206        }
207
208}