001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.jca.endpoint;
018:
019: import javax.resource.ResourceException;
020: import javax.resource.spi.ActivationSpec;
021: import javax.resource.spi.ResourceAdapter;
022: import javax.resource.spi.endpoint.MessageEndpointFactory;
023:
024: import org.springframework.beans.factory.DisposableBean;
025: import org.springframework.beans.factory.InitializingBean;
026: import org.springframework.context.Lifecycle;
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: * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager">
040: * <property name="resourceAdapter" ref="resourceAdapter"/>
041: * <property name="messageEndpointFactory">
042: * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory">
043: * <property name="messageListener" ref="messageListener"/>
044: * </bean>
045: * </property>
046: * <property name="activationSpec">
047: * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
048: * <property name="destination" value="myQueue"/>
049: * <property name="destinationType" value="javax.jms.Queue"/>
050: * </bean>
051: * </property>
052: * </bean></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: * <bean id="messageListener" class="com.myorg.messaging.myMessageListener">
062: * ...
063: * </bean></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: * <bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean">
072: * <property name="resourceAdapter">
073: * <bean class="org.apache.activemq.ra.ActiveMQResourceAdapter">
074: * <property name="serverUrl" value="tcp://localhost:61616"/>
075: * </bean>
076: * </property>
077: * <property name="workManager">
078: * <bean class="org.springframework.jca.work.SimpleTaskWorkManager"/>
079: * </property>
080: * </bean></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: * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager">
102: * <property name="resourceAdapter" ref="resourceAdapter"/>
103: * <property name="messageEndpointFactory">
104: * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory">
105: * <property name="messageListener" ref="messageListener"/>
106: * <property name="transactionManager" ref="transactionManager"/>
107: * </bean>
108: * </property>
109: * <property name="activationSpec">
110: * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
111: * <property name="destination" value="myQueue"/>
112: * <property name="destinationType" value="javax.jms.Queue"/>
113: * </bean>
114: * </property>
115: * </bean>
116: *
117: * <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/></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: * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager">
125: * <property name="resourceAdapter" ref="resourceAdapter"/>
126: * <property name="messageEndpointFactory">
127: * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory">
128: * <property name="messageListener" ref="messageListener"/>
129: * </bean>
130: * </property>
131: * <property name="activationSpec">
132: * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
133: * <property name="destination" value="myQueue"/>
134: * <property name="destinationType" value="javax.jms.Queue"/>
135: * <property name="useRAManagedTransaction" value="true"/>
136: * </bean>
137: * </property>
138: * </bean></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: */
147: public class GenericMessageEndpointManager implements InitializingBean,
148: Lifecycle, DisposableBean {
149:
150: private ResourceAdapter resourceAdapter;
151:
152: private MessageEndpointFactory messageEndpointFactory;
153:
154: private ActivationSpec activationSpec;
155:
156: private boolean autoStartup = true;
157:
158: private boolean running = false;
159:
160: private final Object lifecycleMonitor = new Object();
161:
162: /**
163: * Set the JCA ResourceAdapter to manage endpoints for.
164: */
165: public void setResourceAdapter(ResourceAdapter resourceAdapter) {
166: this .resourceAdapter = resourceAdapter;
167: }
168:
169: /**
170: * Return the JCA ResourceAdapter to manage endpoints for.
171: */
172: public ResourceAdapter getResourceAdapter() {
173: return this .resourceAdapter;
174: }
175:
176: /**
177: * Set the JCA MessageEndpointFactory to activate, pointing to a
178: * MessageListener object that the endpoints will delegate to.
179: * <p>A MessageEndpointFactory instance may be shared across multiple
180: * endpoints (i.e. multiple GenericMessageEndpointManager instances),
181: * with different {@link #setActivationSpec ActivationSpec} objects applied.
182: * @see GenericMessageEndpointFactory#setMessageListener
183: */
184: public void setMessageEndpointFactory(
185: MessageEndpointFactory messageEndpointFactory) {
186: this .messageEndpointFactory = messageEndpointFactory;
187: }
188:
189: /**
190: * Return the JCA MessageEndpointFactory to activate.
191: */
192: public MessageEndpointFactory getMessageEndpointFactory() {
193: return this .messageEndpointFactory;
194: }
195:
196: /**
197: * Set the JCA ActivationSpec to use for activating the endpoint.
198: * <p>Note that this ActivationSpec instance should not be shared
199: * across multiple ResourceAdapter instances.
200: */
201: public void setActivationSpec(ActivationSpec activationSpec) {
202: this .activationSpec = activationSpec;
203: }
204:
205: /**
206: * Return the JCA ActivationSpec to use for activating the endpoint.
207: */
208: public ActivationSpec getActivationSpec() {
209: return this .activationSpec;
210: }
211:
212: /**
213: * Set whether to auto-start the endpoint activation along with
214: * this endpoint manager's initialization.
215: * <p>Default is "true". Turn this flag off to defer the endpoint
216: * activation until an explicit {#start()} call.
217: */
218: public void setAutoStartup(boolean autoStartup) {
219: this .autoStartup = autoStartup;
220: }
221:
222: /**
223: * Prepares the message endpoint, and automatically activates it
224: * if the "autoStartup" flag is set to "true".
225: */
226: public void afterPropertiesSet() throws ResourceException {
227: if (getResourceAdapter() == null) {
228: throw new IllegalArgumentException(
229: "Property 'resourceAdapter' is required");
230: }
231: if (getMessageEndpointFactory() == null) {
232: throw new IllegalArgumentException(
233: "Property 'messageEndpointFactory' is required");
234: }
235: ActivationSpec activationSpec = getActivationSpec();
236: if (activationSpec == null) {
237: throw new IllegalArgumentException(
238: "Property 'activationSpec' is required");
239: }
240:
241: if (activationSpec.getResourceAdapter() == null) {
242: activationSpec.setResourceAdapter(getResourceAdapter());
243: } else if (activationSpec.getResourceAdapter() != getResourceAdapter()) {
244: throw new IllegalArgumentException(
245: "ActivationSpec ["
246: + activationSpec
247: + "] is associated with a different ResourceAdapter: "
248: + activationSpec.getResourceAdapter());
249: }
250:
251: if (this .autoStartup) {
252: start();
253: }
254: }
255:
256: /**
257: * Activates the configured message endpoint.
258: */
259: public void start() {
260: synchronized (this .lifecycleMonitor) {
261: if (!this .running) {
262: try {
263: getResourceAdapter().endpointActivation(
264: getMessageEndpointFactory(),
265: getActivationSpec());
266: } catch (ResourceException ex) {
267: IllegalStateException wrapped = new IllegalStateException(
268: "Could not activate message endpoint");
269: wrapped.initCause(ex);
270: throw wrapped;
271: }
272: this .running = true;
273: }
274: }
275: }
276:
277: /**
278: * Deactivates the configured message endpoint.
279: */
280: public void stop() {
281: synchronized (this .lifecycleMonitor) {
282: if (this .running) {
283: getResourceAdapter().endpointDeactivation(
284: getMessageEndpointFactory(),
285: getActivationSpec());
286: this .running = false;
287: }
288: }
289: }
290:
291: /**
292: * Return whether the configured message endpoint is currently active.
293: */
294: public boolean isRunning() {
295: synchronized (this .lifecycleMonitor) {
296: return this .running;
297: }
298: }
299:
300: /**
301: * Deactivates the message endpoint, preparing it for shutdown.
302: */
303: public void destroy() {
304: stop();
305: }
306:
307: }
|