Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
71 changed files
with
7,915 additions
and
457 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
260 changes: 260 additions & 0 deletions
260
spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { | ||
} |
54 changes: 54 additions & 0 deletions
54
spring-jms/src/main/java/org/springframework/jms/annotation/JmsBootstrapConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
|
||
} |
Oops, something went wrong.