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