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 java.lang.reflect.Method;
020:
021: import javax.resource.ResourceException;
022: import javax.resource.spi.ApplicationServerInternalException;
023: import javax.resource.spi.UnavailableException;
024: import javax.resource.spi.endpoint.MessageEndpoint;
025: import javax.resource.spi.endpoint.MessageEndpointFactory;
026: import javax.transaction.Transaction;
027: import javax.transaction.TransactionManager;
028: import javax.transaction.xa.XAResource;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import org.springframework.transaction.jta.SimpleTransactionFactory;
034: import org.springframework.transaction.jta.TransactionFactory;
035:
036: /**
037: * Abstract base implementation of the JCA 1.5
038: * {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
039: * providing transaction management capabilities as well as ClassLoader
040: * exposure for endpoint invocations.
041: *
042: * @author Juergen Hoeller
043: * @since 2.5
044: * @see #setTransactionManager
045: */
046: public abstract class AbstractMessageEndpointFactory implements
047: MessageEndpointFactory {
048:
049: /** Logger available to subclasses */
050: protected final Log logger = LogFactory.getLog(getClass());
051:
052: private TransactionFactory transactionFactory;
053:
054: private String transactionName;
055:
056: private int transactionTimeout = -1;
057:
058: /**
059: * Set the the XA transaction manager to use for wrapping endpoint
060: * invocations, enlisting the endpoint resource in each such transaction.
061: * <p>The passed-in object may be a transaction manager which implements
062: * Spring's {@link org.springframework.transaction.jta.TransactionFactory}
063: * interface, or a plain {@link javax.transaction.TransactionManager}.
064: * <p>If no transaction manager is specified, the endpoint invocation
065: * will simply not be wrapped in an XA transaction. Check out your
066: * resource provider's ActivationSpec documentation for local
067: * transaction options of your particular provider.
068: * @see #setTransactionName
069: * @see #setTransactionTimeout
070: */
071: public void setTransactionManager(Object transactionManager) {
072: if (transactionManager instanceof TransactionFactory) {
073: this .transactionFactory = (TransactionFactory) transactionManager;
074: } else if (transactionManager instanceof TransactionManager) {
075: this .transactionFactory = new SimpleTransactionFactory(
076: (TransactionManager) transactionManager);
077: } else {
078: throw new IllegalArgumentException(
079: "Transaction manager ["
080: + transactionManager
081: + "] is neither a [org.springframework.transaction.jta.TransactionFactory} nor a "
082: + "[javax.transaction.TransactionManager]");
083: }
084: }
085:
086: /**
087: * Set the Spring TransactionFactory to use for wrapping endpoint
088: * invocations, enlisting the endpoint resource in each such transaction.
089: * <p>Alternatively, specify an appropriate transaction manager through
090: * the {@link #setTransactionManager "transactionManager"} property.
091: * <p>If no transaction factory is specified, the endpoint invocation
092: * will simply not be wrapped in an XA transaction. Check out your
093: * resource provider's ActivationSpec documentation for local
094: * transaction options of your particular provider.
095: * @see #setTransactionName
096: * @see #setTransactionTimeout
097: */
098: public void setTransactionFactory(
099: TransactionFactory transactionFactory) {
100: this .transactionFactory = transactionFactory;
101: }
102:
103: /**
104: * Specify the name of the transaction, if any.
105: * <p>Default is none. A specified name will be passed on to the transaction
106: * manager, allowing to identify the transaction in a transaction monitor.
107: */
108: public void setTransactionName(String transactionName) {
109: this .transactionName = transactionName;
110: }
111:
112: /**
113: * Specify the transaction timeout, if any.
114: * <p>Default is -1: rely on the transaction manager's default timeout.
115: * Specify a concrete timeout to restrict the maximum duration of each
116: * endpoint invocation.
117: */
118: public void setTransactionTimeout(int transactionTimeout) {
119: this .transactionTimeout = transactionTimeout;
120: }
121:
122: /**
123: * This implementation returns <code>true</code> if a transaction manager
124: * has been specified; <code>false</code> otherwise.
125: * @see #setTransactionManager
126: * @see #setTransactionFactory
127: */
128: public boolean isDeliveryTransacted(Method method)
129: throws NoSuchMethodException {
130: return (this .transactionFactory != null);
131: }
132:
133: /**
134: * This implementation delegates to {@link #createEndpointInternal()},
135: * initializing the endpoint's XAResource before the endpoint gets invoked.
136: */
137: public MessageEndpoint createEndpoint(XAResource xaResource)
138: throws UnavailableException {
139: AbstractMessageEndpoint endpoint = createEndpointInternal();
140: endpoint.initXAResource(xaResource);
141: return endpoint;
142: }
143:
144: /**
145: * Create the actual endpoint instance, as a subclass of the
146: * {@link AbstractMessageEndpoint} inner class of this factory.
147: * @return the actual endpoint instance (never <code>null</code>)
148: * @throws UnavailableException if no endpoint is available at present
149: */
150: protected abstract AbstractMessageEndpoint createEndpointInternal()
151: throws UnavailableException;
152:
153: /**
154: * Inner class for actual endpoint implementations, based on template
155: * method to allow for any kind of concrete endpoint implementation.
156: */
157: protected abstract class AbstractMessageEndpoint implements
158: MessageEndpoint {
159:
160: private TransactionDelegate transactionDelegate;
161:
162: private boolean beforeDeliveryCalled = false;
163:
164: private ClassLoader previousContextClassLoader;
165:
166: /**
167: * Initialize this endpoint's TransactionDelegate.
168: * @param xaResource the XAResource for this endpoint
169: */
170: void initXAResource(XAResource xaResource) {
171: this .transactionDelegate = new TransactionDelegate(
172: xaResource);
173: }
174:
175: /**
176: * This <code>beforeDelivery</code> implementation starts a transaction,
177: * if necessary, and exposes the endpoint ClassLoader as current
178: * thread context ClassLoader.
179: * <p>Note that the JCA 1.5 specification does not require a ResourceAdapter
180: * to call this method before invoking the concrete endpoint. If this method
181: * has not been called (check {@link #hasBeforeDeliveryBeenCalled()}), the
182: * concrete endpoint method should call <code>beforeDelivery</code> and its
183: * sibling {@link #afterDelivery()} explicitly, as part of its own processing.
184: */
185: public void beforeDelivery(Method method)
186: throws ResourceException {
187: this .beforeDeliveryCalled = true;
188: try {
189: this .transactionDelegate.beginTransaction();
190: } catch (Throwable ex) {
191: throw new ApplicationServerInternalException(
192: "Failed to begin transaction", ex);
193: }
194: Thread currentThread = Thread.currentThread();
195: this .previousContextClassLoader = currentThread
196: .getContextClassLoader();
197: currentThread
198: .setContextClassLoader(getEndpointClassLoader());
199: }
200:
201: /**
202: * Template method for exposing the endpoint's ClassLoader
203: * (typically the ClassLoader that the message listener class
204: * has been loaded with).
205: * @return the endpoint ClassLoader (never <code>null</code>)
206: */
207: protected abstract ClassLoader getEndpointClassLoader();
208:
209: /**
210: * Return whether the {@link #beforeDelivery} method of this endpoint
211: * has already been called.
212: */
213: protected final boolean hasBeforeDeliveryBeenCalled() {
214: return this .beforeDeliveryCalled;
215: }
216:
217: /**
218: * Callback method for notifying the endpoint base class
219: * that the concrete endpoint invocation led to an exception.
220: * <p>To be invoked by subclasses in case of the concrete
221: * endpoint throwing an exception.
222: * @param ex the exception thrown from the concrete endpoint
223: */
224: protected final void onEndpointException(Throwable ex) {
225: this .transactionDelegate.setRollbackOnly();
226: }
227:
228: /**
229: * This <code>afterDelivery</code> implementation resets the thread context
230: * ClassLoader and completes the transaction, if any.
231: * <p>Note that the JCA 1.5 specification does not require a ResourceAdapter
232: * to call this method after invoking the concrete endpoint. See the
233: * explanation in {@link #beforeDelivery}'s javadoc.
234: */
235: public void afterDelivery() throws ResourceException {
236: this .beforeDeliveryCalled = false;
237: Thread.currentThread().setContextClassLoader(
238: this .previousContextClassLoader);
239: this .previousContextClassLoader = null;
240: try {
241: this .transactionDelegate.endTransaction();
242: } catch (Throwable ex) {
243: throw new ApplicationServerInternalException(
244: "Failed to complete transaction", ex);
245: }
246: }
247:
248: public void release() {
249: try {
250: this .transactionDelegate.setRollbackOnly();
251: this .transactionDelegate.endTransaction();
252: } catch (Throwable ex) {
253: logger
254: .error(
255: "Could not complete unfinished transaction on endpoint release",
256: ex);
257: }
258: }
259: }
260:
261: /**
262: * Private inner class that performs the actual transaction handling,
263: * including enlistment of the endpoint's XAResource.
264: */
265: private class TransactionDelegate {
266:
267: private XAResource xaResource;
268:
269: private Transaction transaction;
270:
271: private boolean rollbackOnly;
272:
273: public TransactionDelegate(XAResource xaResource) {
274: if (transactionFactory != null && xaResource == null) {
275: throw new IllegalStateException(
276: "ResourceAdapter-provided XAResource is required for "
277: + "transaction management. Check your ResourceAdapter's configuration.");
278: }
279: this .xaResource = xaResource;
280: }
281:
282: public void beginTransaction() throws Exception {
283: if (transactionFactory != null) {
284: this .transaction = transactionFactory
285: .createTransaction(transactionName,
286: transactionTimeout);
287: this .transaction.enlistResource(this .xaResource);
288: }
289: }
290:
291: public void setRollbackOnly() {
292: if (this .transaction != null) {
293: this .rollbackOnly = true;
294: }
295: }
296:
297: public void endTransaction() throws Exception {
298: if (this .transaction != null) {
299: try {
300: if (this .rollbackOnly) {
301: this .transaction.rollback();
302: } else {
303: this .transaction.commit();
304: }
305: } finally {
306: this .transaction = null;
307: this .rollbackOnly = false;
308: }
309: }
310: }
311: }
312:
313: }
|