001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins;
023:
024: import java.io.PrintWriter;
025: import java.io.StringWriter;
026:
027: import java.rmi.RemoteException;
028: import java.rmi.ServerException;
029: import java.rmi.NoSuchObjectException;
030: import java.lang.reflect.Method;
031:
032: import javax.transaction.TransactionManager;
033: import javax.transaction.TransactionRolledbackException;
034: import javax.transaction.SystemException;
035: import javax.transaction.Transaction;
036: import javax.transaction.Synchronization;
037: import javax.transaction.RollbackException;
038:
039: import javax.ejb.EJBException;
040: import javax.ejb.NoSuchEntityException;
041: import javax.ejb.NoSuchObjectLocalException;
042: import javax.ejb.TransactionRolledbackLocalException;
043: import javax.ejb.TimedObject;
044: import javax.ejb.Timer;
045:
046: import org.jboss.invocation.Invocation;
047: import org.jboss.invocation.InvocationType;
048: import org.jboss.tm.TxUtils;
049:
050: /**
051: * A common superclass for the transaction interceptors.
052: * <p/>
053: * The only important method in this class is invokeNext which is incharge
054: * of invoking the next interceptor and if an exception is thrown, it must
055: * follow the rules in the EJB 2.0 specification section 18.3. These
056: * rules specify if the transaction is rolled back and what exception
057: * should be thrown.
058: *
059: * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
060: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
061: * @version $Revision: 57209 $
062: */
063: abstract class AbstractTxInterceptor extends AbstractInterceptor {
064:
065: /** A reference to {@link javax.ejb.TimedObject#ejbTimeout}. */
066: protected static final Method ejbTimeout;
067: static {
068: try {
069: ejbTimeout = TimedObject.class.getMethod("ejbTimeout",
070: new Class[] { Timer.class });
071: } catch (Exception e) {
072: throw new ExceptionInInitializerError(e);
073: }
074: }
075:
076: /**
077: * Local reference to the container's TransactionManager.
078: */
079: protected TransactionManager tm;
080:
081: public void create() throws Exception {
082: super .create();
083: tm = getContainer().getTransactionManager();
084: }
085:
086: /**
087: * This method calls the next interceptor in the chain.
088: * <p/>
089: * All Throwables are caught and divided into two groups: application
090: * exceptions and system exceptions. Application exception are simply
091: * rethrown. System exceptions result in the transaction being marked
092: * for rollback only. If the transaction was not started by the container
093: * (i.e., it was inherited from the client) the system exception is wrapped
094: * in a TransactionRolledBack[Local]Exception.
095: *
096: * @param invocation The <code>Invocation</code> of this call.
097: * @param inheritedTx If <code>true</code> the transaction has just been started
098: * in this interceptor.
099: * @throws Exception if an exception occures in the interceptor chain. The
100: * actual exception throw is governed by the rules in the EJB 2.0
101: * specification section 18.3
102: */
103: protected Object invokeNext(Invocation invocation,
104: boolean inheritedTx) throws Exception {
105: InvocationType type = invocation.getType();
106: try {
107: if (type == InvocationType.REMOTE
108: || type == InvocationType.LOCAL
109: || type == InvocationType.SERVICE_ENDPOINT) {
110: // register the Timer with the transaction
111: if (ejbTimeout.equals(invocation.getMethod()))
112: registerTimer(invocation);
113:
114: return getNext().invoke(invocation);
115: } else {
116: return getNext().invokeHome(invocation);
117: }
118: } catch (Throwable e) {
119: // if this is an ApplicationException, just rethrow it
120: if (e instanceof Exception
121: && !(e instanceof RuntimeException || e instanceof RemoteException)) {
122: throw (Exception) e;
123: }
124:
125: // attempt to rollback the transaction
126: Transaction tx = invocation.getTransaction();
127: if (tx == null) {
128: // Look for a hanging active user transaction that we should mark rollback
129: try {
130: tx = tm.getTransaction();
131: if (TxUtils.isActive(tx) == false)
132: tx = null;
133: } catch (Exception ex) {
134: log.warn("Unable to determine transaction context",
135: ex);
136: }
137: }
138: if (tx != null) {
139: try {
140: tx.setRollbackOnly();
141: } catch (SystemException ex) {
142: log.error(
143: "SystemException while setting transaction "
144: + "for rollback only", ex);
145: } catch (IllegalStateException ex) {
146: log.error(
147: "IllegalStateException while setting transaction "
148: + "for rollback only", ex);
149: }
150: }
151:
152: // is this a local invocation
153: boolean isLocal = type == InvocationType.LOCAL
154: || type == InvocationType.LOCALHOME;
155:
156: // if this transaction was NOT inherited from the caller we simply
157: // rethrow the exception, and LogInterceptor will handle
158: // all exception conversions.
159: if (!inheritedTx) {
160: if (e instanceof Exception) {
161: throw (Exception) e;
162: }
163: if (e instanceof Error) {
164: throw (Error) e;
165: }
166:
167: // we have some funky throwable, wrap it
168: if (isLocal) {
169: String msg = formatException(
170: "Unexpected Throwable", e);
171: throw new EJBException(msg);
172: } else {
173: ServerException ex = new ServerException(
174: "Unexpected Throwable");
175: ex.detail = e;
176: throw ex;
177: }
178: }
179:
180: // to be nice we coerce the execption to an interface friendly type
181: // before wrapping it with a transaction rolled back exception
182: Throwable cause;
183: if (e instanceof NoSuchEntityException) {
184: NoSuchEntityException nsee = (NoSuchEntityException) e;
185: if (isLocal) {
186: cause = new NoSuchObjectLocalException(nsee
187: .getMessage(), nsee.getCausedByException());
188: } else {
189: cause = new NoSuchObjectException(nsee.getMessage());
190:
191: // set the detil of the exception
192: ((NoSuchObjectException) cause).detail = nsee
193: .getCausedByException();
194: }
195: } else {
196: if (isLocal) {
197: // local transaction rolled back exception can only wrap
198: // an exception so we create an EJBException for the cause
199: if (e instanceof Exception) {
200: cause = e;
201: } else if (e instanceof Error) {
202: String msg = formatException(
203: "Unexpected Error", e);
204: cause = new EJBException(msg);
205: } else {
206: String msg = formatException(
207: "Unexpected Throwable", e);
208: cause = new EJBException(msg);
209: }
210: } else {
211: // remote transaction rolled back exception can wrap
212: // any throwable so we are ok
213: cause = e;
214: }
215: }
216:
217: // We inherited tx: Tell caller we marked for rollback only.
218: if (isLocal) {
219: if (cause instanceof TransactionRolledbackLocalException) {
220: throw (TransactionRolledbackLocalException) cause;
221: } else {
222: throw new TransactionRolledbackLocalException(cause
223: .getMessage(), (Exception) cause);
224: }
225: } else {
226: if (cause instanceof TransactionRolledbackException) {
227: throw (TransactionRolledbackException) cause;
228: } else {
229: TransactionRolledbackException ex = new TransactionRolledbackException(
230: cause.getMessage());
231: ex.detail = cause;
232: throw ex;
233: }
234: }
235: }
236: }
237:
238: private void registerTimer(Invocation invocation)
239: throws RollbackException, SystemException {
240: Timer timer = (Timer) invocation.getArguments()[0];
241: Transaction transaction = invocation.getTransaction();
242: if (transaction != null && timer instanceof Synchronization)
243: transaction
244: .registerSynchronization((Synchronization) timer);
245: }
246:
247: private String formatException(String msg, Throwable t) {
248: StringWriter sw = new StringWriter();
249: PrintWriter pw = new PrintWriter(sw);
250: if (msg != null) {
251: pw.println(msg);
252: }
253: t.printStackTrace(pw);
254: return sw.toString();
255: }
256: }
|