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