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)) {

View File

@@ -63,16 +63,16 @@
<xsd:attribute name="handler-method-factory" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies a custom org.springframework.jms.config.JmsHandlerMethodFactory instance to
use to configure the message listener responsible to serve an endpoint detected by this
processor. By default, DefaultJmsHandlerMethodFactory is used and it can be configured
Specifies a custom org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory
instance to use to configure the message listener responsible to serve an endpoint detected by this
processor. By default, DefaultMessageHandlerMethodFactory is used and it can be configured
further to support additional method arguments or to customize conversion and validation
support. See org.springframework.jms.config.DefaultJmsHandlerMethodFactory Javadoc for
more details.
support. See org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory
Javadoc for more details.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.config.JmsHandlerMethodFactory"/>
<tool:expected-type type="org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>