Move JmsHandlerMethodFactory to spring-messaging

This commit moves JmsHandlerMethodFactory and its default
implementation to the messaging abstraction. Working on a similar
support for AMQP revealed that this factory has nothing that is JMS
specific and is exactly identical in the case of AMQP.

Issue: SPR-12053
This commit is contained in:
Stephane Nicoll
2014-08-01 16:08:31 +02:00
parent 25decee1a4
commit 7d1e33d88d
15 changed files with 148 additions and 104 deletions

View File

@@ -112,10 +112,10 @@ import org.springframework.context.annotation.Import;
* // process incoming message
* }</pre>
*
* These features are abstracted by the {@link org.springframework.jms.config.JmsHandlerMethodFactory
* JmsHandlerMethodFactory} that is responsible to build the necessary invoker to process
* the annotated method. By default, {@link org.springframework.jms.config.DefaultJmsHandlerMethodFactory
* DefaultJmsHandlerMethodFactory} is used.
* These features are abstracted by the {@link org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory
* MessageHandlerMethodFactory} that is responsible to build the necessary invoker to process
* the annotated method. By default, {@link org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory
* DefaultMessageHandlerMethodFactory} is used.
*
* <p>When more control is desired, a {@code @Configuration} class may implement
* {@link JmsListenerConfigurer}. This allows access to the underlying
@@ -172,7 +172,7 @@ import org.springframework.context.annotation.Import;
* &#064;Override
* public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
* registrar.setEndpointRegistry(myJmsListenerEndpointRegistry());
* registrar.setJmsHandlerMethodFactory(myJmsHandlerMethodFactory);
* registrar.setMessageHandlerMethodFactory(myJmsHandlerMethodFactory);
* }
*
* &#064;Bean
@@ -206,7 +206,7 @@ import org.springframework.context.annotation.Import;
* </bean>
*
* <bean id="myJmsHandlerMethodFactory"
* class="org.springframework.jms.config.DefaultJmsHandlerMethodFactory">
* class="org.springframework.messaging.handler.support.DefaultJmsHandlerMethodFactory">
* <property name="validator" ref="myValidator"/>
* </bean>
*

View File

