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: AbsTransactionInterceptor.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_MARKED_ROLLBACK;
027:
028: import java.lang.reflect.Method;
029: import java.util.Map;
030:
031: import javax.ejb.ApplicationException;
032: import javax.ejb.EJBException;
033: import javax.ejb.EJBTransactionRolledbackException;
034: import javax.transaction.HeuristicMixedException;
035: import javax.transaction.HeuristicRollbackException;
036: import javax.transaction.RollbackException;
037: import javax.transaction.SystemException;
038: import javax.transaction.Transaction;
039: import javax.transaction.TransactionManager;
040:
041: import org.ow2.easybeans.api.EasyBeansInterceptor;
042: import org.ow2.easybeans.api.EasyBeansInvocationContext;
043: import org.ow2.easybeans.api.Factory;
044: import org.ow2.easybeans.api.bean.EasyBeansSFSB;
045: import org.ow2.easybeans.api.pool.Pool;
046: import org.ow2.easybeans.api.pool.PoolException;
047: import org.ow2.easybeans.transaction.JTransactionManager;
048: import org.ow2.util.log.Log;
049: import org.ow2.util.log.LogFactory;
050:
051: /**
052: * Defines an abstract interceptor for transaction with common code used by all
053: * transaction interceptors.
054: * @author Florent Benoit
055: */
056: public abstract class AbsTransactionInterceptor implements
057: EasyBeansInterceptor {
058:
059: /**
060: * Logger.
061: */
062: private Log logger = LogFactory
063: .getLog(AbsTransactionInterceptor.class);
064:
065: /**
066: * Transaction manager.
067: */
068: private TransactionManager transactionManager = null;
069:
070: /**
071: * Constructor.<br>
072: * Acquire the transaction manager.
073: */
074: public AbsTransactionInterceptor() {
075: this .transactionManager = JTransactionManager
076: .getTransactionManager();
077: }
078:
079: /**
080: * Defines the code used by the transaction interceptor on a given method.
081: * @param invocationContext context with useful attributes on the current
082: * invocation
083: * @return result of the next invocation (to chain interceptors)
084: * @throws Exception if interceptor fails
085: */
086: public abstract Object intercept(
087: final EasyBeansInvocationContext invocationContext)
088: throws Exception;
089:
090: /**
091: * Gets the transaction manager.
092: * @return TM.
093: */
094: public TransactionManager getTransactionManager() {
095: return transactionManager;
096: }
097:
098: /**
099: * Gets the application exception (if any) for the given invocation context
100: * and the given exception.<br />
101: * Note that a checked Exception is by default an application exception.
102: * @param invocationContext context that provides access to the Method
103: * object.
104: * @param e the exception to check
105: * @return the application exception object, else null.
106: */
107: protected ApplicationException getApplicationException(
108: final EasyBeansInvocationContext invocationContext,
109: final Exception e) {
110: Map<String, ApplicationException> applicationExceptions = invocationContext
111: .getFactory().getBeanInfo().getApplicationExceptions();
112: ApplicationException appException = applicationExceptions.get(e
113: .getClass().getName());
114: if (appException != null) {
115: return appException;
116: }
117: // If runtime exception, not an application by default
118: if (e instanceof RuntimeException) {
119: return null;
120: }
121: // Is it a checked Exception ?
122: Method method = invocationContext.getMethod();
123: if (method != null) {
124: Class[] exceptions = method.getExceptionTypes();
125: if (exceptions != null) {
126: for (Class clazz : exceptions) {
127: // Is an Exception but not a runtime exception
128: if (clazz.isInstance(e)
129: && !(e instanceof RuntimeException)) {
130: // Checked exception, so application exception with
131: // rollback = false (default)
132: return applicationExceptions.get("DEFAULT");
133: }
134: }
135: }
136: }
137: // Was not a checked exception.
138: return null;
139:
140: }
141:
142: /**
143: * Remove from the factory's pool the bean found in the current invocation
144: * context.
145: * @param invocationContext the context of the current invocation
146: * @throws PoolException if removal is failing
147: */
148: @SuppressWarnings("unchecked")
149: protected void discard(
150: final EasyBeansInvocationContext invocationContext)
151: throws PoolException {
152:
153: Object target = invocationContext.getTarget();
154:
155: // factory is a stateful factory ?
156: if (target instanceof EasyBeansSFSB) {
157: // Gets the factory
158: Factory factory = invocationContext.getFactory();
159:
160: // get pool
161: Pool<EasyBeansSFSB, Long> pool = factory.getPool();
162:
163: // Bean is a stateful bean
164: EasyBeansSFSB bean = (EasyBeansSFSB) invocationContext
165: .getTarget();
166:
167: // discard instance
168: pool.discard(bean);
169: } else {
170: logger
171: .debug("Instance not discarded as it is not a stateful bean");
172: }
173:
174: }
175:
176: /**
177: * Marks the transaction for rollback.
178: */
179: protected void markTransactionRollback() {
180: // Look if there is a transaction
181: Transaction transaction;
182: try {
183: transaction = getTransactionManager().getTransaction();
184: } catch (SystemException se) {
185: throw new EJBException(
186: "Cannot get the current transaction on transaction manager.",
187: se);
188: }
189: if (transaction != null) {
190: try {
191: transactionManager.setRollbackOnly();
192: } catch (IllegalStateException e) {
193: logger.warn("Cannot mark transaction as rollbackOnly",
194: e);
195: } catch (SystemException e) {
196: logger.warn("Cannot mark transaction as rollbackOnly",
197: e);
198: }
199: }
200: }
201:
202: /**
203: * Test if current transaction is with status STATUS_MARKED_ROLLBACK.
204: * @return true if status == STATUS_MARKED_ROLLBACK
205: */
206: protected boolean isMarkedRollbackOnly() {
207: try {
208: return (STATUS_MARKED_ROLLBACK == transactionManager
209: .getStatus());
210: } catch (SystemException e) {
211: logger.warn("Cannot get transaction status", e);
212: return false;
213: }
214: }
215:
216: /**
217: * Rollback the current transaction.
218: */
219: protected void rollback() {
220: try {
221: transactionManager.rollback();
222: } catch (IllegalStateException e) {
223: logger.warn("Cannot rollback the transaction", e);
224: } catch (SecurityException e) {
225: logger.warn("Cannot rollback the transaction", e);
226: } catch (SystemException e) {
227: logger.warn("Cannot rollback the transaction", e);
228: }
229: }
230:
231: /**
232: * Commit the current transaction.
233: */
234: protected void commit() {
235: try {
236: transactionManager.commit();
237: } catch (IllegalStateException e) {
238: logger.warn("Cannot commit the transaction", e);
239: } catch (SecurityException e) {
240: logger.warn("Cannot commit the transaction", e);
241: } catch (HeuristicMixedException e) {
242: logger.warn("Cannot commit the transaction", e);
243: } catch (HeuristicRollbackException e) {
244: logger.warn("Cannot commit the transaction", e);
245: } catch (RollbackException e) {
246: logger.warn("Cannot commit the transaction", e);
247: } catch (SystemException e) {
248: logger.warn("Cannot commit the transaction", e);
249: }
250: }
251:
252: /**
253: * Handle an exception for bean managed transaction.<br />
254: * See Chapter 14.3.1.
255: * @param invocationContext the context of the current invocation
256: * @param e the exception to handle
257: * @throws Exception when handling the exception.
258: */
259: protected void handleBeanManagedException(
260: final EasyBeansInvocationContext invocationContext,
261: final Exception e) throws Exception {
262: ApplicationException applicationException = getApplicationException(
263: invocationContext, e);
264:
265: // it is an application exception
266: if (applicationException != null) {
267: // Re-throw AppException.
268: throw e;
269: }
270:
271: // else, not an application exception :
272:
273: // Log the exception or error.
274: logger
275: .error(
276: "Bean Managed Transaction : Exception (not application exception) in business method",
277: e);
278:
279: // Mark for rollback a transaction that has been
280: // started, but not yet completed, by the instance.
281: markTransactionRollback();
282:
283: // Discard instance.
284: try {
285: discard(invocationContext);
286: } catch (PoolException pe) {
287: throw new EJBException("Cannot discard the bean", pe);
288: }
289:
290: // Throw EJBException to client.
291: throw new EJBException(
292: "Bean Managed Transaction : Business exception which is not an application exception",
293: e);
294: }
295:
296: /**
297: * Handle an exception that are in an unspecified transaction context.<br />
298: * See Chapter 14.3.1.
299: * @param invocationContext the context of the current invocation
300: * @param e the exception to handle
301: * @throws Exception when handling the exception.
302: */
303: protected void handleUnspecifiedTransactionContext(
304: final EasyBeansInvocationContext invocationContext,
305: final Exception e) throws Exception {
306:
307: ApplicationException applicationException = getApplicationException(
308: invocationContext, e);
309:
310: // it is an application exception
311: if (applicationException != null) {
312: // Re-throw AppException.
313: throw e;
314: }
315:
316: // else, not an application exception :
317:
318: // Log the exception or error.
319: logger
320: .error(
321: "Exception (not application exception) in business method",
322: e);
323:
324: // Discard instance.
325: try {
326: discard(invocationContext);
327: } catch (PoolException pe) {
328: throw new EJBException("Cannot discard the bean", pe);
329: }
330:
331: // Throw EJBException to client.
332: throw new EJBException(
333: "Business exception which is not an application exception",
334: e);
335:
336: }
337:
338: /**
339: * Handle an exception and the transaction is the client transaction.<br />
340: * See Chapter 14.3.1.
341: * @param invocationContext the context of the current invocation
342: * @param e the exception to handle
343: * @throws Exception when handling the exception.
344: */
345: protected void handleContextClientTransaction(
346: final EasyBeansInvocationContext invocationContext,
347: final Exception e) throws Exception {
348: ApplicationException applicationException = getApplicationException(
349: invocationContext, e);
350:
351: // An application exception ?
352: if (applicationException != null) {
353: /*
354: * Re-throw AppException. Mark the transaction for rollback if the
355: * application exception is specified as causing rollback.
356: */
357:
358: // Mark the transaction for rollback.
359: if (applicationException.rollback()) {
360: markTransactionRollback();
361: }
362:
363: // rethrow
364: throw e;
365: }
366:
367: // else, not an application exception :
368:
369: // Log the exception or error.
370: logger
371: .error(
372: "Exception (not application exception) in business method",
373: e);
374:
375: // Mark the transaction for rollback.
376: markTransactionRollback();
377:
378: // Discard instance.
379: try {
380: discard(invocationContext);
381: } catch (PoolException pe) {
382: throw new EJBException("Cannot discard the bean", pe);
383: }
384:
385: // Throw javax.ejb.EJBTransactionRolledbackException to
386: // client.
387: EJBTransactionRolledbackException transactionException = new EJBTransactionRolledbackException(
388: "System exception, The transaction has been marked for rollback only");
389: transactionException.initCause(e);
390: throw transactionException;
391: }
392:
393: /**
394: * Handle an exception and the transaction is the container transaction.
395: * Bean method runs in the context of a transaction that the container
396: * started immediately before dispatching the business method.<br />
397: * See Chapter 14.3.1.
398: * @param invocationContext the context of the current invocation
399: * @param e the exception to handle
400: * @throws Exception when handling the exception.
401: */
402: protected void handleContextContainerTransaction(
403: final EasyBeansInvocationContext invocationContext,
404: final Exception e) throws Exception {
405:
406: ApplicationException applicationException = getApplicationException(
407: invocationContext, e);
408:
409: // An application exception ?
410: if (applicationException != null) {
411: /*
412: * If the instance called setRollback-Only(), then rollback the
413: * transaction, and re-throw AppException.
414: */
415: if (isMarkedRollbackOnly()) {
416: rollback();
417: throw e;
418: }
419:
420: /*
421: * Mark the transaction for rollback if the application exception is
422: * specified as causing rollback, and then re-throw AppException.
423: * Otherwise, attempt to commit the transaction, and then re-throw
424: * AppException.
425: */
426: if (applicationException.rollback()) {
427: // TODO: rollback or mark rollback ??
428: rollback();
429: } else {
430: commit();
431: }
432: throw e;
433: }
434: // else, not an application exception :
435: // Log the exception or error.
436: logger
437: .error(
438: "Exception (not application exception) in business method",
439: e);
440:
441: // Rollback the container-started transaction.
442: rollback();
443:
444: // Discard instance.
445: try {
446: discard(invocationContext);
447: } catch (PoolException pe) {
448: throw new EJBException("Cannot discard the bean", pe);
449: }
450:
451: // Throw EJBException to client.
452: throw new EJBException(
453: "Exception in a business interface with REQUIRED TX attribute",
454: e);
455:
456: }
457: }
|