JMS annotation-driven endpoints.
This commit adds the support of JMS annotated endpoint. Can be activated both by @EnableJms or <jms:annotation-driven/> and detects methods of managed beans annotated with @JmsListener, either directly or through a meta-annotation. Containers are created and managed under the cover by a registry at application startup time. Container creation is delegated to a JmsListenerContainerFactory that is identified by the containerFactory attribute of the JmsListener annotation. Containers can be retrieved from the registry using a custom id that can be specified directly on the annotation. A "factory-id" attribute is available on the container element of the XML namespace. When it is present, the configuration defined at the namespace level is used to build a JmsListenerContainerFactory that is exposed with the value of the "factory-id" attribute. This can be used as a smooth migration path for users having listener containers defined at the namespace level. It is also possible to migrate all listeners to annotated endpoints and yet keep the <jms:listener-container> or <jms:jca-listener-container> element to share the container configuration. The configuration can be fine-tuned by implementing the JmsListenerConfigurer interface which gives access to the registrar used to register endpoints. This includes a programmatic registration of endpoints in complement to the declarative approach. A default JmsListenerContainerFactory can also be specified to be used if no containerFactory has been set on the annotation. Annotated methods can have flexible method arguments that are similar to what @MessageMapping provides. In particular, jms listener endpoint methods can fully use the messaging abstraction, including convenient header accessors. It is also possible to inject the raw javax.jms.Message and the Session for more advanced use cases. The payload can be injected as long as the conversion service is able to convert it from the original type of the JMS payload. By default, a DefaultJmsHandlerMethodFactory is used but it can be configured further to support additional method arguments or to customize conversion and validation support. The return type of an annotated method can also be an instance of Spring's Message abstraction. Instead of just converting the payload, such response type allows to communicate standard and custom headers. The JmsHeaderMapper infrastructure from Spring integration has also been migrated to the Spring framework. SimpleJmsHeaderMapper is based on SI's DefaultJmsHeaderMapper. The simple implementation maps all JMS headers so that the generated Message abstraction has all the information stored in the protocol specific message. Issue: SPR-9882
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Enable JMS listener annotated endpoints that are created under the cover
|
||||
* by a {@link org.springframework.jms.config.JmsListenerContainerFactory
|
||||
* JmsListenerContainerFactory}. To be used on
|
||||
* @{@link org.springframework.context.annotation.Configuration Configuration} classes
|
||||
* as follows:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableJms
|
||||
* public class AppConfig {
|
||||
* @Bean
|
||||
* public DefaultJmsListenerContainerFactory myJmsListenerContainerFactory() {
|
||||
* DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
|
||||
* factory.setConnectionFactory(connectionFactory());
|
||||
* factory.setDestinationResolver(destinationResolver());
|
||||
* factory.setConcurrency("5");
|
||||
* return factory;
|
||||
* }
|
||||
* // other @Bean definitions
|
||||
* }</pre>
|
||||
*
|
||||
* The {@code JmsListenerContainerFactory} is responsible to create the listener container
|
||||
* responsible for a particular endpoint. Typical implementations, as the
|
||||
* {@link org.springframework.jms.config.DefaultJmsListenerContainerFactory DefaultJmsListenerContainerFactory}
|
||||
* used in the sample above, provides the necessary configuration options that are supported by
|
||||
* the underlying {@link org.springframework.jms.listener.MessageListenerContainer MessageListenerContainer}.
|
||||
*
|
||||
* <p>{@code @EnableJms} enables detection of @{@link JmsListener} annotations on
|
||||
* any Spring-managed bean in the container. For example, given a class {@code MyService}
|
||||
*
|
||||
* <pre class="code">
|
||||
* package com.acme.foo;
|
||||
*
|
||||
* public class MyService {
|
||||
* @JmsListener(containerFactory = "myJmsListenerContainerFactory", destination="myQueue")
|
||||
* public void process(String msg) {
|
||||
* // process incoming message
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* The container factory to use is identified by the {@link JmsListener#containerFactory() containerFactory}
|
||||
* attribute defining the name of the {@code JmsListenerContainerFactory} bean to use.
|
||||
*
|
||||
* <p>the following configuration would ensure that every time a {@link javax.jms.Message}
|
||||
* is received on the {@link javax.jms.Destination} named "myQueue", {@code MyService.process()}
|
||||
* is called with the content of the message:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableJms
|
||||
* public class AppConfig {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* // JMS infrastructure setup
|
||||
* }</pre>
|
||||
*
|
||||
* Alternatively, if {@code MyService} were annotated with {@code @Component}, the
|
||||
* following configuration would ensure that its {@code @JmsListener} annotated
|
||||
* method is invoked with a matching incoming message:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableJms
|
||||
* @ComponentScan(basePackages="com.acme.foo")
|
||||
* public class AppConfig {
|
||||
* }</pre>
|
||||
*
|
||||
* Note that the created containers are not registered against the application context
|
||||
* but can be easily located for management purposes using the
|
||||
* {@link org.springframework.jms.config.JmsListenerEndpointRegistry JmsListenerEndpointRegistry}.
|
||||
*
|
||||
* <p>Annotated methods can use flexible signature; in particular, it is possible to use
|
||||
* the {@link org.springframework.messaging.Message Message} abstraction and related annotations,
|
||||
* see @{@link JmsListener} Javadoc for more details. For instance, the following would
|
||||
* inject the content of the message and a a custom "myCounter" JMS header:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @JmsListener(containerFactory = "myJmsListenerContainerFactory", destination="myQueue")
|
||||
* public void process(String msg, @Header("myCounter") int counter) {
|
||||
* // 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.
|
||||
*
|
||||
* <p>When more control is desired, a {@code @Configuration} class may implement
|
||||
* {@link JmsListenerConfigurer}. This allows access to the underlying
|
||||
* {@link org.springframework.jms.config.JmsListenerEndpointRegistrar JmsListenerEndpointRegistrar}
|
||||
* instance. The following example demonstrates how to specify a default
|
||||
* {@code JmsListenerContainerFactory} so that {@link JmsListener#containerFactory()} may be
|
||||
* omitted for endpoints willing to use the <em>default</em> container factory.
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableJms
|
||||
* public class AppConfig implements JmsListenerConfigurer {
|
||||
* @Override
|
||||
* public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
|
||||
* registrar.setDefaultContainerFactory(myJmsListenerContainerFactory());
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public JmsListenerContainerFactory<?> myJmsListenerContainerFactory() {
|
||||
* // factory settings
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* For reference, the example above can be compared to the following Spring XML
|
||||
* configuration:
|
||||
* <pre class="code">
|
||||
* {@code <beans>
|
||||
* <jms:annotation-driven default-container-factory="myJmsListenerContainerFactory"/>
|
||||
*
|
||||
* <bean id="myJmsListenerContainerFactory"
|
||||
* class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
|
||||
* // factory settings
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="myService" class="com.acme.foo.MyService"/>
|
||||
* </beans>
|
||||
* }</pre>
|
||||
*
|
||||
* It is also possible to specify a custom {@link org.springframework.jms.config.JmsListenerEndpointRegistry
|
||||
* JmsListenerEndpointRegistry} in case you need more control on the way the containers
|
||||
* are created and managed. The example below also demonstrates how to customize the
|
||||
* {@code JmsHandlerMethodFactory} to use with a custom {@link org.springframework.validation.Validator
|
||||
* Validator} so that payloads annotated with {@link org.springframework.validation.annotation.Validated
|
||||
* @Validated} are first validated against a custom {@code Validator}.
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableJms
|
||||
* public class AppConfig implements JmsListenerConfigurer {
|
||||
* @Override
|
||||
* public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
|
||||
* registrar.setEndpointRegistry(myJmsListenerEndpointRegistry());
|
||||
* registrar.setJmsHandlerMethodFactory(myJmsHandlerMethodFactory);
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public JmsListenerEndpointRegistry<?> myJmsListenerEndpointRegistry() {
|
||||
* // registry configuration
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public JmsHandlerMethodFactory myJmsHandlerMethodFactory() {
|
||||
* DefaultJmsHandlerMethodFactory factory = new DefaultJmsHandlerMethodFactory();
|
||||
* factory.setValidator(new MyValidator());
|
||||
* return factory;
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* For reference, the example above can be compared to the following Spring XML
|
||||
* configuration:
|
||||
* <pre class="code">
|
||||
* {@code <beans>
|
||||
* <jms:annotation-driven registry="myJmsListenerEndpointRegistry"
|
||||
* handler-method-factory="myJmsHandlerMethodFactory"/>
|
||||
*
|
||||
* <bean id="myJmsListenerEndpointRegistry"
|
||||
* class="org.springframework.jms.config.JmsListenerEndpointRegistry">
|
||||
* // registry configuration
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="myJmsHandlerMethodFactory"
|
||||
* class="org.springframework.jms.config.DefaultJmsHandlerMethodFactory">
|
||||
* <property name="validator" ref="myValidator"/>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="myService" class="com.acme.foo.MyService"/>
|
||||
* </beans>
|
||||
* }</pre>
|
||||
*
|
||||
* Implementing {@code JmsListenerConfigurer} also allows for fine-grained
|
||||
* control over endpoints registration via the {@code JmsListenerEndpointRegistrar}.
|
||||
* For example, the following configures an extra endpoint:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableJms
|
||||
* public class AppConfig implements JmsListenerConfigurer {
|
||||
* @Override
|
||||
* public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
|
||||
* SimpleJmsListenerEndpoint myEndpoint = new SimpleJmsListenerEndpoint();
|
||||
* // ... configure the endpoint
|
||||
* registrar.registerEndpoint(endpoint, anotherJmsListenerContainerFactory());
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public JmsListenerContainerFactory<?> anotherJmsListenerContainerFactory() {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // JMS infrastructure setup
|
||||
* }</pre>
|
||||
*
|
||||
* Note that all beans implementing {@code JmsListenerConfigurer} will be detected and
|
||||
* invoked in a similar fashion. The example above can be translated in a regular bean
|
||||
* definition registered in the context in case you use the XML configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JmsListener
|
||||
* @see JmsListenerAnnotationBeanPostProcessor
|
||||
* @see org.springframework.jms.config.JmsListenerEndpointRegistrar
|
||||
* @see org.springframework.jms.config.JmsListenerEndpointRegistry
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(JmsBootstrapConfiguration.class)
|
||||
public @interface EnableJms {
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.jms.config.JmsListenerEndpointRegistry;
|
||||
|
||||
/**
|
||||
* {@code @Configuration} class that registers a {@link JmsListenerAnnotationBeanPostProcessor}
|
||||
* bean capable of processing Spring's @{@link JmsListener} annotation. Also register
|
||||
* a default {@link JmsListenerEndpointRegistry}.
|
||||
*
|
||||
* <p>This configuration class is automatically imported when using the @{@link EnableJms}
|
||||
* annotation. See {@link EnableJms} Javadoc for complete usage.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JmsListenerAnnotationBeanPostProcessor
|
||||
* @see JmsListenerEndpointRegistry
|
||||
* @see EnableJms
|
||||
*/
|
||||
@Configuration
|
||||
public class JmsBootstrapConfiguration {
|
||||
|
||||
@Bean(name = AnnotationConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JmsListenerAnnotationBeanPostProcessor jmsListenerAnnotationProcessor() {
|
||||
return new JmsListenerAnnotationBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean(name = AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
|
||||
public JmsListenerEndpointRegistry defaultJmsListenerEndpointRegistry() {
|
||||
return new JmsListenerEndpointRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
|
||||
/**
|
||||
* Annotation that marks a method to be the target of a JMS message
|
||||
* listener on the specified {@link #destination()}. The {@link #containerFactory()}
|
||||
* identifies the {@link org.springframework.jms.config.JmsListenerContainerFactory
|
||||
* JmsListenerContainerFactory} to use to build the jms listener container. It may
|
||||
* be omitted as long as a <em>default</em> container factory has been defined.
|
||||
*
|
||||
* <p>Processing of {@code @JmsListener} annotations is performed by
|
||||
* registering a {@link JmsListenerAnnotationBeanPostProcessor}. This can be
|
||||
* done manually or, more conveniently, through the {@code <jms:annotation-driven/>}
|
||||
* element or {@link EnableJms} annotation.
|
||||
*
|
||||
* <p>Annotated methods are allowed to have flexible signatures similar to what
|
||||
* {@link MessageMapping} provides, that is
|
||||
* <ul>
|
||||
* <li>{@link javax.jms.Session} to get access to the JMS session</li>
|
||||
* <li>{@link javax.jms.Message} or one if subclass to get access to the raw JMS message</li>
|
||||
* <li>{@link org.springframework.messaging.Message} to use the messaging abstraction counterpart</li>
|
||||
* <li>{@link org.springframework.messaging.handler.annotation.Payload @Payload}-annotated method
|
||||
* arguments including the support of validation</li>
|
||||
* <li>{@link org.springframework.messaging.handler.annotation.Header @Header}-annotated method
|
||||
* arguments to extract a specific header value, including standard JMS headers defined by
|
||||
* {@link org.springframework.jms.support.converter.JmsHeaders JmsHeaders}</li>
|
||||
* <li>{@link org.springframework.messaging.handler.annotation.Headers @Headers}-annotated
|
||||
* argument that must also be assignable to {@link java.util.Map} for getting access to all
|
||||
* headers.</li>
|
||||
* <li>{@link org.springframework.messaging.MessageHeaders MessageHeaders} arguments for
|
||||
* getting access to all headers.</li>
|
||||
* <li>{@link org.springframework.messaging.support.MessageHeaderAccessor MessageHeaderAccessor}
|
||||
* or {@link org.springframework.jms.support.JmsMessageHeaderAccessor JmsMessageHeaderAccessor}
|
||||
* for convenient access to all method arguments.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Annotated method may have a non {@code void} return type. When they do, the result of the
|
||||
* method invocation is sent as a JMS reply to the destination defined by either the
|
||||
* {@code JMSReplyTO} header of the incoming message or the value of {@link #responseDestination()}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see EnableJms
|
||||
* @see JmsListenerAnnotationBeanPostProcessor
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@MessageMapping
|
||||
@Documented
|
||||
public @interface JmsListener {
|
||||
|
||||
/**
|
||||
* The unique identifier of the container managing this endpoint.
|
||||
* <p>if none is specified an auto-generated one is provided.
|
||||
* @see org.springframework.jms.config.JmsListenerEndpointRegistry#getContainer(String)
|
||||
*/
|
||||
String id() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the {@link org.springframework.jms.config.JmsListenerContainerFactory}
|
||||
* to use to create the message listener container responsible to serve this endpoint.
|
||||
* <p>If not specified, the default container factory is used, if any.
|
||||
*/
|
||||
String containerFactory() default "";
|
||||
|
||||
/**
|
||||
* The destination name for this listener, resolved through the container-wide
|
||||
* {@link org.springframework.jms.support.destination.DestinationResolver} strategy.
|
||||
*/
|
||||
String destination();
|
||||
|
||||
/**
|
||||
* Specify if the {@link #destination()} refers to a queue or not. Refer to a
|
||||
* queue by default.
|
||||
*/
|
||||
boolean queue() default true;
|
||||
|
||||
/**
|
||||
* The name for the durable subscription, if any.
|
||||
*/
|
||||
String subscription() default "";
|
||||
|
||||
/**
|
||||
* The JMS message selector expression, if any
|
||||
* <p>See the JMS specification for a detailed definition of selector expressions.
|
||||
*/
|
||||
String selector() default "";
|
||||
|
||||
/**
|
||||
* The name of the default response destination to send response messages to.
|
||||
* <p>This will be applied in case of a request message that does not carry
|
||||
* a "JMSReplyTo" field. The type of this destination will be determined
|
||||
* by the listener-container's "destination-type" attribute.
|
||||
* <p>Note: This only applies to a listener method with a return value,
|
||||
* for which each result object will be converted into a response message.
|
||||
*/
|
||||
String responseDestination() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
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.JmsListenerContainerFactory;
|
||||
import org.springframework.jms.config.JmsListenerEndpointRegistrar;
|
||||
import org.springframework.jms.config.JmsListenerEndpointRegistry;
|
||||
import org.springframework.jms.config.MethodJmsListenerEndpoint;
|
||||
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Bean post-processor that registers methods annotated with @{@link JmsListener}
|
||||
* to be invoked by a JMS message listener container created under the cover
|
||||
* by a {@link org.springframework.jms.config.JmsListenerContainerFactory} according
|
||||
* to the parameters of the annotation.
|
||||
*
|
||||
* <p>Annotated methods can use flexible arguments as defined by @{@link JmsListener}.
|
||||
*
|
||||
* <p>This post-processor is automatically registered by Spring's
|
||||
* {@code <jms:annotation-driven>} XML element, and also by the @{@link EnableJms}
|
||||
* annotation.
|
||||
*
|
||||
* <p>Auto-detect any {@link JmsListenerConfigurer} instances in the container,
|
||||
* allowing for customization of the registry to be used, the default container
|
||||
* factory or for fine-grained control over endpoints registration. See
|
||||
* @{@link EnableJms} Javadoc for complete usage details.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JmsListener
|
||||
* @see EnableJms
|
||||
* @see JmsListenerConfigurer
|
||||
* @see JmsListenerEndpointRegistrar
|
||||
* @see JmsListenerEndpointRegistry
|
||||
* @see org.springframework.jms.config.AbstractJmsListenerEndpoint
|
||||
* @see MethodJmsListenerEndpoint
|
||||
* @see MessageListenerFactory
|
||||
*/
|
||||
public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
|
||||
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private JmsListenerEndpointRegistry endpointRegistry;
|
||||
|
||||
private JmsListenerContainerFactory<?> defaultContainerFactory;
|
||||
|
||||
private final JmsHandlerMethodFactoryAdapter jmsHandlerMethodFactory = new JmsHandlerMethodFactoryAdapter();
|
||||
|
||||
private final JmsListenerEndpointRegistrar registrar = new JmsListenerEndpointRegistrar();
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link JmsListenerEndpointRegistry} that will hold the created
|
||||
* endpoint and manage the lifecycle of the related listener container.
|
||||
*/
|
||||
public void setEndpointRegistry(JmsListenerEndpointRegistry endpointRegistry) {
|
||||
this.endpointRegistry = endpointRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default {@link JmsListenerContainerFactory} to use in case a
|
||||
* {@link JmsListener} does not define any.
|
||||
* {@linkplain JmsListener#containerFactory() containerFactory}
|
||||
*/
|
||||
public void setDefaultContainerFactory(JmsListenerContainerFactory<?> defaultContainerFactory) {
|
||||
this.defaultContainerFactory = defaultContainerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link JmsHandlerMethodFactory} 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
|
||||
* can be configured further to support additional method arguments
|
||||
* or to customize conversion and validation support. See
|
||||
* {@link DefaultJmsHandlerMethodFactory} Javadoc for more details.
|
||||
*/
|
||||
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
|
||||
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(jmsHandlerMethodFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
JmsListener jmsListener = AnnotationUtils.getAnnotation(method, JmsListener.class);
|
||||
if (jmsListener != null) {
|
||||
processJmsListener(jmsListener, method, bean);
|
||||
}
|
||||
}
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
||||
protected void processJmsListener(JmsListener jmsListener, Method method, Object bean) {
|
||||
if (AopUtils.isJdkDynamicProxy(bean)) {
|
||||
try {
|
||||
// Found a @JmsListener method on the target class for this JDK proxy ->
|
||||
// is it also present on the proxy itself?
|
||||
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
}
|
||||
catch (SecurityException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"@JmsListener method '%s' found on bean target class '%s', " +
|
||||
"but not found in any interface(s) for bean JDK proxy. Either " +
|
||||
"pull the method up to an interface or switch to subclass (CGLIB) " +
|
||||
"proxies by setting proxy-target-class/proxyTargetClass " +
|
||||
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
MethodJmsListenerEndpoint endpoint = new MethodJmsListenerEndpoint();
|
||||
endpoint.setBean(bean);
|
||||
endpoint.setMethod(method);
|
||||
endpoint.setJmsHandlerMethodFactory(jmsHandlerMethodFactory);
|
||||
endpoint.setId(getEndpointId(jmsListener));
|
||||
endpoint.setDestination(jmsListener.destination());
|
||||
endpoint.setQueue(jmsListener.queue());
|
||||
if (StringUtils.hasText(jmsListener.selector())) {
|
||||
endpoint.setSelector(jmsListener.selector());
|
||||
}
|
||||
if (StringUtils.hasText(jmsListener.subscription())) {
|
||||
endpoint.setSubscription(jmsListener.subscription());
|
||||
}
|
||||
if (StringUtils.hasText(jmsListener.responseDestination())) {
|
||||
endpoint.setResponseDestination(jmsListener.responseDestination());
|
||||
}
|
||||
|
||||
JmsListenerContainerFactory<?> factory = null;
|
||||
String containerFactoryBeanName = jmsListener.containerFactory();
|
||||
if (StringUtils.hasText(containerFactoryBeanName)) {
|
||||
try {
|
||||
factory = applicationContext.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
throw new BeanInitializationException("Could not register jms listener endpoint on ["
|
||||
+ method + "], no " + JmsListenerContainerFactory.class.getSimpleName() + " with id '"
|
||||
+ containerFactoryBeanName + "' was found in the application context", e);
|
||||
}
|
||||
}
|
||||
|
||||
registrar.registerEndpoint(endpoint, factory);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (event.getApplicationContext() != this.applicationContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, JmsListenerConfigurer> instances =
|
||||
this.applicationContext.getBeansOfType(JmsListenerConfigurer.class);
|
||||
for (JmsListenerConfigurer configurer : instances.values()) {
|
||||
configurer.configureJmsListeners(registrar);
|
||||
}
|
||||
if (registrar.getEndpointRegistry() == null) {
|
||||
if (endpointRegistry == null) {
|
||||
endpointRegistry = applicationContext
|
||||
.getBean(AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
|
||||
JmsListenerEndpointRegistry.class);
|
||||
}
|
||||
registrar.setEndpointRegistry(endpointRegistry);
|
||||
}
|
||||
if (registrar.getDefaultContainerFactory() == null && defaultContainerFactory != null) {
|
||||
registrar.setDefaultContainerFactory(defaultContainerFactory);
|
||||
}
|
||||
|
||||
JmsHandlerMethodFactory handlerMethodFactory = registrar.getJmsHandlerMethodFactory();
|
||||
if (handlerMethodFactory != null) {
|
||||
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(handlerMethodFactory);
|
||||
}
|
||||
|
||||
// Create all the listeners and starts them
|
||||
try {
|
||||
registrar.afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new BeanInitializationException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getEndpointId(JmsListener jmsListener) {
|
||||
if (StringUtils.hasText(jmsListener.id())) {
|
||||
return jmsListener.id();
|
||||
}
|
||||
else {
|
||||
return "org.springframework.jms.JmsListenerEndpointContainer#"
|
||||
+ counter.getAndIncrement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link JmsHandlerMethodFactory} 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)
|
||||
*/
|
||||
private class JmsHandlerMethodFactoryAdapter implements JmsHandlerMethodFactory {
|
||||
|
||||
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
|
||||
|
||||
private void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
|
||||
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
|
||||
return getJmsHandlerMethodFactory().createInvocableHandlerMethod(bean, method);
|
||||
}
|
||||
|
||||
private JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
|
||||
if (jmsHandlerMethodFactory == null) {
|
||||
jmsHandlerMethodFactory= createDefaultJmsHandlerMethodFactory();
|
||||
}
|
||||
return jmsHandlerMethodFactory;
|
||||
}
|
||||
|
||||
private JmsHandlerMethodFactory createDefaultJmsHandlerMethodFactory() {
|
||||
DefaultJmsHandlerMethodFactory defaultFactory = new DefaultJmsHandlerMethodFactory();
|
||||
defaultFactory.setApplicationContext(applicationContext);
|
||||
defaultFactory.afterPropertiesSet();
|
||||
return defaultFactory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import org.springframework.jms.config.JmsListenerEndpointRegistrar;
|
||||
|
||||
/**
|
||||
* Optional interface to be implemented by Spring managed bean willing
|
||||
* to customize how JMS listener endpoints are configured. Typically
|
||||
* used to defined the default {@link org.springframework.jms.config.JmsListenerContainerFactory
|
||||
* JmsListenerContainerFactory} to use or for registering jms endpoints
|
||||
* in a <em>programmatic</em> fashion as opposed to the <em>declarative</em>
|
||||
* approach of using the @{@link JmsListener} annotation.
|
||||
*
|
||||
* <p>See @{@link EnableJms} for detailed usage examples.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see EnableJms
|
||||
* @see JmsListenerEndpointRegistrar
|
||||
*/
|
||||
public interface JmsListenerConfigurer {
|
||||
|
||||
/**
|
||||
* Callback allowing a {@link org.springframework.jms.config.JmsListenerEndpointRegistry
|
||||
* JmsListenerEndpointRegistry} and specific {@link org.springframework.jms.config.AbstractJmsListenerEndpoint
|
||||
* JmsListenerEndpoint} instances to be registered against the given
|
||||
* {@link JmsListenerEndpointRegistrar}. The default
|
||||
* {@link org.springframework.jms.config.JmsListenerContainerFactory JmsListenerContainerFactory}
|
||||
* can also be customized.
|
||||
* @param registrar the registrar to be configured
|
||||
*/
|
||||
void configureJmsListeners(JmsListenerEndpointRegistrar registrar);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Annotations and supporting classes for declarative jms listener
|
||||
* endpoint
|
||||
*/
|
||||
package org.springframework.jms.annotation;
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 javax.jms.ConnectionFactory;
|
||||
|
||||
import org.springframework.jms.listener.AbstractMessageListenerContainer;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Base {@link JmsListenerContainerFactory} for Spring's base container implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see AbstractMessageListenerContainer
|
||||
*/
|
||||
public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMessageListenerContainer>
|
||||
implements JmsListenerContainerFactory<C> {
|
||||
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
private DestinationResolver destinationResolver;
|
||||
|
||||
private ErrorHandler errorHandler;
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
private Boolean sessionTransacted;
|
||||
|
||||
private Integer sessionAcknowledgeMode;
|
||||
|
||||
private Boolean pubSubDomain;
|
||||
|
||||
private Boolean subscriptionDurable;
|
||||
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setConnectionFactory(ConnectionFactory)
|
||||
*/
|
||||
public void setConnectionFactory(ConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setDestinationResolver(DestinationResolver)
|
||||
*/
|
||||
public void setDestinationResolver(DestinationResolver destinationResolver) {
|
||||
this.destinationResolver = destinationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setErrorHandler(ErrorHandler)
|
||||
*/
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setMessageConverter(MessageConverter)
|
||||
*/
|
||||
public void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setSessionTransacted(boolean)
|
||||
*/
|
||||
public void setSessionTransacted(Boolean sessionTransacted) {
|
||||
this.sessionTransacted = sessionTransacted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setSessionAcknowledgeMode(int)
|
||||
*/
|
||||
public void setSessionAcknowledgeMode(Integer sessionAcknowledgeMode) {
|
||||
this.sessionAcknowledgeMode = sessionAcknowledgeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setPubSubDomain(boolean)
|
||||
*/
|
||||
public void setPubSubDomain(Boolean pubSubDomain) {
|
||||
this.pubSubDomain = pubSubDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setSubscriptionDurable(boolean)
|
||||
*/
|
||||
public void setSubscriptionDurable(Boolean subscriptionDurable) {
|
||||
this.subscriptionDurable = subscriptionDurable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractMessageListenerContainer#setClientId(String)
|
||||
*/
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty container instance.
|
||||
*/
|
||||
protected abstract C createContainerInstance();
|
||||
|
||||
@Override
|
||||
public C createMessageListenerContainer(JmsListenerEndpoint endpoint) {
|
||||
C instance = createContainerInstance();
|
||||
|
||||
if (this.connectionFactory != null) {
|
||||
instance.setConnectionFactory(this.connectionFactory);
|
||||
}
|
||||
if (this.destinationResolver != null) {
|
||||
instance.setDestinationResolver(this.destinationResolver);
|
||||
}
|
||||
if (this.errorHandler != null) {
|
||||
instance.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
if (this.messageConverter != null) {
|
||||
instance.setMessageConverter(this.messageConverter);
|
||||
}
|
||||
|
||||
if (this.sessionTransacted != null) {
|
||||
instance.setSessionTransacted(this.sessionTransacted);
|
||||
}
|
||||
if (this.sessionAcknowledgeMode != null) {
|
||||
instance.setSessionAcknowledgeMode(this.sessionAcknowledgeMode);
|
||||
}
|
||||
|
||||
if (this.pubSubDomain != null) {
|
||||
instance.setPubSubDomain(this.pubSubDomain);
|
||||
}
|
||||
if (this.subscriptionDurable != null) {
|
||||
instance.setSubscriptionDurable(this.subscriptionDurable);
|
||||
}
|
||||
if (this.clientId != null) {
|
||||
instance.setClientId(this.clientId);
|
||||
}
|
||||
|
||||
endpoint.setupMessageContainer(instance);
|
||||
|
||||
initializeContainer(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Further initialize the specified container.
|
||||
* <p>Subclasses can inherit from this method to apply extra
|
||||
* configuration if necessary.
|
||||
*/
|
||||
protected void initializeContainer(C instance) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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 javax.jms.MessageListener;
|
||||
|
||||
import org.springframework.jms.listener.AbstractMessageListenerContainer;
|
||||
import org.springframework.jms.listener.MessageListenerContainer;
|
||||
import org.springframework.jms.listener.endpoint.JmsActivationSpecConfig;
|
||||
import org.springframework.jms.listener.endpoint.JmsMessageEndpointManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base model for a JMS listener endpoint
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see MethodJmsListenerEndpoint
|
||||
* @see SimpleJmsListenerEndpoint
|
||||
*/
|
||||
public abstract class AbstractJmsListenerEndpoint implements JmsListenerEndpoint {
|
||||
|
||||
private String id;
|
||||
|
||||
private String destination;
|
||||
|
||||
private boolean queue = true;
|
||||
|
||||
private String subscription;
|
||||
|
||||
private String selector;
|
||||
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the destination for this endpoint.
|
||||
*/
|
||||
public String getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the destination for this endpoint.
|
||||
*/
|
||||
public void setDestination(String destination) {
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the destination is a queue.
|
||||
*/
|
||||
public boolean isQueue() {
|
||||
return queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the destination is a queue.
|
||||
*/
|
||||
public void setQueue(boolean queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for the durable subscription, if any.
|
||||
*/
|
||||
public String getSubscription() {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name for the durable subscription.
|
||||
*/
|
||||
public void setSubscription(String subscription) {
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JMS message selector expression, if any.
|
||||
* <p>See the JMS specification for a detailed definition of selector expressions.
|
||||
*/
|
||||
public String getSelector() {
|
||||
return selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JMS message selector expression.
|
||||
*/
|
||||
public void setSelector(String selector) {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupMessageContainer(MessageListenerContainer container) {
|
||||
if (container instanceof AbstractMessageListenerContainer) { // JMS
|
||||
setupJmsMessageContainer((AbstractMessageListenerContainer) container);
|
||||
}
|
||||
else if (container instanceof JmsMessageEndpointManager) { // JCA
|
||||
setupJcaMessageContainer((JmsMessageEndpointManager) container);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Could not configure endpoint with the specified container '"
|
||||
+ container + "' Only JMS (" + AbstractMessageListenerContainer.class.getName()
|
||||
+ " subclass) or JCA (" + JmsMessageEndpointManager.class.getName() + ") are supported.");
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupJmsMessageContainer(AbstractMessageListenerContainer container) {
|
||||
if (getDestination() != null) {
|
||||
container.setDestinationName(getDestination());
|
||||
}
|
||||
if (getSubscription() != null) {
|
||||
container.setDurableSubscriptionName(getSubscription());
|
||||
}
|
||||
if (getSelector() != null) {
|
||||
container.setMessageSelector(getSelector());
|
||||
}
|
||||
container.setPubSubDomain(!isQueue());
|
||||
setupMessageListener(container);
|
||||
}
|
||||
|
||||
protected void setupJcaMessageContainer(JmsMessageEndpointManager container) {
|
||||
JmsActivationSpecConfig activationSpecConfig = container.getActivationSpecConfig();
|
||||
if (activationSpecConfig == null) {
|
||||
activationSpecConfig = new JmsActivationSpecConfig();
|
||||
container.setActivationSpecConfig(activationSpecConfig);
|
||||
}
|
||||
|
||||
if (getDestination() != null) {
|
||||
activationSpecConfig.setDestinationName(getDestination());
|
||||
}
|
||||
if (getSubscription() != null) {
|
||||
activationSpecConfig.setDurableSubscriptionName(getSubscription());
|
||||
}
|
||||
if (getSelector() != null) {
|
||||
activationSpecConfig.setMessageSelector(getSelector());
|
||||
}
|
||||
activationSpecConfig.setPubSubDomain(!isQueue());
|
||||
setupMessageListener(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link MessageListener} that is able to serve this endpoint for the
|
||||
* specified container.
|
||||
*/
|
||||
protected abstract MessageListener createMessageListener(MessageListenerContainer container);
|
||||
|
||||
private void setupMessageListener(MessageListenerContainer container) {
|
||||
MessageListener messageListener = createMessageListener(container);
|
||||
Assert.state(messageListener != null, "Endpoint [" + this + "] must provide a non null message listener");
|
||||
container.setupMessageListener(messageListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description for this endpoint.
|
||||
* <p>Available to subclasses, for inclusion in their {@code toString()} result.
|
||||
*/
|
||||
protected StringBuilder getEndpointDescription() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
return result.append(getClass().getSimpleName())
|
||||
.append("[")
|
||||
.append(this.id)
|
||||
.append("] destination=")
|
||||
.append(this.destination)
|
||||
.append(" | queue='")
|
||||
.append(this.queue)
|
||||
.append("' | subscription='")
|
||||
.append(this.subscription)
|
||||
.append(" | selector='")
|
||||
.append(this.selector)
|
||||
.append("'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getEndpointDescription().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,6 +22,9 @@ import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
@@ -36,10 +39,13 @@ import org.springframework.util.StringUtils;
|
||||
* common properties that are identical for all listener container variants.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
*/
|
||||
abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
|
||||
protected static final String FACTORY_ID_ATTRIBUTE = "factory-id";
|
||||
|
||||
protected static final String LISTENER_ELEMENT = "listener";
|
||||
|
||||
protected static final String ID_ATTRIBUTE = "id";
|
||||
@@ -95,13 +101,21 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
|
||||
parserContext.pushContainingComponent(compositeDef);
|
||||
|
||||
// First parse the common configuration of the container
|
||||
PropertyValues containerValues = parseProperties(element, parserContext);
|
||||
|
||||
// Expose the factory if requested
|
||||
parseContainerFactory(element, parserContext, containerValues);
|
||||
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = parserContext.getDelegate().getLocalName(child);
|
||||
if (LISTENER_ELEMENT.equals(localName)) {
|
||||
parseListener((Element) child, element, parserContext);
|
||||
ListenerContainerParserContext context = new ListenerContainerParserContext(
|
||||
element, (Element) child, containerValues, parserContext);
|
||||
parseListener(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,10 +124,46 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void parseListener(Element listenerEle, Element containerEle, ParserContext parserContext) {
|
||||
/**
|
||||
* Parse the common properties for all listeners as defined by the specified
|
||||
* container {@link Element}.
|
||||
*/
|
||||
protected abstract PropertyValues parseProperties(Element containerEle, ParserContext parserContext);
|
||||
|
||||
/**
|
||||
* Create the {@link BeanDefinition} for the container factory using the specified
|
||||
* shared property values.
|
||||
*/
|
||||
protected abstract RootBeanDefinition createContainerFactory(String factoryId,
|
||||
Element containerElement, PropertyValues propertyValues);
|
||||
|
||||
/**
|
||||
* Create the container {@link BeanDefinition} for the specified context.
|
||||
*/
|
||||
protected abstract BeanDefinition createContainer(ListenerContainerParserContext context);
|
||||
|
||||
|
||||
private void parseContainerFactory(Element element, ParserContext parserContext, PropertyValues propertyValues) {
|
||||
String factoryId = element.getAttribute(FACTORY_ID_ATTRIBUTE);
|
||||
if (StringUtils.hasText(factoryId)) {
|
||||
RootBeanDefinition beanDefinition = createContainerFactory(factoryId, element, propertyValues);
|
||||
if (beanDefinition != null) {
|
||||
beanDefinition.setSource(parserContext.extractSource(element));
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, factoryId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseListener(ListenerContainerParserContext context) {
|
||||
ParserContext parserContext = context.getParserContext();
|
||||
PropertyValues containerValues = context.getContainerValues();
|
||||
Element listenerEle = context.getListenerElement();
|
||||
|
||||
RootBeanDefinition listenerDef = new RootBeanDefinition();
|
||||
listenerDef.setSource(parserContext.extractSource(listenerEle));
|
||||
|
||||
BeanDefinition containerDef = createContainer(context);
|
||||
|
||||
String ref = listenerEle.getAttribute(REF_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(ref)) {
|
||||
parserContext.getReaderContext().error(
|
||||
@@ -133,23 +183,15 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
}
|
||||
listenerDef.getPropertyValues().add("defaultListenerMethod", method);
|
||||
|
||||
if (containerEle.hasAttribute(MESSAGE_CONVERTER_ATTRIBUTE)) {
|
||||
String messageConverter = containerEle.getAttribute(MESSAGE_CONVERTER_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(messageConverter)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Listener container 'message-converter' attribute contains empty value.", containerEle);
|
||||
}
|
||||
else {
|
||||
listenerDef.getPropertyValues().add("messageConverter",
|
||||
new RuntimeBeanReference(messageConverter));
|
||||
}
|
||||
PropertyValue messageConverterPv = getMessageConverter(containerValues);
|
||||
if (messageConverterPv != null) {
|
||||
listenerDef.getPropertyValues().addPropertyValue(messageConverterPv);
|
||||
}
|
||||
|
||||
BeanDefinition containerDef = parseContainer(listenerEle, containerEle, parserContext);
|
||||
|
||||
if (listenerEle.hasAttribute(RESPONSE_DESTINATION_ATTRIBUTE)) {
|
||||
String responseDestination = listenerEle.getAttribute(RESPONSE_DESTINATION_ATTRIBUTE);
|
||||
boolean pubSubDomain = indicatesPubSub(containerDef);
|
||||
boolean pubSubDomain = indicatesPubSub(containerValues);
|
||||
listenerDef.getPropertyValues().add(
|
||||
pubSubDomain ? "defaultResponseTopicName" : "defaultResponseQueueName", responseDestination);
|
||||
if (containerDef.getPropertyValues().contains("destinationResolver")) {
|
||||
@@ -172,13 +214,14 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(containerDef, containerBeanName));
|
||||
}
|
||||
|
||||
protected abstract BeanDefinition parseContainer(
|
||||
Element listenerEle, Element containerEle, ParserContext parserContext);
|
||||
|
||||
protected boolean indicatesPubSub(BeanDefinition containerDef) {
|
||||
protected boolean indicatesPubSub(PropertyValues propertyValues) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected PropertyValue getMessageConverter(PropertyValues containerValues) {
|
||||
return containerValues.getPropertyValue("messageConverter");
|
||||
}
|
||||
|
||||
protected void parseListenerConfiguration(Element ele, ParserContext parserContext, BeanDefinition configDef) {
|
||||
String destination = ele.getAttribute(DESTINATION_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(destination)) {
|
||||
@@ -206,7 +249,9 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseContainerConfiguration(Element ele, ParserContext parserContext, BeanDefinition configDef) {
|
||||
protected PropertyValues parseCommonContainerProperties(Element ele, ParserContext parserContext) {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
|
||||
String destinationType = ele.getAttribute(DESTINATION_TYPE_ATTRIBUTE);
|
||||
boolean pubSubDomain = false;
|
||||
boolean subscriptionDurable = false;
|
||||
@@ -224,8 +269,8 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
parserContext.getReaderContext().error("Invalid listener container 'destination-type': " +
|
||||
"only \"queue\", \"topic\" and \"durableTopic\" supported.", ele);
|
||||
}
|
||||
configDef.getPropertyValues().add("pubSubDomain", pubSubDomain);
|
||||
configDef.getPropertyValues().add("subscriptionDurable", subscriptionDurable);
|
||||
propertyValues.add("pubSubDomain", pubSubDomain);
|
||||
propertyValues.add("subscriptionDurable", subscriptionDurable);
|
||||
|
||||
if (ele.hasAttribute(CLIENT_ID_ATTRIBUTE)) {
|
||||
String clientId = ele.getAttribute(CLIENT_ID_ATTRIBUTE);
|
||||
@@ -233,8 +278,9 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
parserContext.getReaderContext().error(
|
||||
"Listener 'client-id' attribute contains empty value.", ele);
|
||||
}
|
||||
configDef.getPropertyValues().add("clientId", clientId);
|
||||
propertyValues.add("clientId", clientId);
|
||||
}
|
||||
return propertyValues;
|
||||
}
|
||||
|
||||
protected Integer parseAcknowledgeMode(Element ele, ParserContext parserContext) {
|
||||
@@ -261,8 +307,45 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean indicatesPubSubConfig(BeanDefinition configDef) {
|
||||
return (Boolean) configDef.getPropertyValues().getPropertyValue("pubSubDomain").getValue();
|
||||
protected boolean indicatesPubSubConfig(PropertyValues configuration) {
|
||||
return (Boolean) configuration.getPropertyValue("pubSubDomain").getValue();
|
||||
}
|
||||
|
||||
|
||||
protected static class ListenerContainerParserContext {
|
||||
private final Element containerElement;
|
||||
private final Element listenerElement;
|
||||
private final PropertyValues containerValues;
|
||||
private final ParserContext parserContext;
|
||||
|
||||
public ListenerContainerParserContext(Element containerElement, Element listenerElement,
|
||||
PropertyValues containerValues, ParserContext parserContext) {
|
||||
this.containerElement = containerElement;
|
||||
this.listenerElement = listenerElement;
|
||||
this.containerValues = containerValues;
|
||||
this.parserContext = parserContext;
|
||||
}
|
||||
|
||||
public Element getContainerElement() {
|
||||
return containerElement;
|
||||
}
|
||||
|
||||
public Element getListenerElement() {
|
||||
return listenerElement;
|
||||
}
|
||||
|
||||
public PropertyValues getContainerValues() {
|
||||
return containerValues;
|
||||
}
|
||||
|
||||
public ParserContext getParserContext() {
|
||||
return parserContext;
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return parserContext.extractSource(containerElement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the 'annotation-driven' element of the 'jms' namespace.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
final class AnnotationDrivenJmsBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
// Register component for the surrounding <jms:annotation-driven> element.
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
parserContext.pushContainingComponent(compDefinition);
|
||||
|
||||
// Nest the concrete post-processor bean in the surrounding component.
|
||||
BeanDefinitionRegistry registry = parserContext.getRegistry();
|
||||
|
||||
if (registry.containsBeanDefinition(AnnotationConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Only one JmsListenerAnnotationBeanPostProcessor may exist within the context.", source);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.jms.annotation.JmsListenerAnnotationBeanPostProcessor");
|
||||
builder.getRawBeanDefinition().setSource(source);
|
||||
String endpointRegistry = element.getAttribute("registry");
|
||||
if (StringUtils.hasText(endpointRegistry)) {
|
||||
builder.addPropertyReference("endpointRegistry", endpointRegistry);
|
||||
}
|
||||
else {
|
||||
registerDefaultEndpointRegistry(source, parserContext);
|
||||
}
|
||||
String defaultContainerFactory = element.getAttribute("default-container-factory");
|
||||
if (StringUtils.hasText(defaultContainerFactory)) {
|
||||
builder.addPropertyReference("defaultContainerFactory", defaultContainerFactory);
|
||||
}
|
||||
String handlerMethodFactory = element.getAttribute("handler-method-factory");
|
||||
if (StringUtils.hasText(handlerMethodFactory)) {
|
||||
builder.addPropertyReference("jmsHandlerMethodFactory", handlerMethodFactory);
|
||||
}
|
||||
|
||||
registerInfrastructureBean(parserContext, builder,
|
||||
AnnotationConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
}
|
||||
|
||||
// Finally register the composite component.
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void registerDefaultEndpointRegistry(Object source, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.jms.config.JmsListenerEndpointRegistry");
|
||||
builder.getRawBeanDefinition().setSource(source);
|
||||
builder.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
|
||||
registerInfrastructureBean(parserContext, builder, AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME);
|
||||
}
|
||||
|
||||
private static void registerInfrastructureBean(
|
||||
ParserContext parserContext, BeanDefinitionBuilder builder, String beanName) {
|
||||
|
||||
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, builder.getBeanDefinition());
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(builder.getBeanDefinition(), beanName);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(holder));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 javax.resource.spi.ResourceAdapter;
|
||||
|
||||
import org.springframework.jms.listener.endpoint.JmsActivationSpecConfig;
|
||||
import org.springframework.jms.listener.endpoint.JmsActivationSpecFactory;
|
||||
import org.springframework.jms.listener.endpoint.JmsMessageEndpointManager;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
|
||||
/**
|
||||
* A {@link JmsListenerContainerFactory} implementation to build a
|
||||
* JCA {@link JmsMessageEndpointManager}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultJcaListenerContainerFactory implements JmsListenerContainerFactory<JmsMessageEndpointManager> {
|
||||
|
||||
private ResourceAdapter resourceAdapter;
|
||||
|
||||
private JmsActivationSpecFactory activationSpecFactory;
|
||||
|
||||
private DestinationResolver destinationResolver;
|
||||
|
||||
private Object transactionManager;
|
||||
|
||||
private JmsActivationSpecConfig activationSpecConfig;
|
||||
|
||||
/**
|
||||
* @see JmsMessageEndpointManager#setResourceAdapter(ResourceAdapter)
|
||||
*/
|
||||
public void setResourceAdapter(ResourceAdapter resourceAdapter) {
|
||||
this.resourceAdapter = resourceAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JmsMessageEndpointManager#setActivationSpecFactory(JmsActivationSpecFactory)
|
||||
*/
|
||||
public void setActivationSpecFactory(JmsActivationSpecFactory activationSpecFactory) {
|
||||
this.activationSpecFactory = activationSpecFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JmsMessageEndpointManager#setDestinationResolver(DestinationResolver)
|
||||
*/
|
||||
public void setDestinationResolver(DestinationResolver destinationResolver) {
|
||||
this.destinationResolver = destinationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JmsMessageEndpointManager#setTransactionManager(Object)
|
||||
*/
|
||||
public void setTransactionManager(Object transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JmsMessageEndpointManager#setActivationSpecConfig(JmsActivationSpecConfig)
|
||||
*/
|
||||
public void setActivationSpecConfig(JmsActivationSpecConfig activationSpecConfig) {
|
||||
this.activationSpecConfig = activationSpecConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current {@link JmsActivationSpecConfig}.
|
||||
*/
|
||||
public JmsActivationSpecConfig getActivationSpecConfig() {
|
||||
return activationSpecConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty container instance.
|
||||
*/
|
||||
protected JmsMessageEndpointManager createContainerInstance() {
|
||||
return new JmsMessageEndpointManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JmsMessageEndpointManager createMessageListenerContainer(JmsListenerEndpoint endpoint) {
|
||||
if (this.destinationResolver != null && this.activationSpecFactory != null) {
|
||||
throw new IllegalStateException("Specify either 'activationSpecFactory' or " +
|
||||
"'destinationResolver', not both. If you define a dedicated JmsActivationSpecFactory bean, " +
|
||||
"specify the custom DestinationResolver there (if possible)");
|
||||
}
|
||||
|
||||
JmsMessageEndpointManager instance = createContainerInstance();
|
||||
|
||||
if (this.resourceAdapter != null) {
|
||||
instance.setResourceAdapter(this.resourceAdapter);
|
||||
}
|
||||
if (this.activationSpecFactory != null) {
|
||||
instance.setActivationSpecFactory(this.activationSpecFactory);
|
||||
}
|
||||
if (this.destinationResolver != null) {
|
||||
instance.setDestinationResolver(this.destinationResolver);
|
||||
}
|
||||
if (this.transactionManager != null) {
|
||||
instance.setTransactionManager(this.transactionManager);
|
||||
}
|
||||
if (this.activationSpecConfig != null) {
|
||||
instance.setActivationSpecConfig(this.activationSpecConfig);
|
||||
}
|
||||
|
||||
endpoint.setupMessageContainer(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
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.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
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
|
||||
* @since 4.1
|
||||
* @see #setCustomArgumentResolvers(java.util.List)
|
||||
* @see #setValidator(Validator)
|
||||
* @see #setConversionService(ConversionService)
|
||||
*/
|
||||
public class DefaultJmsHandlerMethodFactory
|
||||
implements JmsHandlerMethodFactory, InitializingBean, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private ConversionService conversionService = new DefaultFormattingConversionService();
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
private Validator validator = new NoOpValidator();
|
||||
|
||||
private List<HandlerMethodArgumentResolver> customArgumentResolvers
|
||||
= new ArrayList<HandlerMethodArgumentResolver>();
|
||||
|
||||
private HandlerMethodArgumentResolverComposite argumentResolvers
|
||||
= new HandlerMethodArgumentResolverComposite();
|
||||
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
protected ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link MessageConverter} to use. By default a {@link GenericMessageConverter}
|
||||
* is used.
|
||||
*
|
||||
* @see GenericMessageConverter
|
||||
*/
|
||||
public void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
protected MessageConverter getMessageConverter() {
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The configured Validator instance
|
||||
*/
|
||||
public Validator getValidator() {
|
||||
return 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) {
|
||||
Assert.notNull(customArgumentResolvers, "The 'customArgumentResolvers' cannot be null.");
|
||||
this.customArgumentResolvers = customArgumentResolvers;
|
||||
}
|
||||
|
||||
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
|
||||
return 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);
|
||||
}
|
||||
|
||||
public List<HandlerMethodArgumentResolver> getArgumentResolvers() {
|
||||
return this.argumentResolvers.getResolvers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (messageConverter == null) {
|
||||
messageConverter = new GenericMessageConverter(getConversionService());
|
||||
}
|
||||
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() {
|
||||
ConfigurableBeanFactory beanFactory =
|
||||
(ClassUtils.isAssignableValue(ConfigurableApplicationContext.class, applicationContext)) ?
|
||||
((ConfigurableApplicationContext) applicationContext).getBeanFactory() : null;
|
||||
|
||||
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
|
||||
|
||||
// Annotation-based argument resolution
|
||||
resolvers.add(new HeaderMethodArgumentResolver(getConversionService(), beanFactory));
|
||||
resolvers.add(new HeadersMethodArgumentResolver());
|
||||
|
||||
// Type-based argument resolution
|
||||
resolvers.add(new MessageMethodArgumentResolver());
|
||||
|
||||
resolvers.addAll(getCustomArgumentResolvers());
|
||||
resolvers.add(new PayloadArgumentResolver(getMessageConverter(), getValidator()));
|
||||
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.jms.listener.DefaultMessageListenerContainer;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* A {@link JmsListenerContainerFactory} implementation to build regular
|
||||
* {@link DefaultMessageListenerContainer}.
|
||||
*
|
||||
* <p>This should be the default for most users and a good transition
|
||||
* paths for those that are used to build such container definition
|
||||
* manually.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultJmsListenerContainerFactory
|
||||
extends AbstractJmsListenerContainerFactory<DefaultMessageListenerContainer> {
|
||||
|
||||
private Executor taskExecutor;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
private Integer cacheLevel;
|
||||
|
||||
private String cacheLevelName;
|
||||
|
||||
private String concurrency;
|
||||
|
||||
private Integer maxMessagesPerTask;
|
||||
|
||||
private Long receiveTimeout;
|
||||
|
||||
private Long recoveryInterval;
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setTaskExecutor(java.util.concurrent.Executor)
|
||||
*/
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setTransactionManager(PlatformTransactionManager)
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setCacheLevel(int)
|
||||
*/
|
||||
public void setCacheLevel(Integer cacheLevel) {
|
||||
this.cacheLevel = cacheLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setCacheLevelName(String)
|
||||
*/
|
||||
public void setCacheLevelName(String cacheLevelName) {
|
||||
this.cacheLevelName = cacheLevelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setConcurrency(String)
|
||||
*/
|
||||
public void setConcurrency(String concurrency) {
|
||||
this.concurrency = concurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setMaxMessagesPerTask(int)
|
||||
*/
|
||||
public void setMaxMessagesPerTask(Integer maxMessagesPerTask) {
|
||||
this.maxMessagesPerTask = maxMessagesPerTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setReceiveTimeout(long)
|
||||
*/
|
||||
public void setReceiveTimeout(Long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DefaultMessageListenerContainer#setRecoveryInterval(long)
|
||||
*/
|
||||
public void setRecoveryInterval(Long recoveryInterval) {
|
||||
this.recoveryInterval = recoveryInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultMessageListenerContainer createContainerInstance() {
|
||||
return new DefaultMessageListenerContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeContainer(DefaultMessageListenerContainer container) {
|
||||
if (this.taskExecutor != null) {
|
||||
container.setTaskExecutor(this.taskExecutor);
|
||||
}
|
||||
if (this.transactionManager != null) {
|
||||
container.setTransactionManager(this.transactionManager);
|
||||
}
|
||||
|
||||
if (this.cacheLevel != null) {
|
||||
container.setCacheLevel(this.cacheLevel);
|
||||
}
|
||||
else if (this.cacheLevelName != null) {
|
||||
container.setCacheLevelName(this.cacheLevelName);
|
||||
}
|
||||
|
||||
if (this.concurrency != null) {
|
||||
container.setConcurrency(this.concurrency);
|
||||
}
|
||||
if (this.maxMessagesPerTask != null) {
|
||||
container.setMaxMessagesPerTask(this.maxMessagesPerTask);
|
||||
}
|
||||
if (this.receiveTimeout != null) {
|
||||
container.setReceiveTimeout(this.receiveTimeout);
|
||||
}
|
||||
if (this.recoveryInterval != null) {
|
||||
container.setRecoveryInterval(this.recoveryInterval);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,9 @@ package org.springframework.jms.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -28,6 +31,7 @@ import org.springframework.util.StringUtils;
|
||||
* Parser for the JMS {@code <jca-listener-container>} element.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
*/
|
||||
class JcaListenerContainerParser extends AbstractListenerContainerParser {
|
||||
@@ -38,11 +42,68 @@ class JcaListenerContainerParser extends AbstractListenerContainerParser {
|
||||
|
||||
|
||||
@Override
|
||||
protected BeanDefinition parseContainer(Element listenerEle, Element containerEle, ParserContext parserContext) {
|
||||
protected PropertyValues parseProperties(Element containerEle, ParserContext parserContext) {
|
||||
final MutablePropertyValues properties = new MutablePropertyValues();
|
||||
PropertyValues containerValues = parseContainerProperties(containerEle, parserContext);
|
||||
|
||||
// Common values are added to the activationSpecConfig
|
||||
PropertyValues commonValues = parseCommonContainerProperties(containerEle, parserContext);
|
||||
BeanDefinition beanDefinition = getActivationSpecConfigBeanDefinition(containerValues);
|
||||
beanDefinition.getPropertyValues().addPropertyValues(commonValues);
|
||||
|
||||
properties.addPropertyValues(containerValues);
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RootBeanDefinition createContainerFactory(String factoryId,
|
||||
Element containerElement, PropertyValues propertyValues) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setBeanClassName("org.springframework.jms.config.DefaultJcaListenerContainerFactory");
|
||||
beanDefinition.getPropertyValues().addPropertyValues(propertyValues);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition createContainer(ListenerContainerParserContext context) {
|
||||
RootBeanDefinition containerDef = new RootBeanDefinition();
|
||||
containerDef.setSource(parserContext.extractSource(containerEle));
|
||||
containerDef.setSource(context.getSource());
|
||||
containerDef.setBeanClassName("org.springframework.jms.listener.endpoint.JmsMessageEndpointManager");
|
||||
|
||||
containerDef.getPropertyValues().addPropertyValues(context.getContainerValues());
|
||||
|
||||
|
||||
BeanDefinition activationSpec = getActivationSpecConfigBeanDefinition(context.getContainerValues());
|
||||
parseListenerConfiguration(context.getListenerElement(), context.getParserContext(), activationSpec);
|
||||
|
||||
String phase = context.getContainerElement().getAttribute(PHASE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(phase)) {
|
||||
containerDef.getPropertyValues().add("phase", phase);
|
||||
}
|
||||
|
||||
return containerDef;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean indicatesPubSub(PropertyValues propertyValues) {
|
||||
BeanDefinition configDef = getActivationSpecConfigBeanDefinition(propertyValues);
|
||||
return indicatesPubSubConfig(configDef.getPropertyValues());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyValue getMessageConverter(PropertyValues containerValues) {
|
||||
BeanDefinition configDef = getActivationSpecConfigBeanDefinition(containerValues);
|
||||
return super.getMessageConverter(configDef.getPropertyValues());
|
||||
}
|
||||
|
||||
private BeanDefinition getActivationSpecConfigBeanDefinition(PropertyValues containerValues) {
|
||||
PropertyValue activationSpecConfig = containerValues.getPropertyValue("activationSpecConfig");
|
||||
return (BeanDefinition) activationSpecConfig.getValue();
|
||||
}
|
||||
|
||||
private PropertyValues parseContainerProperties(Element containerEle,
|
||||
ParserContext parserContext) {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
if (containerEle.hasAttribute(RESOURCE_ADAPTER_ATTRIBUTE)) {
|
||||
String resourceAdapterBeanName = containerEle.getAttribute(RESOURCE_ADAPTER_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(resourceAdapterBeanName)) {
|
||||
@@ -50,7 +111,7 @@ class JcaListenerContainerParser extends AbstractListenerContainerParser {
|
||||
"Listener container 'resource-adapter' attribute contains empty value.", containerEle);
|
||||
}
|
||||
else {
|
||||
containerDef.getPropertyValues().add("resourceAdapter",
|
||||
propertyValues.add("resourceAdapter",
|
||||
new RuntimeBeanReference(resourceAdapterBeanName));
|
||||
}
|
||||
}
|
||||
@@ -63,32 +124,29 @@ class JcaListenerContainerParser extends AbstractListenerContainerParser {
|
||||
"'destination-resolver', not both. If you define a dedicated JmsActivationSpecFactory bean, " +
|
||||
"specify the custom DestinationResolver there (if possible).", containerEle);
|
||||
}
|
||||
containerDef.getPropertyValues().add("activationSpecFactory",
|
||||
propertyValues.add("activationSpecFactory",
|
||||
new RuntimeBeanReference(activationSpecFactoryBeanName));
|
||||
}
|
||||
if (StringUtils.hasText(destinationResolverBeanName)) {
|
||||
containerDef.getPropertyValues().add("destinationResolver",
|
||||
propertyValues.add("destinationResolver",
|
||||
new RuntimeBeanReference(destinationResolverBeanName));
|
||||
}
|
||||
|
||||
String transactionManagerBeanName = containerEle.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(transactionManagerBeanName)) {
|
||||
propertyValues.add("transactionManager",
|
||||
new RuntimeBeanReference(transactionManagerBeanName));
|
||||
}
|
||||
|
||||
RootBeanDefinition configDef = new RootBeanDefinition();
|
||||
configDef.setSource(parserContext.extractSource(configDef));
|
||||
configDef.setBeanClassName("org.springframework.jms.listener.endpoint.JmsActivationSpecConfig");
|
||||
|
||||
parseListenerConfiguration(listenerEle, parserContext, configDef);
|
||||
parseContainerConfiguration(containerEle, parserContext, configDef);
|
||||
|
||||
Integer acknowledgeMode = parseAcknowledgeMode(containerEle, parserContext);
|
||||
if (acknowledgeMode != null) {
|
||||
configDef.getPropertyValues().add("acknowledgeMode", acknowledgeMode);
|
||||
}
|
||||
|
||||
String transactionManagerBeanName = containerEle.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(transactionManagerBeanName)) {
|
||||
containerDef.getPropertyValues().add("transactionManager",
|
||||
new RuntimeBeanReference(transactionManagerBeanName));
|
||||
}
|
||||
|
||||
String concurrency = containerEle.getAttribute(CONCURRENCY_ATTRIBUTE);
|
||||
if (StringUtils.hasText(concurrency)) {
|
||||
configDef.getPropertyValues().add("concurrency", concurrency);
|
||||
@@ -99,21 +157,21 @@ class JcaListenerContainerParser extends AbstractListenerContainerParser {
|
||||
configDef.getPropertyValues().add("prefetchSize", new Integer(prefetch));
|
||||
}
|
||||
|
||||
String phase = containerEle.getAttribute(PHASE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(phase)) {
|
||||
containerDef.getPropertyValues().add("phase", phase);
|
||||
if (containerEle.hasAttribute(MESSAGE_CONVERTER_ATTRIBUTE)) {
|
||||
String messageConverter = containerEle.getAttribute(MESSAGE_CONVERTER_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(messageConverter)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"listener container 'message-converter' attribute contains empty value.", containerEle);
|
||||
}
|
||||
else {
|
||||
configDef.getPropertyValues().add("messageConverter",
|
||||
new RuntimeBeanReference(messageConverter));
|
||||
}
|
||||
}
|
||||
|
||||
containerDef.getPropertyValues().add("activationSpecConfig", configDef);
|
||||
propertyValues.add("activationSpecConfig", configDef);
|
||||
|
||||
return containerDef;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean indicatesPubSub(BeanDefinition containerDef) {
|
||||
BeanDefinition configDef =
|
||||
(BeanDefinition) containerDef.getPropertyValues().getPropertyValue("activationSpecConfig").getValue();
|
||||
return indicatesPubSubConfig(configDef);
|
||||
return propertyValues;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 an JMS-specific {@link InvocableHandlerMethod} suitable for that method
|
||||
*/
|
||||
InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 org.springframework.jms.listener.MessageListenerContainer;
|
||||
|
||||
/**
|
||||
* Factory of {@link MessageListenerContainer} based on a
|
||||
* {@link JmsListenerEndpoint} definition.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JmsListenerEndpoint
|
||||
*/
|
||||
public interface JmsListenerContainerFactory<C extends MessageListenerContainer> {
|
||||
|
||||
/**
|
||||
* Create a {@link MessageListenerContainer} for the given {@link JmsListenerEndpoint}.
|
||||
* @param endpoint the endpoint to configure
|
||||
* @return the created container
|
||||
*/
|
||||
C createMessageListenerContainer(JmsListenerEndpoint endpoint);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,6 +20,8 @@ import javax.jms.Session;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -31,6 +33,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
*/
|
||||
class JmsListenerContainerParser extends AbstractListenerContainerParser {
|
||||
@@ -52,14 +55,44 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser {
|
||||
private static final String RECOVERY_INTERVAL_ATTRIBUTE = "recovery-interval";
|
||||
|
||||
|
||||
protected PropertyValues parseProperties(Element containerEle, ParserContext parserContext) {
|
||||
final MutablePropertyValues properties = new MutablePropertyValues();
|
||||
PropertyValues commonValues = parseCommonContainerProperties(containerEle, parserContext);
|
||||
PropertyValues containerValues = parseContainerProperties(containerEle,
|
||||
parserContext, isSimpleContainer(containerEle));
|
||||
properties.addPropertyValues(commonValues);
|
||||
properties.addPropertyValues(containerValues);
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition parseContainer(Element listenerEle, Element containerEle, ParserContext parserContext) {
|
||||
protected RootBeanDefinition createContainerFactory(String factoryId, Element containerEle, PropertyValues propertyValues) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
String containerType = containerEle.getAttribute(CONTAINER_TYPE_ATTRIBUTE);
|
||||
String containerClass = containerEle.getAttribute(CONTAINER_CLASS_ATTRIBUTE);
|
||||
if (!"".equals(containerClass)) {
|
||||
return null; // Not supported
|
||||
}
|
||||
else if ("".equals(containerType) || containerType.startsWith("default")) {
|
||||
beanDefinition.setBeanClassName("org.springframework.jms.config.DefaultJmsListenerContainerFactory");
|
||||
}
|
||||
else if (containerType.startsWith("simple")) {
|
||||
beanDefinition.setBeanClassName("org.springframework.jms.config.SimpleJmsListenerContainerFactory");
|
||||
}
|
||||
beanDefinition.getPropertyValues().addPropertyValues(propertyValues);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition createContainer(ListenerContainerParserContext context) {
|
||||
RootBeanDefinition containerDef = new RootBeanDefinition();
|
||||
containerDef.setSource(parserContext.extractSource(containerEle));
|
||||
containerDef.setSource(context.getSource());
|
||||
|
||||
parseListenerConfiguration(listenerEle, parserContext, containerDef);
|
||||
parseContainerConfiguration(containerEle, parserContext, containerDef);
|
||||
// Set all container values
|
||||
containerDef.getPropertyValues().addPropertyValues(context.getContainerValues());
|
||||
parseListenerConfiguration(context.getListenerElement(), context.getParserContext(), containerDef);
|
||||
|
||||
Element containerEle = context.getContainerElement();
|
||||
String containerType = containerEle.getAttribute(CONTAINER_TYPE_ATTRIBUTE);
|
||||
String containerClass = containerEle.getAttribute(CONTAINER_CLASS_ATTRIBUTE);
|
||||
if (!"".equals(containerClass)) {
|
||||
@@ -72,103 +105,10 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser {
|
||||
containerDef.setBeanClassName("org.springframework.jms.listener.SimpleMessageListenerContainer");
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error(
|
||||
context.getParserContext().getReaderContext().error(
|
||||
"Invalid 'container-type' attribute: only \"default\" and \"simple\" supported.", containerEle);
|
||||
}
|
||||
|
||||
String connectionFactoryBeanName = "connectionFactory";
|
||||
if (containerEle.hasAttribute(CONNECTION_FACTORY_ATTRIBUTE)) {
|
||||
connectionFactoryBeanName = containerEle.getAttribute(CONNECTION_FACTORY_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(connectionFactoryBeanName)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Listener container 'connection-factory' attribute contains empty value.", containerEle);
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(connectionFactoryBeanName)) {
|
||||
containerDef.getPropertyValues().add("connectionFactory",
|
||||
new RuntimeBeanReference(connectionFactoryBeanName));
|
||||
}
|
||||
|
||||
String taskExecutorBeanName = containerEle.getAttribute(TASK_EXECUTOR_ATTRIBUTE);
|
||||
if (StringUtils.hasText(taskExecutorBeanName)) {
|
||||
containerDef.getPropertyValues().add("taskExecutor",
|
||||
new RuntimeBeanReference(taskExecutorBeanName));
|
||||
}
|
||||
|
||||
String errorHandlerBeanName = containerEle.getAttribute(ERROR_HANDLER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(errorHandlerBeanName)) {
|
||||
containerDef.getPropertyValues().add("errorHandler",
|
||||
new RuntimeBeanReference(errorHandlerBeanName));
|
||||
}
|
||||
|
||||
String destinationResolverBeanName = containerEle.getAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(destinationResolverBeanName)) {
|
||||
containerDef.getPropertyValues().add("destinationResolver",
|
||||
new RuntimeBeanReference(destinationResolverBeanName));
|
||||
}
|
||||
|
||||
String cache = containerEle.getAttribute(CACHE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(cache)) {
|
||||
if (containerType.startsWith("simple")) {
|
||||
if (!("auto".equals(cache) || "consumer".equals(cache))) {
|
||||
parserContext.getReaderContext().warning(
|
||||
"'cache' attribute not actively supported for listener container of type \"simple\". " +
|
||||
"Effective runtime behavior will be equivalent to \"consumer\" / \"auto\".", containerEle);
|
||||
}
|
||||
}
|
||||
else {
|
||||
containerDef.getPropertyValues().add("cacheLevelName", "CACHE_" + cache.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
Integer acknowledgeMode = parseAcknowledgeMode(containerEle, parserContext);
|
||||
if (acknowledgeMode != null) {
|
||||
if (acknowledgeMode == Session.SESSION_TRANSACTED) {
|
||||
containerDef.getPropertyValues().add("sessionTransacted", Boolean.TRUE);
|
||||
}
|
||||
else {
|
||||
containerDef.getPropertyValues().add("sessionAcknowledgeMode", acknowledgeMode);
|
||||
}
|
||||
}
|
||||
|
||||
String transactionManagerBeanName = containerEle.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(transactionManagerBeanName)) {
|
||||
if (containerType.startsWith("simple")) {
|
||||
parserContext.getReaderContext().error(
|
||||
"'transaction-manager' attribute not supported for listener container of type \"simple\".", containerEle);
|
||||
}
|
||||
else {
|
||||
containerDef.getPropertyValues().add("transactionManager",
|
||||
new RuntimeBeanReference(transactionManagerBeanName));
|
||||
}
|
||||
}
|
||||
|
||||
String concurrency = containerEle.getAttribute(CONCURRENCY_ATTRIBUTE);
|
||||
if (StringUtils.hasText(concurrency)) {
|
||||
containerDef.getPropertyValues().add("concurrency", concurrency);
|
||||
}
|
||||
|
||||
String prefetch = containerEle.getAttribute(PREFETCH_ATTRIBUTE);
|
||||
if (StringUtils.hasText(prefetch)) {
|
||||
if (containerType.startsWith("default")) {
|
||||
containerDef.getPropertyValues().add("maxMessagesPerTask", prefetch);
|
||||
}
|
||||
}
|
||||
|
||||
String receiveTimeout = containerEle.getAttribute(RECEIVE_TIMEOUT_ATTRIBUTE);
|
||||
if (StringUtils.hasText(receiveTimeout)) {
|
||||
if (containerType.startsWith("default")) {
|
||||
containerDef.getPropertyValues().add("receiveTimeout", receiveTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
String recoveryInterval = containerEle.getAttribute(RECOVERY_INTERVAL_ATTRIBUTE);
|
||||
if (StringUtils.hasText(recoveryInterval)) {
|
||||
if (containerType.startsWith("default")) {
|
||||
containerDef.getPropertyValues().add("recoveryInterval", recoveryInterval);
|
||||
}
|
||||
}
|
||||
|
||||
String phase = containerEle.getAttribute(PHASE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(phase)) {
|
||||
containerDef.getPropertyValues().add("phase", phase);
|
||||
@@ -178,8 +118,124 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean indicatesPubSub(BeanDefinition containerDef) {
|
||||
return indicatesPubSubConfig(containerDef);
|
||||
protected boolean indicatesPubSub(PropertyValues propertyValues) {
|
||||
return indicatesPubSubConfig(propertyValues);
|
||||
}
|
||||
|
||||
private PropertyValues parseContainerProperties(Element containerEle,
|
||||
ParserContext parserContext, boolean isSimpleContainer) {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
String connectionFactoryBeanName = "connectionFactory";
|
||||
if (containerEle.hasAttribute(CONNECTION_FACTORY_ATTRIBUTE)) {
|
||||
connectionFactoryBeanName = containerEle.getAttribute(CONNECTION_FACTORY_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(connectionFactoryBeanName)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Listener container 'connection-factory' attribute contains empty value.", containerEle);
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(connectionFactoryBeanName)) {
|
||||
propertyValues.add("connectionFactory",
|
||||
new RuntimeBeanReference(connectionFactoryBeanName));
|
||||
}
|
||||
|
||||
String taskExecutorBeanName = containerEle.getAttribute(TASK_EXECUTOR_ATTRIBUTE);
|
||||
if (StringUtils.hasText(taskExecutorBeanName)) {
|
||||
propertyValues.add("taskExecutor",
|
||||
new RuntimeBeanReference(taskExecutorBeanName));
|
||||
}
|
||||
|
||||
String errorHandlerBeanName = containerEle.getAttribute(ERROR_HANDLER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(errorHandlerBeanName)) {
|
||||
propertyValues.add("errorHandler",
|
||||
new RuntimeBeanReference(errorHandlerBeanName));
|
||||
}
|
||||
|
||||
if (containerEle.hasAttribute(MESSAGE_CONVERTER_ATTRIBUTE)) {
|
||||
String messageConverter = containerEle.getAttribute(MESSAGE_CONVERTER_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(messageConverter)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"listener container 'message-converter' attribute contains empty value.", containerEle);
|
||||
}
|
||||
else {
|
||||
propertyValues.add("messageConverter",
|
||||
new RuntimeBeanReference(messageConverter));
|
||||
}
|
||||
}
|
||||
|
||||
String destinationResolverBeanName = containerEle.getAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(destinationResolverBeanName)) {
|
||||
propertyValues.add("destinationResolver",
|
||||
new RuntimeBeanReference(destinationResolverBeanName));
|
||||
}
|
||||
|
||||
String cache = containerEle.getAttribute(CACHE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(cache)) {
|
||||
if (isSimpleContainer) {
|
||||
if (!("auto".equals(cache) || "consumer".equals(cache))) {
|
||||
parserContext.getReaderContext().warning(
|
||||
"'cache' attribute not actively supported for listener container of type \"simple\". " +
|
||||
"Effective runtime behavior will be equivalent to \"consumer\" / \"auto\".", containerEle);
|
||||
}
|
||||
}
|
||||
else {
|
||||
propertyValues.add("cacheLevelName", "CACHE_" + cache.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
Integer acknowledgeMode = parseAcknowledgeMode(containerEle, parserContext);
|
||||
if (acknowledgeMode != null) {
|
||||
if (acknowledgeMode == Session.SESSION_TRANSACTED) {
|
||||
propertyValues.add("sessionTransacted", Boolean.TRUE);
|
||||
}
|
||||
else {
|
||||
propertyValues.add("sessionAcknowledgeMode", acknowledgeMode);
|
||||
}
|
||||
}
|
||||
|
||||
String transactionManagerBeanName = containerEle.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE);
|
||||
if (StringUtils.hasText(transactionManagerBeanName)) {
|
||||
if (isSimpleContainer) {
|
||||
parserContext.getReaderContext().error(
|
||||
"'transaction-manager' attribute not supported for listener container of type \"simple\".", containerEle);
|
||||
}
|
||||
else {
|
||||
propertyValues.add("transactionManager",
|
||||
new RuntimeBeanReference(transactionManagerBeanName));
|
||||
}
|
||||
}
|
||||
|
||||
String concurrency = containerEle.getAttribute(CONCURRENCY_ATTRIBUTE);
|
||||
if (StringUtils.hasText(concurrency)) {
|
||||
propertyValues.add("concurrency", concurrency);
|
||||
}
|
||||
|
||||
String prefetch = containerEle.getAttribute(PREFETCH_ATTRIBUTE);
|
||||
if (StringUtils.hasText(prefetch)) {
|
||||
if (!isSimpleContainer) {
|
||||
propertyValues.add("maxMessagesPerTask", prefetch);
|
||||
}
|
||||
}
|
||||
|
||||
String receiveTimeout = containerEle.getAttribute(RECEIVE_TIMEOUT_ATTRIBUTE);
|
||||
if (StringUtils.hasText(receiveTimeout)) {
|
||||
if (!isSimpleContainer) {
|
||||
propertyValues.add("receiveTimeout", receiveTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
String recoveryInterval = containerEle.getAttribute(RECOVERY_INTERVAL_ATTRIBUTE);
|
||||
if (StringUtils.hasText(recoveryInterval)) {
|
||||
if (!isSimpleContainer) {
|
||||
propertyValues.add("recoveryInterval", recoveryInterval);
|
||||
}
|
||||
}
|
||||
|
||||
return propertyValues;
|
||||
}
|
||||
|
||||
private boolean isSimpleContainer(Element containerEle) {
|
||||
String containerType = containerEle.getAttribute(CONTAINER_TYPE_ATTRIBUTE);
|
||||
return containerType.startsWith("simple");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 org.springframework.jms.listener.MessageListenerContainer;
|
||||
|
||||
/**
|
||||
* Model for a JMS listener endpoint. Can be used against a
|
||||
* {@link org.springframework.jms.annotation.JmsListenerConfigurer
|
||||
* JmsListenerConfigurer} to register endpoints programmatically.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface JmsListenerEndpoint {
|
||||
|
||||
/**
|
||||
* Return the id of this endpoint.
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Setup the specified message listener container with the model
|
||||
* defined by this endpoint.
|
||||
* <p>This endpoint must provide the requested missing option(s) of
|
||||
* the specified container to make it usable. Usually, this is about
|
||||
* setting the {@code destination} and the {@code messageListener} to
|
||||
* use but an implementation may override any default setting that
|
||||
* was already set.
|
||||
* @param container the container to configure
|
||||
*/
|
||||
void setupMessageContainer(MessageListenerContainer container);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper bean for registering {@link JmsListenerEndpoint} with
|
||||
* a {@link JmsListenerEndpointRegistry}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.jms.annotation.JmsListenerConfigurer
|
||||
*/
|
||||
public class JmsListenerEndpointRegistrar implements InitializingBean {
|
||||
|
||||
private JmsListenerEndpointRegistry endpointRegistry;
|
||||
|
||||
private JmsListenerContainerFactory<?> defaultContainerFactory;
|
||||
|
||||
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
|
||||
|
||||
private final List<JmsListenerEndpointDescriptor> endpointDescriptors
|
||||
= new ArrayList<JmsListenerEndpointDescriptor>();
|
||||
|
||||
/**
|
||||
* Set the {@link JmsListenerEndpointRegistry} instance to use.
|
||||
*/
|
||||
public void setEndpointRegistry(JmsListenerEndpointRegistry endpointRegistry) {
|
||||
this.endpointRegistry = endpointRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link JmsListenerEndpointRegistry} instance for this
|
||||
* registrar, may be {@code null}.
|
||||
*/
|
||||
public JmsListenerEndpointRegistry getEndpointRegistry() {
|
||||
return endpointRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default {@link JmsListenerContainerFactory} to use in case a
|
||||
* {@link JmsListenerEndpoint} is registered with a {@code null} container
|
||||
* factory.
|
||||
*/
|
||||
public void setDefaultContainerFactory(JmsListenerContainerFactory<?> defaultContainerFactory) {
|
||||
this.defaultContainerFactory = defaultContainerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link JmsListenerContainerFactory} to use if none has been
|
||||
* defined for a particular endpoint or {@code null} if no default is set.
|
||||
*/
|
||||
public JmsListenerContainerFactory<?> getDefaultContainerFactory() {
|
||||
return defaultContainerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link JmsHandlerMethodFactory} 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
|
||||
* can be configured further to support additional method arguments
|
||||
* or to customize conversion and validation support. See
|
||||
* {@link DefaultJmsHandlerMethodFactory} javadoc for more details.
|
||||
*/
|
||||
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
|
||||
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom {@link JmsHandlerMethodFactory} to use, if any.
|
||||
*/
|
||||
public JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
|
||||
return jmsHandlerMethodFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new {@link JmsListenerEndpoint} alongside the {@link JmsListenerContainerFactory}
|
||||
* to use to create the underlying container.
|
||||
* <p>The {@code factory} may be {@code null} if the default factory has to be used for that
|
||||
* endpoint.
|
||||
*/
|
||||
public void registerEndpoint(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
|
||||
Assert.notNull(endpoint, "Endpoint must be set");
|
||||
Assert.notNull(endpoint.getId(), "Endpoint id must be set");
|
||||
// Factory may be null, we defer the resolution right before actually creating the container
|
||||
this.endpointDescriptors.add(new JmsListenerEndpointDescriptor(endpoint, factory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new {@link JmsListenerEndpoint} using the default {@link JmsListenerContainerFactory}
|
||||
* to create the underlying container.
|
||||
*
|
||||
* @see #setDefaultContainerFactory(JmsListenerContainerFactory)
|
||||
* @see #registerEndpoint(JmsListenerEndpoint, JmsListenerContainerFactory)
|
||||
*/
|
||||
public void registerEndpoint(JmsListenerEndpoint endpoint) {
|
||||
registerEndpoint(endpoint, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
startAllEndpoints();
|
||||
}
|
||||
|
||||
protected void startAllEndpoints() throws Exception {
|
||||
for (JmsListenerEndpointDescriptor descriptor : endpointDescriptors) {
|
||||
endpointRegistry.createJmsListenerContainer(
|
||||
descriptor.endpoint, resolveContainerFactory(descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
private JmsListenerContainerFactory<?> resolveContainerFactory(JmsListenerEndpointDescriptor descriptor) {
|
||||
if (descriptor.containerFactory != null) {
|
||||
return descriptor.containerFactory;
|
||||
}
|
||||
else if (defaultContainerFactory != null) {
|
||||
return defaultContainerFactory;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Could not resolve the "
|
||||
+ JmsListenerContainerFactory.class.getSimpleName() + " to use for ["
|
||||
+ descriptor.endpoint + "] no factory was given and no default is set.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class JmsListenerEndpointDescriptor {
|
||||
private final JmsListenerEndpoint endpoint;
|
||||
|
||||
private final JmsListenerContainerFactory<?> containerFactory;
|
||||
|
||||
private JmsListenerEndpointDescriptor(JmsListenerEndpoint endpoint,
|
||||
JmsListenerContainerFactory<?> containerFactory) {
|
||||
this.endpoint = endpoint;
|
||||
this.containerFactory = containerFactory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jms.listener.MessageListenerContainer;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Create the necessary {@link MessageListenerContainer} instances
|
||||
* for the registered {@linkplain JmsListenerEndpoint endpoints}. Also
|
||||
* manage the lifecycle of the containers, in particular with the
|
||||
* lifecycle of the application context.
|
||||
*
|
||||
* <p>Contrary to {@link MessageListenerContainer} created manually,
|
||||
* containers managed by this instances are not registered in the
|
||||
* application context and are not candidates for autowiring. Use
|
||||
* {@link #getContainers()} if you need to access the containers
|
||||
* of this instance for management purposes. If you need to access
|
||||
* a particular container, use {@link #getContainer(String)} with the
|
||||
* id of the endpoint.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JmsListenerEndpoint
|
||||
* @see MessageListenerContainer
|
||||
* @see JmsListenerContainerFactory
|
||||
*/
|
||||
public class JmsListenerEndpointRegistry implements DisposableBean {
|
||||
|
||||
private final Map<String, MessageListenerContainer> containers =
|
||||
new HashMap<String, MessageListenerContainer>();
|
||||
|
||||
/**
|
||||
* Return the {@link MessageListenerContainer} with the specified id or
|
||||
* {@code null} if no such container exists.
|
||||
* @param id the id of the container
|
||||
* @return the container or {@code null} if no container with that id exists
|
||||
* @see JmsListenerEndpoint#getId()
|
||||
*/
|
||||
public MessageListenerContainer getContainer(String id) {
|
||||
Assert.notNull(id, "the container identifier must be set.");
|
||||
return containers.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the managed {@link MessageListenerContainer} instance(s).
|
||||
*/
|
||||
public Collection<MessageListenerContainer> getContainers() {
|
||||
return Collections.unmodifiableCollection(containers.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a message listener container for the given {@link JmsListenerEndpoint}.
|
||||
* <p>This create the necessary infrastructure to honor that endpoint
|
||||
* with regards to its configuration.
|
||||
* @param endpoint the endpoint to add
|
||||
* @see #getContainers()
|
||||
* @see #getContainer(String)
|
||||
*/
|
||||
public void createJmsListenerContainer(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
|
||||
Assert.notNull(endpoint, "Endpoint must not be null");
|
||||
Assert.notNull(factory, "Factory must not be null");
|
||||
|
||||
String id = endpoint.getId();
|
||||
Assert.notNull(id, "Endpoint id must not be null");
|
||||
Assert.state(!containers.containsKey(id), "another endpoint is already " +
|
||||
"registered with id '" + id + "'");
|
||||
|
||||
MessageListenerContainer container = doCreateJmsListenerContainer(endpoint, factory);
|
||||
containers.put(id, container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and start a new container using the specified factory.
|
||||
*/
|
||||
protected MessageListenerContainer doCreateJmsListenerContainer(JmsListenerEndpoint endpoint,
|
||||
JmsListenerContainerFactory<?> factory) {
|
||||
MessageListenerContainer container = factory.createMessageListenerContainer(endpoint);
|
||||
initializeContainer(container);
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
for (MessageListenerContainer container : getContainers()) {
|
||||
stopContainer(container);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeContainer(MessageListenerContainer container) {
|
||||
container.start();
|
||||
if (container instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) container).afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new BeanInitializationException("Could not start message listener container", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopContainer(MessageListenerContainer container) throws Exception {
|
||||
container.stop();
|
||||
if (container instanceof DisposableBean) {
|
||||
((DisposableBean) container).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
*/
|
||||
public class JmsNamespaceHandler extends NamespaceHandlerSupport {
|
||||
@@ -32,6 +33,7 @@ public class JmsNamespaceHandler extends NamespaceHandlerSupport {
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("listener-container", new JmsListenerContainerParser());
|
||||
registerBeanDefinitionParser("jca-listener-container", new JcaListenerContainerParser());
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenJmsBeanDefinitionParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.jms.listener.MessageListenerContainer;
|
||||
import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link JmsListenerEndpoint} providing the method to invoke to process
|
||||
* an incoming message for this endpoint.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
|
||||
private Object bean;
|
||||
|
||||
private Method method;
|
||||
|
||||
private String responseDestination;
|
||||
|
||||
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
|
||||
|
||||
/**
|
||||
* Set the object instance that should manage this endpoint.
|
||||
*/
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the method to invoke to process a message managed by this
|
||||
* endpoint.
|
||||
*/
|
||||
public void setMethod(Method method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default response destination to send response messages to.
|
||||
*/
|
||||
public void setResponseDestination(String responseDestination) {
|
||||
this.responseDestination = responseDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the default response destination to send response messages to.
|
||||
*/
|
||||
public String getResponseDestination() {
|
||||
return responseDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link DefaultJmsHandlerMethodFactory} to use to build the
|
||||
* {@link InvocableHandlerMethod} responsible to manage the invocation
|
||||
* of this endpoint.
|
||||
*/
|
||||
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
|
||||
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessagingMessageListenerAdapter createMessageListener(MessageListenerContainer container) {
|
||||
Assert.state(jmsHandlerMethodFactory != null,
|
||||
"Could not create message listener, message listener factory not set.");
|
||||
MessagingMessageListenerAdapter messageListener = new MessagingMessageListenerAdapter();
|
||||
InvocableHandlerMethod invocableHandlerMethod =
|
||||
jmsHandlerMethodFactory.createInvocableHandlerMethod(getBean(), getMethod());
|
||||
messageListener.setHandlerMethod(invocableHandlerMethod);
|
||||
String responseDestination = getResponseDestination();
|
||||
if (responseDestination != null) {
|
||||
if (isQueue()) {
|
||||
messageListener.setDefaultResponseQueueName(responseDestination);
|
||||
}
|
||||
else {
|
||||
messageListener.setDefaultResponseTopicName(responseDestination);
|
||||
}
|
||||
}
|
||||
MessageConverter messageConverter = container.getMessageConverter();
|
||||
if (messageConverter != null) {
|
||||
messageListener.setMessageConverter(messageConverter);
|
||||
}
|
||||
return messageListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getEndpointDescription() {
|
||||
return super.getEndpointDescription()
|
||||
.append(" | bean='")
|
||||
.append(this.bean)
|
||||
.append("'")
|
||||
.append(" | method='")
|
||||
.append(this.method)
|
||||
.append("'");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 org.springframework.jms.listener.SimpleMessageListenerContainer;
|
||||
|
||||
/**
|
||||
* A {@link JmsListenerContainerFactory} implementation to build
|
||||
* a {@link SimpleMessageListenerContainer}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SimpleJmsListenerContainerFactory
|
||||
extends AbstractJmsListenerContainerFactory<SimpleMessageListenerContainer> {
|
||||
|
||||
@Override
|
||||
protected SimpleMessageListenerContainer createContainerInstance() {
|
||||
return new SimpleMessageListenerContainer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 javax.jms.MessageListener;
|
||||
|
||||
import org.springframework.jms.listener.MessageListenerContainer;
|
||||
|
||||
/**
|
||||
* A {@link JmsListenerEndpoint} simply providing the {@link MessageListener} to
|
||||
* invoke to process an incoming message for this endpoint.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SimpleJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
|
||||
private MessageListener messageListener;
|
||||
|
||||
/**
|
||||
* Return the {@link MessageListener} to invoke when a message matching
|
||||
* the endpoint is received.
|
||||
*/
|
||||
public MessageListener getMessageListener() {
|
||||
return messageListener;
|
||||
}
|
||||
|
||||
public void setMessageListener(MessageListener messageListener) {
|
||||
this.messageListener = messageListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageListener createMessageListener(MessageListenerContainer container) {
|
||||
return getMessageListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getEndpointDescription() {
|
||||
return super.getEndpointDescription()
|
||||
.append(" | messageListener='")
|
||||
.append(this.messageListener)
|
||||
.append("'");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support package for declarative messaging configuration,
|
||||
* with XML schema being the primary configuration format.
|
||||
*
|
||||
* with Java configuration and XML schema support.
|
||||
*/
|
||||
package org.springframework.jms.config;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -27,6 +27,7 @@ import javax.jms.Session;
|
||||
import javax.jms.Topic;
|
||||
|
||||
import org.springframework.jms.support.JmsUtils;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
@@ -114,6 +115,7 @@ import org.springframework.util.ErrorHandler;
|
||||
* not just direct JMS Session usage in a {@link SessionAwareMessageListener}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0
|
||||
* @see #setMessageListener
|
||||
* @see javax.jms.MessageListener
|
||||
@@ -123,7 +125,8 @@ import org.springframework.util.ErrorHandler;
|
||||
* @see SimpleMessageListenerContainer
|
||||
* @see org.springframework.jms.listener.endpoint.JmsMessageEndpointManager
|
||||
*/
|
||||
public abstract class AbstractMessageListenerContainer extends AbstractJmsListeningContainer {
|
||||
public abstract class AbstractMessageListenerContainer
|
||||
extends AbstractJmsListeningContainer implements MessageListenerContainer {
|
||||
|
||||
private volatile Object destination;
|
||||
|
||||
@@ -139,6 +142,8 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
|
||||
|
||||
private ErrorHandler errorHandler;
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
private boolean exposeListenerSession = true;
|
||||
|
||||
private boolean acceptMessagesWhileStopping = false;
|
||||
@@ -358,6 +363,19 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link MessageConverter} strategy for converting JMS Messages.
|
||||
* @param messageConverter the message converter to use
|
||||
*/
|
||||
public void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageConverter getMessageConverter() {
|
||||
return messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to expose the listener JMS Session to a registered
|
||||
* {@link SessionAwareMessageListener} as well as to
|
||||
@@ -420,6 +438,10 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupMessageListener(Object messageListener) {
|
||||
setMessageListener(messageListener);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Template methods for listener execution
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.listener;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
|
||||
/**
|
||||
* Internal abstraction used by the framework representing a message
|
||||
* listener container. Not meant to be implemented externally with
|
||||
* support for both JMS and JCA style containers.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface MessageListenerContainer extends Lifecycle {
|
||||
|
||||
/**
|
||||
* Setup the message listener to use. Throws an {@link IllegalArgumentException}
|
||||
* if that message listener type is not supported.
|
||||
*/
|
||||
void setupMessageListener(Object messageListener);
|
||||
|
||||
/**
|
||||
* Return the {@link MessageConverter} that can be used to
|
||||
* convert {@link javax.jms.Message}, if any.
|
||||
*/
|
||||
MessageConverter getMessageConverter();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* 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.listener.adapter;
|
||||
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.InvalidDestinationException;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageListener;
|
||||
import javax.jms.MessageProducer;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.jms.listener.SessionAwareMessageListener;
|
||||
import org.springframework.jms.support.JmsUtils;
|
||||
import org.springframework.jms.support.converter.JmsHeaderMapper;
|
||||
import org.springframework.jms.support.converter.MessageConversionException;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.jms.support.converter.SimpleJmsHeaderMapper;
|
||||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
import org.springframework.jms.support.destination.DynamicDestinationResolver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An abstract {@link MessageListener} adapter providing the necessary infrastructure
|
||||
* to extract the payload of a {@link Message}
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see MessageListener
|
||||
* @see SessionAwareMessageListener
|
||||
*/
|
||||
public abstract class AbstractAdaptableMessageListener
|
||||
implements MessageListener, SessionAwareMessageListener<Message> {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Object defaultResponseDestination;
|
||||
|
||||
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
private JmsHeaderMapper headerMapper = new SimpleJmsHeaderMapper();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance with default settings.
|
||||
*/
|
||||
protected AbstractAdaptableMessageListener() {
|
||||
initDefaultStrategies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default destination to send response messages to. This will be applied
|
||||
* in case of a request message that does not carry a "JMSReplyTo" field.
|
||||
* <p>Response destinations are only relevant for listener methods that return
|
||||
* result objects, which will be wrapped in a response message and sent to a
|
||||
* response destination.
|
||||
* <p>Alternatively, specify a "defaultResponseQueueName" or "defaultResponseTopicName",
|
||||
* to be dynamically resolved via the DestinationResolver.
|
||||
* @see #setDefaultResponseQueueName(String)
|
||||
* @see #setDefaultResponseTopicName(String)
|
||||
* @see #getResponseDestination
|
||||
*/
|
||||
public void setDefaultResponseDestination(Destination destination) {
|
||||
this.defaultResponseDestination = destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default response queue to send response messages to.
|
||||
* This will be applied in case of a request message that does not carry a
|
||||
* "JMSReplyTo" field.
|
||||
* <p>Alternatively, specify a JMS Destination object as "defaultResponseDestination".
|
||||
* @see #setDestinationResolver
|
||||
* @see #setDefaultResponseDestination(javax.jms.Destination)
|
||||
*/
|
||||
public void setDefaultResponseQueueName(String destinationName) {
|
||||
this.defaultResponseDestination = new DestinationNameHolder(destinationName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default response topic to send response messages to.
|
||||
* This will be applied in case of a request message that does not carry a
|
||||
* "JMSReplyTo" field.
|
||||
* <p>Alternatively, specify a JMS Destination object as "defaultResponseDestination".
|
||||
* @see #setDestinationResolver
|
||||
* @see #setDefaultResponseDestination(javax.jms.Destination)
|
||||
*/
|
||||
public void setDefaultResponseTopicName(String destinationName) {
|
||||
this.defaultResponseDestination = new DestinationNameHolder(destinationName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DestinationResolver that should be used to resolve response
|
||||
* destination names for this adapter.
|
||||
* <p>The default resolver is a DynamicDestinationResolver. Specify a
|
||||
* JndiDestinationResolver for resolving destination names as JNDI locations.
|
||||
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.JndiDestinationResolver
|
||||
*/
|
||||
public void setDestinationResolver(DestinationResolver destinationResolver) {
|
||||
Assert.notNull(destinationResolver, "DestinationResolver must not be null");
|
||||
this.destinationResolver = destinationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DestinationResolver for this adapter.
|
||||
*/
|
||||
protected DestinationResolver getDestinationResolver() {
|
||||
return this.destinationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the converter that will convert incoming JMS messages to
|
||||
* listener method arguments, and objects returned from listener
|
||||
* methods back to JMS messages.
|
||||
* <p>The default converter is a {@link SimpleMessageConverter}, which is able
|
||||
* to handle {@link javax.jms.BytesMessage BytesMessages},
|
||||
* {@link javax.jms.TextMessage TextMessages} and
|
||||
* {@link javax.jms.ObjectMessage ObjectMessages}.
|
||||
*/
|
||||
public void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the converter that will convert incoming JMS messages to
|
||||
* listener method arguments, and objects returned from listener
|
||||
* methods back to JMS messages.
|
||||
*/
|
||||
protected MessageConverter getMessageConverter() {
|
||||
return this.messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link JmsHeaderMapper} implementation to use to map the
|
||||
* standard JMS headers. By default {@link SimpleJmsHeaderMapper} is
|
||||
* used
|
||||
* @see SimpleJmsHeaderMapper
|
||||
*/
|
||||
public void setHeaderMapper(JmsHeaderMapper headerMapper) {
|
||||
Assert.notNull(headerMapper, "HeaderMapper must not be null");
|
||||
this.headerMapper = headerMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link JmsHeaderMapper} that converts headers from
|
||||
* and to the messaging abstraction.
|
||||
*/
|
||||
protected JmsHeaderMapper getHeaderMapper() {
|
||||
return headerMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard JMS {@link MessageListener} entry point.
|
||||
* <p>Delegates the message to the target listener method, with appropriate
|
||||
* conversion of the message argument. In case of an exception, the
|
||||
* {@link #handleListenerException(Throwable)} method will be invoked.
|
||||
* <p><b>Note:</b> Does not support sending response messages based on
|
||||
* result objects returned from listener methods. Use the
|
||||
* {@link SessionAwareMessageListener} entry point (typically through a Spring
|
||||
* message listener container) for handling result objects as well.
|
||||
* @param message the incoming JMS message
|
||||
* @see #handleListenerException
|
||||
* @see #onMessage(javax.jms.Message, javax.jms.Session)
|
||||
*/
|
||||
@Override
|
||||
public void onMessage(Message message) {
|
||||
try {
|
||||
onMessage(message, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleListenerException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the default implementations for the adapter's strategies.
|
||||
* @see #setMessageConverter
|
||||
* @see org.springframework.jms.support.converter.SimpleMessageConverter
|
||||
*/
|
||||
protected void initDefaultStrategies() {
|
||||
setMessageConverter(new SimpleMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given exception that arose during listener execution.
|
||||
* The default implementation logs the exception at error level.
|
||||
* <p>This method only applies when used as standard JMS {@link MessageListener}.
|
||||
* In case of the Spring {@link SessionAwareMessageListener} mechanism,
|
||||
* exceptions get handled by the caller instead.
|
||||
* @param ex the exception to handle
|
||||
* @see #onMessage(javax.jms.Message)
|
||||
*/
|
||||
protected void handleListenerException(Throwable ex) {
|
||||
logger.error("Listener execution failed", ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the message body from the given JMS message.
|
||||
* @param message the JMS {@code Message}
|
||||
* @return the content of the message, to be passed into the
|
||||
* listener method as argument
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
*/
|
||||
protected Object extractMessage(Message message) throws JMSException {
|
||||
MessageConverter converter = getMessageConverter();
|
||||
if (converter != null) {
|
||||
return converter.fromMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given result object returned from the listener method,
|
||||
* sending a response message back.
|
||||
* @param result the result object to handle (never {@code null})
|
||||
* @param request the original request message
|
||||
* @param session the JMS Session to operate on (may be {@code null})
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see #buildMessage
|
||||
* @see #postProcessResponse
|
||||
* @see #getResponseDestination
|
||||
* @see #sendResponse
|
||||
*/
|
||||
protected void handleResult(Object result, Message request, Session session) throws JMSException {
|
||||
if (session != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Listener method returned result [" + result +
|
||||
"] - generating response message for it");
|
||||
}
|
||||
Message response = buildMessage(session, result);
|
||||
postProcessResponse(request, response);
|
||||
Destination destination = getResponseDestination(request, response, session);
|
||||
sendResponse(session, destination, response);
|
||||
}
|
||||
else {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Listener method returned result [" + result +
|
||||
"]: not generating response message for it because of no JMS Session given");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a JMS message to be sent as response based on the given result object.
|
||||
* @param session the JMS Session to operate on
|
||||
* @param result the content of the message, as returned from the listener method
|
||||
* @return the JMS {@code Message} (never {@code null})
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see #setMessageConverter
|
||||
*/
|
||||
protected Message buildMessage(Session session, Object result) throws JMSException {
|
||||
MessageConverter converter = getMessageConverter();
|
||||
if (converter != null) {
|
||||
if (result instanceof org.springframework.messaging.Message) {
|
||||
org.springframework.messaging.Message<?> message = (org.springframework.messaging.Message<?>) result;
|
||||
Message reply = converter.toMessage(message.getPayload(), session);
|
||||
getHeaderMapper().fromHeaders(message.getHeaders(), reply);
|
||||
return reply;
|
||||
}
|
||||
else {
|
||||
return converter.toMessage(result, session);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(result instanceof Message)) {
|
||||
throw new MessageConversionException(
|
||||
"No MessageConverter specified - cannot handle message [" + result + "]");
|
||||
}
|
||||
return (Message) result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given response message before it will be sent.
|
||||
* <p>The default implementation sets the response's correlation id
|
||||
* to the request message's correlation id, if any; otherwise to the
|
||||
* request message id.
|
||||
* @param request the original incoming JMS message
|
||||
* @param response the outgoing JMS message about to be sent
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see javax.jms.Message#setJMSCorrelationID
|
||||
*/
|
||||
protected void postProcessResponse(Message request, Message response) throws JMSException {
|
||||
String correlation = request.getJMSCorrelationID();
|
||||
if (correlation == null) {
|
||||
correlation = request.getJMSMessageID();
|
||||
}
|
||||
response.setJMSCorrelationID(correlation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a response destination for the given message.
|
||||
* <p>The default implementation first checks the JMS Reply-To
|
||||
* {@link Destination} of the supplied request; if that is not {@code null}
|
||||
* it is returned; if it is {@code null}, then the configured
|
||||
* {@link #resolveDefaultResponseDestination default response destination}
|
||||
* is returned; if this too is {@code null}, then an
|
||||
* {@link javax.jms.InvalidDestinationException} is thrown.
|
||||
* @param request the original incoming JMS message
|
||||
* @param response the outgoing JMS message about to be sent
|
||||
* @param session the JMS Session to operate on
|
||||
* @return the response destination (never {@code null})
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @throws javax.jms.InvalidDestinationException if no {@link Destination} can be determined
|
||||
* @see #setDefaultResponseDestination
|
||||
* @see javax.jms.Message#getJMSReplyTo()
|
||||
*/
|
||||
protected Destination getResponseDestination(Message request, Message response, Session session)
|
||||
throws JMSException {
|
||||
|
||||
Destination replyTo = request.getJMSReplyTo();
|
||||
if (replyTo == null) {
|
||||
replyTo = resolveDefaultResponseDestination(session);
|
||||
if (replyTo == null) {
|
||||
throw new InvalidDestinationException("Cannot determine response destination: " +
|
||||
"Request message does not contain reply-to destination, and no default response destination set.");
|
||||
}
|
||||
}
|
||||
return replyTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default response destination into a JMS {@link Destination}, using this
|
||||
* accessor's {@link DestinationResolver} in case of a destination name.
|
||||
* @return the located {@link Destination}
|
||||
* @throws javax.jms.JMSException if resolution failed
|
||||
* @see #setDefaultResponseDestination
|
||||
* @see #setDefaultResponseQueueName
|
||||
* @see #setDefaultResponseTopicName
|
||||
* @see #setDestinationResolver
|
||||
*/
|
||||
protected Destination resolveDefaultResponseDestination(Session session) throws JMSException {
|
||||
if (this.defaultResponseDestination instanceof Destination) {
|
||||
return (Destination) this.defaultResponseDestination;
|
||||
}
|
||||
if (this.defaultResponseDestination instanceof DestinationNameHolder) {
|
||||
DestinationNameHolder nameHolder = (DestinationNameHolder) this.defaultResponseDestination;
|
||||
return getDestinationResolver().resolveDestinationName(session, nameHolder.name, nameHolder.isTopic);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the given response message to the given destination.
|
||||
* @param response the JMS message to send
|
||||
* @param destination the JMS destination to send to
|
||||
* @param session the JMS session to operate on
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see #postProcessProducer
|
||||
* @see javax.jms.Session#createProducer
|
||||
* @see javax.jms.MessageProducer#send
|
||||
*/
|
||||
protected void sendResponse(Session session, Destination destination, Message response) throws JMSException {
|
||||
MessageProducer producer = session.createProducer(destination);
|
||||
try {
|
||||
postProcessProducer(producer, response);
|
||||
producer.send(response);
|
||||
}
|
||||
finally {
|
||||
JmsUtils.closeMessageProducer(producer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given message producer before using it to send the response.
|
||||
* <p>The default implementation is empty.
|
||||
* @param producer the JMS message producer that will be used to send the message
|
||||
* @param response the outgoing JMS message about to be sent
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
*/
|
||||
protected void postProcessProducer(MessageProducer producer, Message response) throws JMSException {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal class combining a destination name
|
||||
* and its target destination type (queue or topic).
|
||||
*/
|
||||
private static class DestinationNameHolder {
|
||||
|
||||
public final String name;
|
||||
|
||||
public final boolean isTopic;
|
||||
|
||||
public DestinationNameHolder(String name, boolean isTopic) {
|
||||
this.name = name;
|
||||
this.isTopic = isTopic;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,25 +17,16 @@
|
||||
package org.springframework.jms.listener.adapter;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.InvalidDestinationException;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageListener;
|
||||
import javax.jms.MessageProducer;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.jms.listener.SessionAwareMessageListener;
|
||||
import org.springframework.jms.listener.SubscriptionNameProvider;
|
||||
import org.springframework.jms.support.JmsUtils;
|
||||
import org.springframework.jms.support.converter.MessageConversionException;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
import org.springframework.jms.support.destination.DynamicDestinationResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -128,8 +119,8 @@ import org.springframework.util.ObjectUtils;
|
||||
* @see org.springframework.jms.listener.SessionAwareMessageListener
|
||||
* @see org.springframework.jms.listener.AbstractMessageListenerContainer#setMessageListener
|
||||
*/
|
||||
public class MessageListenerAdapter
|
||||
implements MessageListener, SessionAwareMessageListener<Message>, SubscriptionNameProvider {
|
||||
public class MessageListenerAdapter extends AbstractAdaptableMessageListener
|
||||
implements SubscriptionNameProvider {
|
||||
|
||||
/**
|
||||
* Out-of-the-box value for the default listener method: "handleMessage".
|
||||
@@ -137,19 +128,10 @@ public class MessageListenerAdapter
|
||||
public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = "handleMessage";
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Object delegate;
|
||||
|
||||
private String defaultListenerMethod = ORIGINAL_DEFAULT_LISTENER_METHOD;
|
||||
|
||||
private Object defaultResponseDestination;
|
||||
|
||||
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link MessageListenerAdapter} with default settings.
|
||||
@@ -205,112 +187,6 @@ public class MessageListenerAdapter
|
||||
return this.defaultListenerMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default destination to send response messages to. This will be applied
|
||||
* in case of a request message that does not carry a "JMSReplyTo" field.
|
||||
* <p>Response destinations are only relevant for listener methods that return
|
||||
* result objects, which will be wrapped in a response message and sent to a
|
||||
* response destination.
|
||||
* <p>Alternatively, specify a "defaultResponseQueueName" or "defaultResponseTopicName",
|
||||
* to be dynamically resolved via the DestinationResolver.
|
||||
* @see #setDefaultResponseQueueName(String)
|
||||
* @see #setDefaultResponseTopicName(String)
|
||||
* @see #getResponseDestination
|
||||
*/
|
||||
public void setDefaultResponseDestination(Destination destination) {
|
||||
this.defaultResponseDestination = destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default response queue to send response messages to.
|
||||
* This will be applied in case of a request message that does not carry a
|
||||
* "JMSReplyTo" field.
|
||||
* <p>Alternatively, specify a JMS Destination object as "defaultResponseDestination".
|
||||
* @see #setDestinationResolver
|
||||
* @see #setDefaultResponseDestination(javax.jms.Destination)
|
||||
*/
|
||||
public void setDefaultResponseQueueName(String destinationName) {
|
||||
this.defaultResponseDestination = new DestinationNameHolder(destinationName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default response topic to send response messages to.
|
||||
* This will be applied in case of a request message that does not carry a
|
||||
* "JMSReplyTo" field.
|
||||
* <p>Alternatively, specify a JMS Destination object as "defaultResponseDestination".
|
||||
* @see #setDestinationResolver
|
||||
* @see #setDefaultResponseDestination(javax.jms.Destination)
|
||||
*/
|
||||
public void setDefaultResponseTopicName(String destinationName) {
|
||||
this.defaultResponseDestination = new DestinationNameHolder(destinationName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DestinationResolver that should be used to resolve response
|
||||
* destination names for this adapter.
|
||||
* <p>The default resolver is a DynamicDestinationResolver. Specify a
|
||||
* JndiDestinationResolver for resolving destination names as JNDI locations.
|
||||
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.JndiDestinationResolver
|
||||
*/
|
||||
public void setDestinationResolver(DestinationResolver destinationResolver) {
|
||||
Assert.notNull(destinationResolver, "DestinationResolver must not be null");
|
||||
this.destinationResolver = destinationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DestinationResolver for this adapter.
|
||||
*/
|
||||
protected DestinationResolver getDestinationResolver() {
|
||||
return this.destinationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the converter that will convert incoming JMS messages to
|
||||
* listener method arguments, and objects returned from listener
|
||||
* methods back to JMS messages.
|
||||
* <p>The default converter is a {@link SimpleMessageConverter}, which is able
|
||||
* to handle {@link javax.jms.BytesMessage BytesMessages},
|
||||
* {@link javax.jms.TextMessage TextMessages} and
|
||||
* {@link javax.jms.ObjectMessage ObjectMessages}.
|
||||
*/
|
||||
public void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the converter that will convert incoming JMS messages to
|
||||
* listener method arguments, and objects returned from listener
|
||||
* methods back to JMS messages.
|
||||
*/
|
||||
protected MessageConverter getMessageConverter() {
|
||||
return this.messageConverter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Standard JMS {@link MessageListener} entry point.
|
||||
* <p>Delegates the message to the target listener method, with appropriate
|
||||
* conversion of the message argument. In case of an exception, the
|
||||
* {@link #handleListenerException(Throwable)} method will be invoked.
|
||||
* <p><b>Note:</b> Does not support sending response messages based on
|
||||
* result objects returned from listener methods. Use the
|
||||
* {@link SessionAwareMessageListener} entry point (typically through a Spring
|
||||
* message listener container) for handling result objects as well.
|
||||
* @param message the incoming JMS message
|
||||
* @see #handleListenerException
|
||||
* @see #onMessage(javax.jms.Message, javax.jms.Session)
|
||||
*/
|
||||
@Override
|
||||
public void onMessage(Message message) {
|
||||
try {
|
||||
onMessage(message, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleListenerException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring {@link SessionAwareMessageListener} entry point.
|
||||
* <p>Delegates the message to the target listener method, with appropriate
|
||||
@@ -374,44 +250,6 @@ public class MessageListenerAdapter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the default implementations for the adapter's strategies.
|
||||
* @see #setMessageConverter
|
||||
* @see org.springframework.jms.support.converter.SimpleMessageConverter
|
||||
*/
|
||||
protected void initDefaultStrategies() {
|
||||
setMessageConverter(new SimpleMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given exception that arose during listener execution.
|
||||
* The default implementation logs the exception at error level.
|
||||
* <p>This method only applies when used as standard JMS {@link MessageListener}.
|
||||
* In case of the Spring {@link SessionAwareMessageListener} mechanism,
|
||||
* exceptions get handled by the caller instead.
|
||||
* @param ex the exception to handle
|
||||
* @see #onMessage(javax.jms.Message)
|
||||
*/
|
||||
protected void handleListenerException(Throwable ex) {
|
||||
logger.error("Listener execution failed", ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the message body from the given JMS message.
|
||||
* @param message the JMS {@code Message}
|
||||
* @return the content of the message, to be passed into the
|
||||
* listener method as argument
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
*/
|
||||
protected Object extractMessage(Message message) throws JMSException {
|
||||
MessageConverter converter = getMessageConverter();
|
||||
if (converter != null) {
|
||||
return converter.fromMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the name of the listener method that is supposed to
|
||||
* handle the given message.
|
||||
@@ -481,176 +319,4 @@ public class MessageListenerAdapter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the given result object returned from the listener method,
|
||||
* sending a response message back.
|
||||
* @param result the result object to handle (never {@code null})
|
||||
* @param request the original request message
|
||||
* @param session the JMS Session to operate on (may be {@code null})
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see #buildMessage
|
||||
* @see #postProcessResponse
|
||||
* @see #getResponseDestination
|
||||
* @see #sendResponse
|
||||
*/
|
||||
protected void handleResult(Object result, Message request, Session session) throws JMSException {
|
||||
if (session != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Listener method returned result [" + result +
|
||||
"] - generating response message for it");
|
||||
}
|
||||
Message response = buildMessage(session, result);
|
||||
postProcessResponse(request, response);
|
||||
Destination destination = getResponseDestination(request, response, session);
|
||||
sendResponse(session, destination, response);
|
||||
}
|
||||
else {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Listener method returned result [" + result +
|
||||
"]: not generating response message for it because of no JMS Session given");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a JMS message to be sent as response based on the given result object.
|
||||
* @param session the JMS Session to operate on
|
||||
* @param result the content of the message, as returned from the listener method
|
||||
* @return the JMS {@code Message} (never {@code null})
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see #setMessageConverter
|
||||
*/
|
||||
protected Message buildMessage(Session session, Object result) throws JMSException {
|
||||
MessageConverter converter = getMessageConverter();
|
||||
if (converter != null) {
|
||||
return converter.toMessage(result, session);
|
||||
}
|
||||
else {
|
||||
if (!(result instanceof Message)) {
|
||||
throw new MessageConversionException(
|
||||
"No MessageConverter specified - cannot handle message [" + result + "]");
|
||||
}
|
||||
return (Message) result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given response message before it will be sent.
|
||||
* <p>The default implementation sets the response's correlation id
|
||||
* to the request message's correlation id, if any; otherwise to the
|
||||
* request message id.
|
||||
* @param request the original incoming JMS message
|
||||
* @param response the outgoing JMS message about to be sent
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see javax.jms.Message#setJMSCorrelationID
|
||||
*/
|
||||
protected void postProcessResponse(Message request, Message response) throws JMSException {
|
||||
String correlation = request.getJMSCorrelationID();
|
||||
if (correlation == null) {
|
||||
correlation = request.getJMSMessageID();
|
||||
}
|
||||
response.setJMSCorrelationID(correlation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a response destination for the given message.
|
||||
* <p>The default implementation first checks the JMS Reply-To
|
||||
* {@link Destination} of the supplied request; if that is not {@code null}
|
||||
* it is returned; if it is {@code null}, then the configured
|
||||
* {@link #resolveDefaultResponseDestination default response destination}
|
||||
* is returned; if this too is {@code null}, then an
|
||||
* {@link InvalidDestinationException} is thrown.
|
||||
* @param request the original incoming JMS message
|
||||
* @param response the outgoing JMS message about to be sent
|
||||
* @param session the JMS Session to operate on
|
||||
* @return the response destination (never {@code null})
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @throws InvalidDestinationException if no {@link Destination} can be determined
|
||||
* @see #setDefaultResponseDestination
|
||||
* @see javax.jms.Message#getJMSReplyTo()
|
||||
*/
|
||||
protected Destination getResponseDestination(Message request, Message response, Session session)
|
||||
throws JMSException {
|
||||
|
||||
Destination replyTo = request.getJMSReplyTo();
|
||||
if (replyTo == null) {
|
||||
replyTo = resolveDefaultResponseDestination(session);
|
||||
if (replyTo == null) {
|
||||
throw new InvalidDestinationException("Cannot determine response destination: " +
|
||||
"Request message does not contain reply-to destination, and no default response destination set.");
|
||||
}
|
||||
}
|
||||
return replyTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default response destination into a JMS {@link Destination}, using this
|
||||
* accessor's {@link DestinationResolver} in case of a destination name.
|
||||
* @return the located {@link Destination}
|
||||
* @throws javax.jms.JMSException if resolution failed
|
||||
* @see #setDefaultResponseDestination
|
||||
* @see #setDefaultResponseQueueName
|
||||
* @see #setDefaultResponseTopicName
|
||||
* @see #setDestinationResolver
|
||||
*/
|
||||
protected Destination resolveDefaultResponseDestination(Session session) throws JMSException {
|
||||
if (this.defaultResponseDestination instanceof Destination) {
|
||||
return (Destination) this.defaultResponseDestination;
|
||||
}
|
||||
if (this.defaultResponseDestination instanceof DestinationNameHolder) {
|
||||
DestinationNameHolder nameHolder = (DestinationNameHolder) this.defaultResponseDestination;
|
||||
return getDestinationResolver().resolveDestinationName(session, nameHolder.name, nameHolder.isTopic);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the given response message to the given destination.
|
||||
* @param response the JMS message to send
|
||||
* @param destination the JMS destination to send to
|
||||
* @param session the JMS session to operate on
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
* @see #postProcessProducer
|
||||
* @see javax.jms.Session#createProducer
|
||||
* @see javax.jms.MessageProducer#send
|
||||
*/
|
||||
protected void sendResponse(Session session, Destination destination, Message response) throws JMSException {
|
||||
MessageProducer producer = session.createProducer(destination);
|
||||
try {
|
||||
postProcessProducer(producer, response);
|
||||
producer.send(response);
|
||||
}
|
||||
finally {
|
||||
JmsUtils.closeMessageProducer(producer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given message producer before using it to send the response.
|
||||
* <p>The default implementation is empty.
|
||||
* @param producer the JMS message producer that will be used to send the message
|
||||
* @param response the outgoing JMS message about to be sent
|
||||
* @throws JMSException if thrown by JMS API methods
|
||||
*/
|
||||
protected void postProcessProducer(MessageProducer producer, Message response) throws JMSException {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal class combining a destination name
|
||||
* and its target destination type (queue or topic).
|
||||
*/
|
||||
private static class DestinationNameHolder {
|
||||
|
||||
public final String name;
|
||||
|
||||
public final boolean isTopic;
|
||||
|
||||
public DestinationNameHolder(String name, boolean isTopic) {
|
||||
this.name = name;
|
||||
this.isTopic = isTopic;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.listener.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.springframework.jms.support.converter.JmsHeaderMapper;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* A {@link javax.jms.MessageListener} adapter that invokes a configurable
|
||||
* {@link InvocableHandlerMethod}.
|
||||
*
|
||||
* <p>Wraps the incoming {@link javax.jms.Message} to Spring's {@link Message}
|
||||
* abstraction, copying the JMS standard headers using a configurable
|
||||
* {@link JmsHeaderMapper}.
|
||||
*
|
||||
* <p>The original {@link javax.jms.Message} and the {@link javax.jms.Session}
|
||||
* are provided as additional arguments so that these can be injected as
|
||||
* method arguments if necessary.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see Message
|
||||
* @see JmsHeaderMapper
|
||||
* @see InvocableHandlerMethod
|
||||
*/
|
||||
public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageListener {
|
||||
|
||||
private InvocableHandlerMethod handlerMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link InvocableHandlerMethod} to use to invoke the method
|
||||
* processing an incoming {@link javax.jms.Message}.
|
||||
*/
|
||||
public void setHandlerMethod(InvocableHandlerMethod handlerMethod) {
|
||||
this.handlerMethod = handlerMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(javax.jms.Message jmsMessage, Session session) throws JMSException {
|
||||
try {
|
||||
Message<?> message = toMessagingMessage(jmsMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Processing [" + message + "]");
|
||||
}
|
||||
Object result = handlerMethod.invoke(message, jmsMessage, session);
|
||||
if (result != null) {
|
||||
handleResult(result, jmsMessage, session);
|
||||
}
|
||||
else {
|
||||
logger.trace("No result object given - no result to handle");
|
||||
}
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
throw new ListenerExecutionFailedException(createMessagingErrorMessage("Listener method could not " +
|
||||
"be invoked with the incoming message"), e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ListenerExecutionFailedException("Listener method '"
|
||||
+ handlerMethod.getMethod().toGenericString() + "' threw exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Message<?> toMessagingMessage(javax.jms.Message jmsMessage) throws JMSException {
|
||||
Map<String, Object> mappedHeaders = getHeaderMapper().toHeaders(jmsMessage);
|
||||
Object convertedObject = extractMessage(jmsMessage);
|
||||
MessageBuilder<Object> builder = (convertedObject instanceof org.springframework.messaging.Message) ?
|
||||
MessageBuilder.fromMessage((org.springframework.messaging.Message<Object>) convertedObject) :
|
||||
MessageBuilder.withPayload(convertedObject);
|
||||
return builder.copyHeadersIfAbsent(mappedHeaders).build();
|
||||
}
|
||||
|
||||
private String createMessagingErrorMessage(String description) {
|
||||
StringBuilder sb = new StringBuilder(description).append("\n")
|
||||
.append("Endpoint handler details:\n")
|
||||
.append("Method [").append(handlerMethod.getMethod()).append("]\n")
|
||||
.append("Bean [").append(handlerMethod.getBean()).append("]\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.jms.listener.endpoint;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
|
||||
/**
|
||||
* Common configuration object for activating a JMS message endpoint.
|
||||
@@ -29,6 +30,7 @@ import org.springframework.core.Constants;
|
||||
* but not tied to it.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
* @see JmsActivationSpecFactory
|
||||
* @see JmsMessageEndpointManager#setActivationSpecConfig
|
||||
@@ -58,6 +60,8 @@ public class JmsActivationSpecConfig {
|
||||
|
||||
private int prefetchSize = -1;
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
|
||||
public void setDestinationName(String destinationName) {
|
||||
this.destinationName = destinationName;
|
||||
@@ -201,4 +205,19 @@ public class JmsActivationSpecConfig {
|
||||
return this.prefetchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link MessageConverter} strategy for converting JMS Messages.
|
||||
* @param messageConverter the message converter to use
|
||||
*/
|
||||
public void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link MessageConverter} to use, if any.
|
||||
*/
|
||||
public MessageConverter getMessageConverter() {
|
||||
return messageConverter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -39,6 +39,7 @@ import org.springframework.jca.endpoint.AbstractMessageEndpointFactory;
|
||||
* {@link javax.resource.spi.ResourceAdapter} instance.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
* @see #setMessageListener
|
||||
* @see #setTransactionManager
|
||||
@@ -56,6 +57,13 @@ public class JmsMessageEndpointFactory extends AbstractMessageEndpointFactory {
|
||||
this.messageListener = messageListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JMS MessageListener for this endpoint.
|
||||
*/
|
||||
protected MessageListener getMessageListener() {
|
||||
return messageListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a concrete JMS message endpoint, internal to this factory.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,6 +21,8 @@ import javax.resource.ResourceException;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.jca.endpoint.GenericMessageEndpointManager;
|
||||
import org.springframework.jms.listener.MessageListenerContainer;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,7 @@ import org.springframework.jms.support.destination.DestinationResolver;
|
||||
* for obtaining the current JMS {@link javax.jms.Session}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
* @see javax.jms.MessageListener
|
||||
* @see #setActivationSpecConfig
|
||||
@@ -46,7 +49,8 @@ import org.springframework.jms.support.destination.DestinationResolver;
|
||||
* @see JmsActivationSpecFactory
|
||||
* @see JmsMessageEndpointFactory
|
||||
*/
|
||||
public class JmsMessageEndpointManager extends GenericMessageEndpointManager implements BeanNameAware {
|
||||
public class JmsMessageEndpointManager extends GenericMessageEndpointManager
|
||||
implements BeanNameAware, MessageListenerContainer {
|
||||
|
||||
private final JmsMessageEndpointFactory endpointFactory = new JmsMessageEndpointFactory();
|
||||
|
||||
@@ -67,6 +71,13 @@ public class JmsMessageEndpointManager extends GenericMessageEndpointManager imp
|
||||
this.messageListenerSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JMS MessageListener for this endpoint.
|
||||
*/
|
||||
public MessageListener getMessageListener() {
|
||||
return this.endpointFactory.getMessageListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the XA transaction manager to use for wrapping endpoint
|
||||
* invocations, enlisting the endpoint resource in each such transaction.
|
||||
@@ -128,6 +139,23 @@ public class JmsMessageEndpointManager extends GenericMessageEndpointManager imp
|
||||
this.activationSpecConfig = activationSpecConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link JmsActivationSpecConfig} object that this endpoint manager
|
||||
* should use for activating its listener. Return {@code null} if none is set.
|
||||
*/
|
||||
public JmsActivationSpecConfig getActivationSpecConfig() {
|
||||
return activationSpecConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageConverter getMessageConverter() {
|
||||
JmsActivationSpecConfig config = getActivationSpecConfig();
|
||||
if (config != null) {
|
||||
return config.getMessageConverter();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of this message endpoint. Populated with the bean name
|
||||
* automatically when defined within Spring's bean factory.
|
||||
@@ -137,6 +165,18 @@ public class JmsMessageEndpointManager extends GenericMessageEndpointManager imp
|
||||
this.endpointFactory.setBeanName(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupMessageListener(Object messageListener) {
|
||||
if (messageListener instanceof MessageListener) {
|
||||
setMessageListener((MessageListener) messageListener);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported message listener '"
|
||||
+ messageListener.getClass().getName() + "': only '"
|
||||
+ MessageListener.class.getName() + "' type is supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws ResourceException {
|
||||
if (this.messageListenerSet) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.jms.Destination;
|
||||
|
||||
import org.springframework.jms.support.converter.JmsHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.messaging.support.MessageHeaderAccessor}
|
||||
* implementation giving access to JMS-specific headers.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class JmsMessageHeaderAccessor extends NativeMessageHeaderAccessor {
|
||||
|
||||
protected JmsMessageHeaderAccessor(Map<String, List<String>> nativeHeaders) {
|
||||
super(nativeHeaders);
|
||||
}
|
||||
|
||||
protected JmsMessageHeaderAccessor(Message<?> message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create {@link JmsMessageHeaderAccessor} from the headers of an existing message.
|
||||
*/
|
||||
public static JmsMessageHeaderAccessor wrap(Message<?> message) {
|
||||
return new JmsMessageHeaderAccessor(message);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getReplyChannel() {
|
||||
return getReplyTo();
|
||||
}
|
||||
|
||||
public String getCorrelationId() {
|
||||
return (String) getHeader(JmsHeaders.CORRELATION_ID);
|
||||
}
|
||||
|
||||
public Destination getDestination() {
|
||||
return (Destination) getHeader(JmsHeaders.DESTINATION);
|
||||
}
|
||||
|
||||
public Integer getDeliveryMode() {
|
||||
return (Integer) getHeader(JmsHeaders.DELIVERY_MODE);
|
||||
}
|
||||
|
||||
public Long getExpiration() {
|
||||
return (Long) getHeader(JmsHeaders.EXPIRATION);
|
||||
}
|
||||
|
||||
public String getMessageId() {
|
||||
return (String) getHeader(JmsHeaders.MESSAGE_ID);
|
||||
}
|
||||
|
||||
public Integer getPriority() {
|
||||
return (Integer) getHeader(JmsHeaders.PRIORITY);
|
||||
}
|
||||
|
||||
public Destination getReplyTo() {
|
||||
return (Destination) getHeader(JmsHeaders.REPLY_TO);
|
||||
}
|
||||
|
||||
public Boolean getRedelivered() {
|
||||
return (Boolean) getHeader(JmsHeaders.REDELIVERED);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return (String) getHeader(JmsHeaders.TYPE);
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return (Long) getHeader(JmsHeaders.TIMESTAMP);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.support.converter;
|
||||
|
||||
import javax.jms.Message;
|
||||
|
||||
import org.springframework.messaging.mapping.HeaderMapper;
|
||||
|
||||
/**
|
||||
* Strategy interface for mapping messaging Message headers to an outbound
|
||||
* JMS Message (e.g. to configure JMS properties) or extracting messaging
|
||||
* header values from an inbound JMS Message.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface JmsHeaderMapper extends HeaderMapper<Message> {
|
||||
|
||||
static final String CONTENT_TYPE_PROPERTY = "content_type";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.support.converter;
|
||||
|
||||
/**
|
||||
* Pre-defined names and prefixes to be used for setting and/or retrieving JMS
|
||||
* attributes from/to integration Message Headers.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface JmsHeaders {
|
||||
|
||||
/**
|
||||
* Prefix used for JMS API related headers in order to distinguish from
|
||||
* user-defined headers and other internal headers (e.g. correlationId).
|
||||
* @see SimpleJmsHeaderMapper
|
||||
*/
|
||||
public static final String PREFIX = "jms_";
|
||||
|
||||
/**
|
||||
* Correlation ID for the message. This may be the {@link #MESSAGE_ID} of
|
||||
* the message that this message replies to. It may also be an
|
||||
* application-specific identifier.
|
||||
* @see javax.jms.Message#getJMSCorrelationID()
|
||||
*/
|
||||
public static final String CORRELATION_ID = PREFIX + "correlationId";
|
||||
|
||||
/**
|
||||
* Name of the destination (topic or queue) of the message.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSDestination()
|
||||
* @see javax.jms.Destination
|
||||
* @see javax.jms.Queue
|
||||
* @see javax.jms.Topic
|
||||
*/
|
||||
public static final String DESTINATION = PREFIX + "destination";
|
||||
|
||||
/**
|
||||
* Distribution mode.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSDeliveryMode()
|
||||
* @see javax.jms.DeliveryMode
|
||||
*/
|
||||
public static final String DELIVERY_MODE = PREFIX + "deliveryMode";
|
||||
|
||||
/**
|
||||
* Message expiration date and time.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSExpiration()
|
||||
*/
|
||||
public static final String EXPIRATION = PREFIX + "expiration";
|
||||
|
||||
/**
|
||||
* Unique Identifier for a message.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSMessageID()
|
||||
*/
|
||||
public static final String MESSAGE_ID = PREFIX + "messageId";
|
||||
|
||||
/**
|
||||
* The message priority level.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSPriority()
|
||||
*/
|
||||
public static final String PRIORITY = PREFIX + "priority";
|
||||
|
||||
/**
|
||||
* Name of the destination (topic or queue) the message replies should
|
||||
* be sent to.
|
||||
* @see javax.jms.Message#getJMSReplyTo()
|
||||
*/
|
||||
public static final String REPLY_TO = PREFIX + "replyTo";
|
||||
|
||||
/**
|
||||
* Specify if the message was resent. This occurs when a message
|
||||
* consumer fails to acknowledge the message reception.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSRedelivered()
|
||||
*/
|
||||
public static final String REDELIVERED = PREFIX + "redelivered";
|
||||
|
||||
/**
|
||||
* Message type label. This type is a string value describing the message
|
||||
* in a functional manner.
|
||||
* @see javax.jms.Message#getJMSType()
|
||||
*/
|
||||
public static final String TYPE = PREFIX + "type";
|
||||
|
||||
/**
|
||||
* Date and time of the message sending operation.
|
||||
* <p>Read only value.
|
||||
* @see javax.jms.Message#getJMSTimestamp()
|
||||
*/
|
||||
public static final String TIMESTAMP = PREFIX + "timestamp";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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.support.converter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link JmsHeaderMapper}.
|
||||
* <p>
|
||||
* This implementation copies JMS API headers (e.g. JMSReplyTo) to and from
|
||||
* {@link org.springframework.messaging.Message Messages}. Any user-defined
|
||||
* properties will also be copied from a JMS Message to a Message, and any
|
||||
* other headers on a Message (beyond the JMS API headers) will likewise
|
||||
* be copied to a JMS Message. Those other headers will be copied to the
|
||||
* general properties of a JMS Message whereas the JMS API headers are passed
|
||||
* to the appropriate setter methods (e.g. setJMSReplyTo).
|
||||
* <p>
|
||||
* Constants for the JMS API headers are defined in {@link JmsHeaders}.
|
||||
* Note that most of the JMS headers are read-only: the JMSDestination,
|
||||
* JMSDeliveryMode, JMSExpiration, JMSMessageID, JMSPriority, JMSRedelivered
|
||||
* and JMSTimestamp flags are only copied <em>from</em> a JMS Message. Those
|
||||
* values will <em>not</em> be passed along from a Message to an outbound
|
||||
* JMS Message.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russel
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SimpleJmsHeaderMapper implements JmsHeaderMapper {
|
||||
|
||||
private static List<Class<?>> SUPPORTED_PROPERTY_TYPES = Arrays.asList(new Class<?>[] {
|
||||
Boolean.class, Byte.class, Double.class, Float.class, Integer.class, Long.class, Short.class, String.class});
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile String inboundPrefix = "";
|
||||
|
||||
private volatile String outboundPrefix = "";
|
||||
|
||||
/**
|
||||
* Specify a prefix to be appended to the message header name for any
|
||||
* JMS property that is being mapped into the MessageHeaders. The
|
||||
* default is an empty string (no prefix).
|
||||
* <p>
|
||||
* This does not affect the JMS properties covered by the specification/API,
|
||||
* such as JMSCorrelationID, etc. The header names used for mapping such
|
||||
* properties are all defined in our {@link JmsHeaders}.
|
||||
*
|
||||
* @param inboundPrefix The inbound prefix.
|
||||
*/
|
||||
public void setInboundPrefix(String inboundPrefix) {
|
||||
this.inboundPrefix = (inboundPrefix != null) ? inboundPrefix : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a prefix to be appended to the JMS property name for any
|
||||
* message header that is being mapped into the JMS Message. The
|
||||
* default is an empty string (no prefix).
|
||||
* <p>
|
||||
* This does not affect the JMS properties covered by the specification/API,
|
||||
* such as JMSCorrelationID, etc. The header names used for mapping such
|
||||
* properties are all defined in our {@link JmsHeaders}.
|
||||
*
|
||||
* @param outboundPrefix The outbound prefix.
|
||||
*/
|
||||
public void setOutboundPrefix(String outboundPrefix) {
|
||||
this.outboundPrefix = (outboundPrefix != null) ? outboundPrefix : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromHeaders(MessageHeaders headers, javax.jms.Message jmsMessage) {
|
||||
try {
|
||||
Object jmsCorrelationId = headers.get(JmsHeaders.CORRELATION_ID);
|
||||
if (jmsCorrelationId instanceof Number) {
|
||||
jmsCorrelationId = jmsCorrelationId.toString();
|
||||
}
|
||||
if (jmsCorrelationId instanceof String) {
|
||||
try {
|
||||
jmsMessage.setJMSCorrelationID((String) jmsCorrelationId);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to set JMSCorrelationID, skipping", e);
|
||||
}
|
||||
}
|
||||
Object jmsReplyTo = headers.get(JmsHeaders.REPLY_TO);
|
||||
if (jmsReplyTo instanceof Destination) {
|
||||
try {
|
||||
jmsMessage.setJMSReplyTo((Destination) jmsReplyTo);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to set JMSReplyTo, skipping", e);
|
||||
}
|
||||
}
|
||||
Object jmsType = headers.get(JmsHeaders.TYPE);
|
||||
if (jmsType instanceof String) {
|
||||
try {
|
||||
jmsMessage.setJMSType((String) jmsType);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to set JMSType, skipping", e);
|
||||
}
|
||||
}
|
||||
Set<String> headerNames = headers.keySet();
|
||||
for (String headerName : headerNames) {
|
||||
if (StringUtils.hasText(headerName) && !headerName.startsWith(JmsHeaders.PREFIX)) {
|
||||
Object value = headers.get(headerName);
|
||||
if (value != null && SUPPORTED_PROPERTY_TYPES.contains(value.getClass())) {
|
||||
try {
|
||||
String propertyName = this.fromHeaderName(headerName);
|
||||
jmsMessage.setObjectProperty(propertyName, value);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (headerName.startsWith("JMSX")) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("skipping reserved header, it cannot be set by client: " + headerName);
|
||||
}
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("failed to map Message header '" + headerName + "' to JMS property", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("error occurred while mapping from MessageHeaders to JMS properties", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> toHeaders(javax.jms.Message jmsMessage) {
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
try {
|
||||
try {
|
||||
String correlationId = jmsMessage.getJMSCorrelationID();
|
||||
if (correlationId != null) {
|
||||
headers.put(JmsHeaders.CORRELATION_ID, correlationId);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSCorrelationID property, skipping", e);
|
||||
}
|
||||
try {
|
||||
Destination destination = jmsMessage.getJMSDestination();
|
||||
if (destination != null) {
|
||||
headers.put(JmsHeaders.DESTINATION, destination);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSDestination property, skipping", e);
|
||||
}
|
||||
try {
|
||||
int deliveryMode = jmsMessage.getJMSDeliveryMode();
|
||||
headers.put(JmsHeaders.DELIVERY_MODE, deliveryMode);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSDeliveryMode property, skipping", e);
|
||||
}
|
||||
try {
|
||||
long expiration = jmsMessage.getJMSExpiration();
|
||||
headers.put(JmsHeaders.EXPIRATION, expiration);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSExpiration property, skipping", e);
|
||||
}
|
||||
try {
|
||||
String messageId = jmsMessage.getJMSMessageID();
|
||||
if (messageId != null) {
|
||||
headers.put(JmsHeaders.MESSAGE_ID, messageId);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSMessageID property, skipping", e);
|
||||
}
|
||||
try {
|
||||
headers.put(JmsHeaders.PRIORITY, jmsMessage.getJMSPriority());
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSPriority property, skipping", e);
|
||||
}
|
||||
try {
|
||||
Destination replyTo = jmsMessage.getJMSReplyTo();
|
||||
if (replyTo != null) {
|
||||
headers.put(JmsHeaders.REPLY_TO, replyTo);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSReplyTo property, skipping", e);
|
||||
}
|
||||
try {
|
||||
headers.put(JmsHeaders.REDELIVERED, jmsMessage.getJMSRedelivered());
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSRedelivered property, skipping", e);
|
||||
}
|
||||
try {
|
||||
String type = jmsMessage.getJMSType();
|
||||
if (type != null) {
|
||||
headers.put(JmsHeaders.TYPE, type);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSType property, skipping", e);
|
||||
}
|
||||
try {
|
||||
headers.put(JmsHeaders.TIMESTAMP, jmsMessage.getJMSTimestamp());
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.info("failed to read JMSTimestamp property, skipping", e);
|
||||
}
|
||||
|
||||
|
||||
Enumeration<?> jmsPropertyNames = jmsMessage.getPropertyNames();
|
||||
if (jmsPropertyNames != null) {
|
||||
while (jmsPropertyNames.hasMoreElements()) {
|
||||
String propertyName = jmsPropertyNames.nextElement().toString();
|
||||
try {
|
||||
String headerName = this.toHeaderName(propertyName);
|
||||
headers.put(headerName, jmsMessage.getObjectProperty(propertyName));
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("error occurred while mapping JMS property '"
|
||||
+ propertyName + "' to Message header", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JMSException e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("error occurred while mapping from JMS properties to MessageHeaders", e);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the outbound prefix if necessary.
|
||||
* <p>Convert {@link MessageHeaders#CONTENT_TYPE} to content_type for JMS compliance.
|
||||
*/
|
||||
private String fromHeaderName(String headerName) {
|
||||
String propertyName = headerName;
|
||||
if (StringUtils.hasText(this.outboundPrefix) && !propertyName.startsWith(this.outboundPrefix)) {
|
||||
propertyName = this.outboundPrefix + headerName;
|
||||
}
|
||||
else if (MessageHeaders.CONTENT_TYPE.equals(headerName)) {
|
||||
propertyName = CONTENT_TYPE_PROPERTY;
|
||||
}
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the inbound prefix if necessary.
|
||||
* <p>Convert content_type to {@link MessageHeaders#CONTENT_TYPE}.
|
||||
*/
|
||||
private String toHeaderName(String propertyName) {
|
||||
String headerName = propertyName;
|
||||
if (StringUtils.hasText(this.inboundPrefix) && !headerName.startsWith(this.inboundPrefix)) {
|
||||
headerName = this.inboundPrefix + propertyName;
|
||||
}
|
||||
else if (CONTENT_TYPE_PROPERTY.equals(propertyName)) {
|
||||
headerName = MessageHeaders.CONTENT_TYPE;
|
||||
}
|
||||
return headerName;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user