@@ -31,13 +31,13 @@ import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.jms.config.DefaultJmsHandlerMethodFactory;
import org.springframework.jms.config.JmsHandlerMethodFactory;
import org.springframework.jms.config.JmsListenerConfigUtils;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerEndpointRegistrar;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.config.MethodJmsListenerEndpoint;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -86,7 +86,7 @@ public class JmsListenerAnnotationBeanPostProcessor
private BeanFactory beanFactory;
private final JmsHandlerMethodFactoryAdapter jmsHandlerMethodFactory = new JmsHandlerMethodFactoryAdapter();
private final MessageHandlerMethodFactoryAdapter messageHandlerMethodFactory = new MessageHandlerMethodFactoryAdapter();
private final JmsListenerEndpointRegistrar registrar = new JmsListenerEndpointRegistrar();
@@ -115,15 +115,15 @@ public class JmsListenerAnnotationBeanPostProcessor
}
/**
* Set the {@link JmsHandlerMethodFactory} to use to configure the message
* Set the {@link MessageHandlerMethodFactory} to use to configure the message
* listener responsible to serve an endpoint detected by this processor.
* <p>By default, {@link DefaultJmsHandlerMethodFactory} is used and it
* <p>By default, {@link DefaultMessageHandlerMethodFactory} is used and it
* can be configured further to support additional method arguments
* or to customize conversion and validation support. See
* {@link DefaultJmsHandlerMethodFactory} Javadoc for more details.
* {@link DefaultMessageHandlerMethodFactory} Javadoc for more details.
*/
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(jmsHandlerMethodFactory);
public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
}
/**
@@ -163,9 +163,9 @@ public class JmsListenerAnnotationBeanPostProcessor
}
// Set the custom handler method factory once resolved by the configurer
JmsHandlerMethodFactory handlerMethodFactory = this.registrar.getJmsHandlerMethodFactory();
MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory();
if (handlerMethodFactory != null) {
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(handlerMethodFactory);
this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(handlerMethodFactory);
}
// Actually register all listeners
@@ -216,7 +216,7 @@ public class JmsListenerAnnotationBeanPostProcessor
MethodJmsListenerEndpoint endpoint = new MethodJmsListenerEndpoint();
endpoint.setBean(bean);
endpoint.setMethod(method);
endpoint.setJmsHandlerMethodFactory(this.jmsHandlerMethodFactory);
endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
endpoint.setId(getEndpointId(jmsListener));
endpoint.setDestination(jmsListener.destination());
if (StringUtils.hasText(jmsListener.selector())) {
@@ -257,33 +257,33 @@ public class JmsListenerAnnotationBeanPostProcessor
/**
* An {@link JmsHandlerMethodFactory} adapter that offers a configurable underlying
* A {@link MessageHandlerMethodFactory} adapter that offers a configurable underlying
* instance to use. Useful if the factory to use is determined once the endpoints
* have been registered but not created yet.
* @see JmsListenerEndpointRegistrar#setJmsHandlerMethodFactory(JmsHandlerMethodFactory)
* @see JmsListenerEndpointRegistrar#setMessageHandlerMethodFactory
*/
private class JmsHandlerMethodFactoryAdapter implements JmsHandlerMethodFactory {
private class MessageHandlerMethodFactoryAdapter implements MessageHandlerMethodFactory {
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
private MessageHandlerMethodFactory messageHandlerMethodFactory;
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
this.messageHandlerMethodFactory = messageHandlerMethodFactory;
}
@Override
public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
return getJmsHandlerMethodFactory().createInvocableHandlerMethod(bean, method);
return getMessageHandlerMethodFactory().createInvocableHandlerMethod(bean, method);
}
private JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
if (this.jmsHandlerMethodFactory == null) {
this.jmsHandlerMethodFactory = createDefaultJmsHandlerMethodFactory();
private MessageHandlerMethodFactory getMessageHandlerMethodFactory() {
if (this.messageHandlerMethodFactory == null) {
this.messageHandlerMethodFactory = createDefaultJmsHandlerMethodFactory();
}
return this.jmsHandlerMethodFactory;
return this.messageHandlerMethodFactory;
}
private JmsHandlerMethodFactory createDefaultJmsHandlerMethodFactory() {
DefaultJmsHandlerMethodFactory defaultFactory = new DefaultJmsHandlerMethodFactory();
private MessageHandlerMethodFactory createDefaultJmsHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory defaultFactory = new DefaultMessageHandlerMethodFactory();
defaultFactory.setBeanFactory(beanFactory);
defaultFactory.afterPropertiesSet();
return defaultFactory;

View File

@@ -70,7 +70,7 @@ class AnnotationDrivenJmsBeanDefinitionParser implements BeanDefinitionParser {
String handlerMethodFactory = element.getAttribute("handler-method-factory");
if (StringUtils.hasText(handlerMethodFactory)) {
builder.addPropertyReference("jmsHandlerMethodFactory", handlerMethodFactory);
builder.addPropertyReference("messageHandlerMethodFactory", handlerMethodFactory);
}
registerInfrastructureBean(parserContext, builder,

View File

@@ -1,189 +0,0 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jms.config;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.messaging.converter.GenericMessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.support.HeaderMethodArgumentResolver;
import org.springframework.messaging.handler.annotation.support.HeadersMethodArgumentResolver;
import org.springframework.messaging.handler.annotation.support.MessageMethodArgumentResolver;
import org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* The default {@link JmsHandlerMethodFactory} implementation creating an
* {@link InvocableHandlerMethod} with the necessary
* {@link HandlerMethodArgumentResolver} instances to detect and process
* all the use cases defined by {@link org.springframework.jms.annotation.JmsListener
* JmsListener}.
*
* <p>Extra method argument resolvers can be added to customize the method
* signature that can be handled.
*
* <p>By default, the validation process redirects to a no-op implementation, see
* {@link #setValidator(Validator)} to customize it. The {@link ConversionService}
* can be customized in a similar manner to tune how the message payload
* can be converted
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
* @see #setConversionService
* @see #setValidator
* @see #setCustomArgumentResolvers
*/
public class DefaultJmsHandlerMethodFactory implements JmsHandlerMethodFactory, BeanFactoryAware, InitializingBean {
private ConversionService conversionService = new DefaultFormattingConversionService();
private MessageConverter messageConverter;
private Validator validator = new NoOpValidator();
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
private final HandlerMethodArgumentResolverComposite argumentResolvers =
new HandlerMethodArgumentResolverComposite();
private BeanFactory beanFactory;
/**
* Set the {@link ConversionService} to use to convert the original
* message payload or headers.
* @see HeaderMethodArgumentResolver
* @see GenericMessageConverter
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* Set the {@link MessageConverter} to use. By default a {@link GenericMessageConverter}
* is used.
* @see GenericMessageConverter
*/
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}
/**
* Set the Validator instance used for validating @Payload arguments
* @see org.springframework.validation.annotation.Validated
* @see org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver
*/
public void setValidator(Validator validator) {
this.validator = validator;
}
/**
* Set the list of custom {@code HandlerMethodArgumentResolver}s that will be used
* after resolvers for supported argument type.
* @param customArgumentResolvers the list of resolvers (never {@code null})
*/
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> customArgumentResolvers) {
this.customArgumentResolvers = customArgumentResolvers;
}
/**
* Configure the complete list of supported argument types effectively overriding
* the ones configured by default. This is an advanced option. For most use cases
* it should be sufficient to use {@link #setCustomArgumentResolvers(java.util.List)}.
*/
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers == null) {
this.argumentResolvers.clear();
return;
}
this.argumentResolvers.addResolvers(argumentResolvers);
}
/**
* A {@link BeanFactory} only needs to be available for placeholder resolution
* in handler method arguments; it's optional otherwise.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() {
if (this.messageConverter == null) {
this.messageConverter = new GenericMessageConverter(this.conversionService);
}
if (this.argumentResolvers.getResolvers().isEmpty()) {
this.argumentResolvers.addResolvers(initArgumentResolvers());
}
}
@Override
public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(bean, method);
handlerMethod.setMessageMethodArgumentResolvers(argumentResolvers);
return handlerMethod;
}
protected List<HandlerMethodArgumentResolver> initArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ?
(ConfigurableBeanFactory) this.beanFactory : null);
// Annotation-based argument resolution
resolvers.add(new HeaderMethodArgumentResolver(this.conversionService, cbf));
resolvers.add(new HeadersMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new MessageMethodArgumentResolver());
if (this.customArgumentResolvers != null) {
resolvers.addAll(this.customArgumentResolvers);
}
resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator));
return resolvers;
}
private static final class NoOpValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return false;
}
@Override
public void validate(Object target, Errors errors) {
}
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jms.config;
import java.lang.reflect.Method;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
/**
* A factory for {@link InvocableHandlerMethod} that is suitable to process
* an incoming JMS message.
*
* @author Stephane Nicoll
* @since 4.1
*/
public interface JmsHandlerMethodFactory {
/**
* Create the {@link InvocableHandlerMethod} that is able to process the specified
* JMS method endpoint.
* @param bean the bean instance
* @param method the method to invoke
* @return a JMS-specific {@link InvocableHandlerMethod} suitable for that method
*/
InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method);
}

View File

@@ -22,6 +22,8 @@ import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.util.Assert;
/**
@@ -37,7 +39,7 @@ public class JmsListenerEndpointRegistrar implements BeanFactoryAware, Initializ
private JmsListenerEndpointRegistry endpointRegistry;
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
private MessageHandlerMethodFactory messageHandlerMethodFactory;
private JmsListenerContainerFactory<?> containerFactory;
@@ -65,22 +67,22 @@ public class JmsListenerEndpointRegistrar implements BeanFactoryAware, Initializ
}
/**
* Set the {@link JmsHandlerMethodFactory} to use to configure the message
* Set the {@link MessageHandlerMethodFactory} to use to configure the message
* listener responsible to serve an endpoint detected by this processor.
* <p>By default, {@link DefaultJmsHandlerMethodFactory} is used and it
* <p>By default, {@link DefaultMessageHandlerMethodFactory} is used and it
* can be configured further to support additional method arguments
* or to customize conversion and validation support. See
* {@link DefaultJmsHandlerMethodFactory} javadoc for more details.
* {@link DefaultMessageHandlerMethodFactory} javadoc for more details.
*/
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
this.messageHandlerMethodFactory = messageHandlerMethodFactory;
}
/**
* Return the custom {@link JmsHandlerMethodFactory} to use, if any.
* Return the custom {@link MessageHandlerMethodFactory} to use, if any.
*/
public JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
return this.jmsHandlerMethodFactory;
public MessageHandlerMethodFactory getMessageHandlerMethodFactory() {
return this.messageHandlerMethodFactory;
}
/**

View File

@@ -25,6 +25,7 @@ import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -41,7 +42,7 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
private Method method;
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
private MessageHandlerMethodFactory messageHandlerMethodFactory;
/**
@@ -67,22 +68,22 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
}
/**
* Set the {@link JmsHandlerMethodFactory} to use to build the
* Set the {@link MessageHandlerMethodFactory} to use to build the
* {@link InvocableHandlerMethod} responsible to manage the invocation
* of this endpoint.
*/
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
this.messageHandlerMethodFactory = messageHandlerMethodFactory;
}
@Override
protected MessagingMessageListenerAdapter createMessageListener(MessageListenerContainer container) {
Assert.state(this.jmsHandlerMethodFactory != null,
"Could not create message listener - message listener factory not set");
Assert.state(this.messageHandlerMethodFactory != null,
"Could not create message listener - MessageHandlerMethodFactory not set");
MessagingMessageListenerAdapter messageListener = createMessageListenerInstance();
InvocableHandlerMethod invocableHandlerMethod =
this.jmsHandlerMethodFactory.createInvocableHandlerMethod(getBean(), getMethod());
this.messageHandlerMethodFactory.createInvocableHandlerMethod(getBean(), getMethod());
messageListener.setHandlerMethod(invocableHandlerMethod);
String responseDestination = getDefaultResponseDestination();
if (StringUtils.hasText(responseDestination)) {