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}