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