001/*
002 * Copyright 2002-2018 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.jca.endpoint;
018
019import javax.resource.ResourceException;
020import javax.resource.spi.ActivationSpec;
021import javax.resource.spi.ResourceAdapter;
022import javax.resource.spi.endpoint.MessageEndpointFactory;
023
024import org.springframework.beans.factory.DisposableBean;
025import org.springframework.beans.factory.InitializingBean;
026import org.springframework.context.SmartLifecycle;
027
028/**
029 * Generic bean that manages JCA 1.5 message endpoints within a Spring
030 * application context, activating and deactivating the endpoint as part
031 * of the application context's lifecycle.
032 *
033 * <p>This class is completely generic in that it may work with any
034 * ResourceAdapter, any MessageEndpointFactory, and any ActivationSpec.
035 * It can be configured in standard bean style, for example through
036 * Spring's XML bean definition format, as follows:
037 *
038 * <pre class="code">
039 * &lt;bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"&gt;
040 *       &lt;property name="resourceAdapter" ref="resourceAdapter"/&gt;
041 *       &lt;property name="messageEndpointFactory"&gt;
042 *     &lt;bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"&gt;
043 *       &lt;property name="messageListener" ref="messageListener"/&gt;
044 *     &lt;/bean&gt;
045 *       &lt;/property&gt;
046 *       &lt;property name="activationSpec"&gt;
047 *     &lt;bean class="org.apache.activemq.ra.ActiveMQActivationSpec"&gt;
048 *       &lt;property name="destination" value="myQueue"/&gt;
049 *       &lt;property name="destinationType" value="javax.jms.Queue"/&gt;
050 *     &lt;/bean&gt;
051 *   &lt;/property&gt;
052 * &lt;/bean&gt;</pre>
053 *
054 * In this example, Spring's own {@link GenericMessageEndpointFactory} is used
055 * to point to a standard message listener object that happens to be supported
056 * by the specified target ResourceAdapter: in this case, a JMS
057 * {@link javax.jms.MessageListener} object as supported by the ActiveMQ
058 * message broker, defined as a Spring bean:
059 *
060 * <pre class="code">
061 * &lt;bean id="messageListener" class="com.myorg.messaging.myMessageListener"&gt;
062 *   ...
063 * &lt;/bean&gt;</pre>
064 *
065 * The target ResourceAdapter may be configured as a local Spring bean as well
066 * (the typical case) or obtained from JNDI (e.g. on WebLogic). For the
067 * example above, a local ResourceAdapter bean could be defined as follows
068 * (matching the "resourceAdapter" bean reference above):
069 *
070 * <pre class="code">
071 * &lt;bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean"&gt;
072 *   &lt;property name="resourceAdapter"&gt;
073 *     &lt;bean class="org.apache.activemq.ra.ActiveMQResourceAdapter"&gt;
074 *       &lt;property name="serverUrl" value="tcp://localhost:61616"/&gt;
075 *     &lt;/bean&gt;
076 *   &lt;/property&gt;
077 *   &lt;property name="workManager"&gt;
078 *     &lt;bean class="org.springframework.jca.work.SimpleTaskWorkManager"/&gt;
079 *   &lt;/property&gt;
080 * &lt;/bean&gt;</pre>
081 *
082 * For a different target resource, the configuration would simply point to a
083 * different ResourceAdapter and a different ActivationSpec object (which are
084 * both specific to the resource provider), and possibly a different message
085 * listener (e.g. a CCI {@link javax.resource.cci.MessageListener} for a
086 * resource adapter which is based on the JCA Common Client Interface).
087 *
088 * <p>The asynchronous execution strategy can be customized through the
089 * "workManager" property on the ResourceAdapterFactoryBean (as shown above).
090 * Check out {@link org.springframework.jca.work.SimpleTaskWorkManager}'s
091 * javadoc for its configuration options; alternatively, any other
092 * JCA-compliant WorkManager can be used (e.g. Geronimo's).
093 *
094 * <p>Transactional execution is a responsibility of the concrete message endpoint,
095 * as built by the specified MessageEndpointFactory. {@link GenericMessageEndpointFactory}
096 * supports XA transaction participation through its "transactionManager" property,
097 * typically with a Spring {@link org.springframework.transaction.jta.JtaTransactionManager}
098 * or a plain {@link javax.transaction.TransactionManager} implementation specified there.
099 *
100 * <pre class="code">
101 * &lt;bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"&gt;
102 *       &lt;property name="resourceAdapter" ref="resourceAdapter"/&gt;
103 *       &lt;property name="messageEndpointFactory"&gt;
104 *     &lt;bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"&gt;
105 *       &lt;property name="messageListener" ref="messageListener"/&gt;
106 *       &lt;property name="transactionManager" ref="transactionManager"/&gt;
107 *     &lt;/bean&gt;
108 *       &lt;/property&gt;
109 *       &lt;property name="activationSpec"&gt;
110 *     &lt;bean class="org.apache.activemq.ra.ActiveMQActivationSpec"&gt;
111 *       &lt;property name="destination" value="myQueue"/&gt;
112 *       &lt;property name="destinationType" value="javax.jms.Queue"/&gt;
113 *     &lt;/bean&gt;
114 *   &lt;/property&gt;
115 * &lt;/bean&gt;
116 *
117 * &lt;bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/&gt;</pre>
118 *
119 * Alternatively, check out your resource provider's ActivationSpec object,
120 * which should support local transactions through a provider-specific config flag,
121 * e.g. ActiveMQActivationSpec's "useRAManagedTransaction" bean property.
122 *
123 * <pre class="code">
124 * &lt;bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"&gt;
125 *       &lt;property name="resourceAdapter" ref="resourceAdapter"/&gt;
126 *       &lt;property name="messageEndpointFactory"&gt;
127 *     &lt;bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"&gt;
128 *       &lt;property name="messageListener" ref="messageListener"/&gt;
129 *     &lt;/bean&gt;
130 *       &lt;/property&gt;
131 *       &lt;property name="activationSpec"&gt;
132 *     &lt;bean class="org.apache.activemq.ra.ActiveMQActivationSpec"&gt;
133 *       &lt;property name="destination" value="myQueue"/&gt;
134 *       &lt;property name="destinationType" value="javax.jms.Queue"/&gt;
135 *       &lt;property name="useRAManagedTransaction" value="true"/&gt;
136 *     &lt;/bean&gt;
137 *   &lt;/property&gt;
138 * &lt;/bean&gt;</pre>
139 *
140 * @author Juergen Hoeller
141 * @since 2.5
142 * @see javax.resource.spi.ResourceAdapter#endpointActivation
143 * @see javax.resource.spi.ResourceAdapter#endpointDeactivation
144 * @see javax.resource.spi.endpoint.MessageEndpointFactory
145 * @see javax.resource.spi.ActivationSpec
146 */
147public class GenericMessageEndpointManager implements SmartLifecycle, InitializingBean, DisposableBean {
148
149        private ResourceAdapter resourceAdapter;
150
151        private MessageEndpointFactory messageEndpointFactory;
152
153        private ActivationSpec activationSpec;
154
155        private boolean autoStartup = true;
156
157        private int phase = Integer.MAX_VALUE;
158
159        private volatile boolean running = false;
160
161        private final Object lifecycleMonitor = new Object();
162
163
164        /**
165         * Set the JCA ResourceAdapter to manage endpoints for.
166         */
167        public void setResourceAdapter(ResourceAdapter resourceAdapter) {
168                this.resourceAdapter = resourceAdapter;
169        }
170
171        /**
172         * Return the JCA ResourceAdapter to manage endpoints for.
173         */
174        public ResourceAdapter getResourceAdapter() {
175                return this.resourceAdapter;
176        }
177
178        /**
179         * Set the JCA MessageEndpointFactory to activate, pointing to a
180         * MessageListener object that the endpoints will delegate to.
181         * <p>A MessageEndpointFactory instance may be shared across multiple
182         * endpoints (i.e. multiple GenericMessageEndpointManager instances),
183         * with different {@link #setActivationSpec ActivationSpec} objects applied.
184         * @see GenericMessageEndpointFactory#setMessageListener
185         */
186        public void setMessageEndpointFactory(MessageEndpointFactory messageEndpointFactory) {
187                this.messageEndpointFactory = messageEndpointFactory;
188        }
189
190        /**
191         * Return the JCA MessageEndpointFactory to activate.
192         */
193        public MessageEndpointFactory getMessageEndpointFactory() {
194                return this.messageEndpointFactory;
195        }
196
197        /**
198         * Set the JCA ActivationSpec to use for activating the endpoint.
199         * <p>Note that this ActivationSpec instance should not be shared
200         * across multiple ResourceAdapter instances.
201         */
202        public void setActivationSpec(ActivationSpec activationSpec) {
203                this.activationSpec = activationSpec;
204        }
205
206        /**
207         * Return the JCA ActivationSpec to use for activating the endpoint.
208         */
209        public ActivationSpec getActivationSpec() {
210                return this.activationSpec;
211        }
212
213        /**
214         * Set whether to auto-start the endpoint activation after this endpoint
215         * manager has been initialized and the context has been refreshed.
216         * <p>Default is "true". Turn this flag off to defer the endpoint
217         * activation until an explicit {@link #start()} call.
218         */
219        public void setAutoStartup(boolean autoStartup) {
220                this.autoStartup = autoStartup;
221        }
222
223        /**
224         * Return the value for the 'autoStartup' property.     If "true", this
225         * endpoint manager will start upon a ContextRefreshedEvent.
226         */
227        @Override
228        public boolean isAutoStartup() {
229                return this.autoStartup;
230        }
231
232        /**
233         * Specify the phase in which this endpoint manager should be started
234         * and stopped. The startup order proceeds from lowest to highest, and
235         * the shutdown order is the reverse of that. By default this value is
236         * Integer.MAX_VALUE meaning that this endpoint manager starts as late
237         * as possible and stops as soon as possible.
238         */
239        public void setPhase(int phase) {
240                this.phase = phase;
241        }
242
243        /**
244         * Return the phase in which this endpoint manager will be started and stopped.
245         */
246        @Override
247        public int getPhase() {
248                return this.phase;
249        }
250
251        /**
252         * Prepares the message endpoint, and automatically activates it
253         * if the "autoStartup" flag is set to "true".
254         */
255        @Override
256        public void afterPropertiesSet() throws ResourceException {
257                if (getResourceAdapter() == null) {
258                        throw new IllegalArgumentException("Property 'resourceAdapter' is required");
259                }
260                if (getMessageEndpointFactory() == null) {
261                        throw new IllegalArgumentException("Property 'messageEndpointFactory' is required");
262                }
263                ActivationSpec activationSpec = getActivationSpec();
264                if (activationSpec == null) {
265                        throw new IllegalArgumentException("Property 'activationSpec' is required");
266                }
267
268                if (activationSpec.getResourceAdapter() == null) {
269                        activationSpec.setResourceAdapter(getResourceAdapter());
270                }
271                else if (activationSpec.getResourceAdapter() != getResourceAdapter()) {
272                        throw new IllegalArgumentException("ActivationSpec [" + activationSpec +
273                                        "] is associated with a different ResourceAdapter: " + activationSpec.getResourceAdapter());
274                }
275        }
276
277        /**
278         * Activates the configured message endpoint.
279         */
280        @Override
281        public void start() {
282                synchronized (this.lifecycleMonitor) {
283                        if (!this.running) {
284                                try {
285                                        getResourceAdapter().endpointActivation(getMessageEndpointFactory(), getActivationSpec());
286                                }
287                                catch (ResourceException ex) {
288                                        throw new IllegalStateException("Could not activate message endpoint", ex);
289                                }
290                                this.running = true;
291                        }
292                }
293        }
294
295        /**
296         * Deactivates the configured message endpoint.
297         */
298        @Override
299        public void stop() {
300                synchronized (this.lifecycleMonitor) {
301                        if (this.running) {
302                                getResourceAdapter().endpointDeactivation(getMessageEndpointFactory(), getActivationSpec());
303                                this.running = false;
304                        }
305                }
306        }
307
308        @Override
309        public void stop(Runnable callback) {
310                synchronized (this.lifecycleMonitor) {
311                        stop();
312                        callback.run();
313                }
314        }
315
316        /**
317         * Return whether the configured message endpoint is currently active.
318         */
319        @Override
320        public boolean isRunning() {
321                return this.running;
322        }
323
324        /**
325         * Deactivates the message endpoint, preparing it for shutdown.
326         */
327        @Override
328        public void destroy() {
329                stop();
330        }
331
332}