001/*
002 * Copyright 2002-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jms.listener.endpoint;
018
019import javax.jms.MessageListener;
020import javax.resource.ResourceException;
021
022import org.springframework.beans.factory.BeanNameAware;
023import org.springframework.jca.endpoint.GenericMessageEndpointManager;
024import org.springframework.jms.listener.MessageListenerContainer;
025import org.springframework.jms.support.converter.MessageConverter;
026import org.springframework.jms.support.destination.DestinationResolver;
027
028/**
029 * Extension of the generic JCA 1.5
030 * {@link org.springframework.jca.endpoint.GenericMessageEndpointManager},
031 * adding JMS-specific support for ActivationSpec configuration.
032 *
033 * <p>Allows for defining a common {@link JmsActivationSpecConfig} object
034 * that gets converted into a provider-specific JCA 1.5 ActivationSpec
035 * object for activating the endpoint.
036 *
037 * <p><b>NOTE:</b> This JCA-based endpoint manager supports standard JMS
038 * {@link javax.jms.MessageListener} endpoints only. It does <i>not</i> support
039 * Spring's {@link org.springframework.jms.listener.SessionAwareMessageListener}
040 * variant, simply because the JCA endpoint management contract does not allow
041 * for obtaining the current JMS {@link javax.jms.Session}.
042 *
043 * @author Juergen Hoeller
044 * @author Stephane Nicoll
045 * @since 2.5
046 * @see javax.jms.MessageListener
047 * @see #setActivationSpecConfig
048 * @see JmsActivationSpecConfig
049 * @see JmsActivationSpecFactory
050 * @see JmsMessageEndpointFactory
051 */
052public class JmsMessageEndpointManager extends GenericMessageEndpointManager
053                implements BeanNameAware, MessageListenerContainer {
054
055        private final JmsMessageEndpointFactory endpointFactory = new JmsMessageEndpointFactory();
056
057        private boolean messageListenerSet = false;
058
059        private JmsActivationSpecFactory activationSpecFactory = new DefaultJmsActivationSpecFactory();
060
061        private JmsActivationSpecConfig activationSpecConfig;
062
063
064        /**
065         * Set the JMS MessageListener for this endpoint.
066         * <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory.
067         * @see JmsMessageEndpointFactory#setMessageListener
068         */
069        public void setMessageListener(MessageListener messageListener) {
070                this.endpointFactory.setMessageListener(messageListener);
071                this.messageListenerSet = true;
072        }
073
074        /**
075         * Return the JMS MessageListener for this endpoint.
076         */
077        public MessageListener getMessageListener() {
078                return this.endpointFactory.getMessageListener();
079        }
080
081        /**
082         * Set the XA transaction manager to use for wrapping endpoint
083         * invocations, enlisting the endpoint resource in each such transaction.
084         * <p>The passed-in object may be a transaction manager which implements
085         * Spring's {@link org.springframework.transaction.jta.TransactionFactory}
086         * interface, or a plain {@link javax.transaction.TransactionManager}.
087         * <p>If no transaction manager is specified, the endpoint invocation
088         * will simply not be wrapped in an XA transaction. Consult your
089         * resource provider's ActivationSpec documentation for the local
090         * transaction options of your particular provider.
091         * <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory.
092         * @see JmsMessageEndpointFactory#setTransactionManager
093         */
094        public void setTransactionManager(Object transactionManager) {
095                this.endpointFactory.setTransactionManager(transactionManager);
096        }
097
098        /**
099         * Set the factory for concrete JCA 1.5 ActivationSpec objects,
100         * creating JCA ActivationSpecs based on
101         * {@link #setActivationSpecConfig JmsActivationSpecConfig} objects.
102         * <p>This factory is dependent on the concrete JMS provider, e.g. on ActiveMQ.
103         * The default implementation simply guesses the ActivationSpec class name
104         * from the provider's class name (e.g. "ActiveMQResourceAdapter" ->
105         * "ActiveMQActivationSpec" in the same package), and populates the
106         * ActivationSpec properties as suggested by the JCA 1.5 specification
107         * (plus a couple of autodetected vendor-specific properties).
108         * @see DefaultJmsActivationSpecFactory
109         */
110        public void setActivationSpecFactory(JmsActivationSpecFactory activationSpecFactory) {
111                this.activationSpecFactory =
112                                (activationSpecFactory != null ? activationSpecFactory : new DefaultJmsActivationSpecFactory());
113        }
114
115        /**
116         * Set the DestinationResolver to use for resolving destination names
117         * into the JCA 1.5 ActivationSpec "destination" property.
118         * <p>If not specified, destination names will simply be passed in as Strings.
119         * If specified, destination names will be resolved into Destination objects first.
120         * <p>Note that a DestinationResolver is usually specified on the JmsActivationSpecFactory
121         * (see {@link StandardJmsActivationSpecFactory#setDestinationResolver}). This is simply
122         * a shortcut for parameterizing the default JmsActivationSpecFactory; it will replace
123         * any custom JmsActivationSpecFactory that might have been set before.
124         * @see StandardJmsActivationSpecFactory#setDestinationResolver
125         */
126        public void setDestinationResolver(DestinationResolver destinationResolver) {
127                DefaultJmsActivationSpecFactory factory = new DefaultJmsActivationSpecFactory();
128                factory.setDestinationResolver(destinationResolver);
129                this.activationSpecFactory = factory;
130        }
131
132        /**
133         * Specify the {@link JmsActivationSpecConfig} object that this endpoint manager
134         * should use for activating its listener.
135         * <p>This config object will be turned into a concrete JCA 1.5 ActivationSpec
136         * object through a {@link #setActivationSpecFactory JmsActivationSpecFactory}.
137         */
138        public void setActivationSpecConfig(JmsActivationSpecConfig activationSpecConfig) {
139                this.activationSpecConfig = activationSpecConfig;
140        }
141
142        /**
143         * Return the {@link JmsActivationSpecConfig} object that this endpoint manager
144         * should use for activating its listener. Return {@code null} if none is set.
145         */
146        public JmsActivationSpecConfig getActivationSpecConfig() {
147                return this.activationSpecConfig;
148        }
149
150        /**
151         * Set the name of this message endpoint. Populated with the bean name
152         * automatically when defined within Spring's bean factory.
153         */
154        @Override
155        public void setBeanName(String beanName) {
156                this.endpointFactory.setBeanName(beanName);
157        }
158
159
160        @Override
161        public void afterPropertiesSet() throws ResourceException {
162                if (this.messageListenerSet) {
163                        setMessageEndpointFactory(this.endpointFactory);
164                }
165                if (this.activationSpecConfig != null) {
166                        setActivationSpec(
167                                        this.activationSpecFactory.createActivationSpec(getResourceAdapter(), this.activationSpecConfig));
168                }
169                super.afterPropertiesSet();
170        }
171
172
173        @Override
174        public void setupMessageListener(Object messageListener) {
175                if (messageListener instanceof MessageListener) {
176                        setMessageListener((MessageListener) messageListener);
177                }
178                else {
179                        throw new IllegalArgumentException("Unsupported message listener '" +
180                                        messageListener.getClass().getName() + "': only '" + MessageListener.class.getName() +
181                                        "' type is supported");
182                }
183        }
184
185        @Override
186        public MessageConverter getMessageConverter() {
187                JmsActivationSpecConfig config = getActivationSpecConfig();
188                if (config != null) {
189                        return config.getMessageConverter();
190                }
191                return null;
192        }
193
194        @Override
195        public DestinationResolver getDestinationResolver() {
196                if (this.activationSpecFactory instanceof StandardJmsActivationSpecFactory) {
197                        return ((StandardJmsActivationSpecFactory) this.activationSpecFactory).getDestinationResolver();
198                }
199                return null;
200        }
201
202        @Override
203        public boolean isPubSubDomain() {
204                JmsActivationSpecConfig config = getActivationSpecConfig();
205                if (config != null) {
206                        return config.isPubSubDomain();
207                }
208                throw new IllegalStateException("Could not determine pubSubDomain - no activation spec config is set");
209        }
210
211        @Override
212        public boolean isReplyPubSubDomain() {
213                JmsActivationSpecConfig config = getActivationSpecConfig();
214                if (config != null) {
215                        return config.isReplyPubSubDomain();
216                }
217                throw new IllegalStateException("Could not determine reply pubSubDomain - no activation spec config is set");
218        }
219
220}