001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: CMTRequiresNewTransactionInterceptor.java 1970 2007-10-16 11:49:25Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.transaction.interceptors;
025:
026: import static javax.transaction.Status.STATUS_ACTIVE;
027: import static javax.transaction.Status.STATUS_MARKED_ROLLBACK;
028:
029: import javax.ejb.EJBException;
030: import javax.ejb.TransactionRolledbackLocalException;
031: import javax.transaction.InvalidTransactionException;
032: import javax.transaction.NotSupportedException;
033: import javax.transaction.RollbackException;
034: import javax.transaction.SystemException;
035: import javax.transaction.Transaction;
036:
037: import org.ow2.easybeans.api.EasyBeansInvocationContext;
038: import org.ow2.util.log.Log;
039: import org.ow2.util.log.LogFactory;
040:
041: /**
042: * Defines an interceptor for method using the REQUIRES_NEW attribute.
043: * @author Florent Benoit
044: */
045: public class CMTRequiresNewTransactionInterceptor extends
046: AbsTransactionInterceptor {
047:
048: /**
049: * Logger.
050: */
051: private Log logger = LogFactory
052: .getLog(CMTRequiresNewTransactionInterceptor.class);
053:
054: /**
055: * Constructor.<br>
056: * Acquire the transaction manager.
057: */
058: public CMTRequiresNewTransactionInterceptor() {
059: super ();
060: }
061:
062: /**
063: * Execute transaction as specified with the REQUIRES_NEW attribute.
064: * @param invocationContext context with useful attributes on the current
065: * invocation
066: * @return result of the next invocation (to chain interceptors)
067: * @throws Exception if interceptor fails
068: * @see <a href="http://www.jcp.org/en/jsr/detail?id=220">EJB 3.0
069: * specification 12.6.2.4</a>
070: */
071: @Override
072: public Object intercept(
073: final EasyBeansInvocationContext invocationContext)
074: throws Exception {
075: logger.debug("Calling RequiresNew TX interceptor");
076:
077: // Get current transaction
078: Transaction transaction;
079: try {
080: transaction = getTransactionManager().getTransaction();
081: } catch (SystemException se) {
082: throw new EJBException(
083: "Cannot get the current transaction on transaction manager.",
084: se);
085: }
086:
087: logger.debug("Transaction found = {0}", transaction);
088:
089: /*
090: * If a client calls with a transaction context, the container
091: * suspends the association of the transaction context with the
092: * current thread before starting the new transaction and invoking
093: * the business method. The container resumes the suspended
094: * transaction association after the business method and the new
095: * transaction have been completed.
096: */
097: // Existing transaction
098: Transaction suspendedTransaction = null;
099: if (transaction != null) {
100: try {
101: logger.debug("Suspending transaction {0}", transaction);
102: suspendedTransaction = getTransactionManager()
103: .suspend();
104: } catch (SystemException se) {
105: throw new EJBException(
106: "Cannot call suspend() on the transaction manager.",
107: se);
108: }
109: }
110:
111: /*
112: * If the client invokes the enterprise bean's method while the client
113: * is not associated with a transaction context, the container
114: * automatically starts a new transaction before delegating a method
115: * call to the enterprise bean business method. The container
116: * automatically enlists all the resource managers accessed by the
117: * business method with the transaction. If the business method invokes
118: * other enterprise beans, the container passes the transaction context
119: * with the invocation. The container attempts to commit the transaction
120: * when the business method has completed. The container performs the
121: * commit protocol before the method result is sent to the client.
122: */
123: try {
124: getTransactionManager().begin();
125: } catch (NotSupportedException nse) {
126: throw new EJBException(
127: "Transaction Manager implementation does not support nested transactions.",
128: nse);
129: } catch (SystemException se) {
130: throw new EJBException(
131: "Cannot call begin() on the transaction manager.",
132: se);
133: }
134:
135: boolean gotBusinessException = false;
136: try {
137: return invocationContext.proceed();
138: } catch (Exception e) {
139: gotBusinessException = true;
140: handleContextContainerTransaction(invocationContext, e);
141:
142: // Shouldn't come here
143: return null;
144: } finally {
145:
146: // only do some operations if transaction has been started before
147: // invoking the method.
148: if (!gotBusinessException) {
149: // sanity check.
150: Transaction transactionAfter = null;
151: try {
152: transactionAfter = getTransactionManager()
153: .getTransaction();
154: } catch (SystemException se) {
155: throw new EJBException(
156: "Cannot get the current transaction on transaction manager.",
157: se);
158: }
159:
160: if (transactionAfter == null) {
161: throw new EJBException("Transaction disappeared.");
162: }
163:
164: /*
165: * The container attempts to commit the transaction when the
166: * business method has completed. The container performs the
167: * commit protocol before the method result is sent to the
168: * client.
169: */
170: try {
171: switch (getTransactionManager().getStatus()) {
172: case STATUS_ACTIVE:
173: getTransactionManager().commit();
174: break;
175: case STATUS_MARKED_ROLLBACK:
176: getTransactionManager().rollback();
177: break;
178: default:
179: throw new RuntimeException(
180: "Unexpected transaction status"
181: + getTransactionManager()
182: .getStatus());
183: }
184: } catch (RollbackException e) {
185: throw new TransactionRolledbackLocalException(
186: "Could not commit transaction", e);
187: } catch (Exception e) {
188: throw new EJBException("Container exception", e);
189: }
190: }
191:
192: // if it was suspended
193: if (suspendedTransaction != null) {
194:
195: logger.debug("Resuming transaction {0}", transaction);
196:
197: try {
198: getTransactionManager()
199: .resume(suspendedTransaction);
200: } catch (InvalidTransactionException ite) {
201: throw new EJBException(
202: "Cannot call resume() on the given transaction. There is an invalid transaction",
203: ite);
204: } catch (IllegalStateException ise) {
205: throw new EJBException(
206: "Cannot call resume() on the given transaction. There is another associated transaction",
207: ise);
208: } catch (SystemException se) {
209: throw new EJBException(
210: "Cannot call resume() on the given transaction. Unexpected error condition",
211: se);
212: }
213: }
214: }
215: }
216:
217: }
|