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}