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.util.Hashtable;
025:
026: import java.rmi.RemoteException;
027:
028: import javax.transaction.Transaction;
029: import javax.transaction.Status;
030: import javax.transaction.SystemException;
031:
032: import javax.naming.Context;
033: import javax.naming.InitialContext;
034: import javax.naming.Name;
035: import javax.naming.Reference;
036: import javax.naming.RefAddr;
037: import javax.naming.spi.ObjectFactory;
038:
039: import org.jboss.ejb.EnterpriseContext;
040: import org.jboss.ejb.AllowedOperationsAssociation;
041: import org.jboss.invocation.Invocation;
042: import org.jboss.tm.TxUtils;
043:
044: /**
045: * A common superclass for the BMT transaction interceptors.
046: *
047: * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
048: * @version $Revision: 63545 $
049: */
050: abstract class AbstractTxInterceptorBMT extends AbstractTxInterceptor {
051: // Attributes ----------------------------------------------------
052:
053: /**
054: * This associates the thread to the UserTransaction.
055: *
056: * It is used to redirect lookups on java:comp/UserTransaction to
057: * the <code>getUserTransaction()</code> method of the context.
058: */
059: private ThreadLocal userTransaction = new ThreadLocal();
060:
061: /**
062: * If <code>false</code>, transactions may live across bean instance
063: * invocations, otherwise the bean instance should terminate any
064: * transaction before returning from the invocation.
065: * This attribute defaults to <code>true</code>.
066: */
067: protected boolean stateless = true;
068:
069: // Static --------------------------------------------------------
070:
071: // Constructors --------------------------------------------------
072:
073: // Public --------------------------------------------------------
074:
075: // Interceptor implementation --------------------------------------
076:
077: public void create() throws Exception {
078: // Do initialization in superclass.
079: super .create();
080:
081: // bind java:comp/UserTransaction
082: RefAddr refAddr = new RefAddr("userTransaction") {
083: /** This is never really serialized */
084: private static final long serialVersionUID = -8228448967597474960L;
085:
086: public Object getContent() {
087: return userTransaction;
088: }
089: };
090:
091: Reference ref = new Reference(
092: "javax.transaction.UserTransaction", refAddr,
093: new UserTxFactory().getClass().getName(), null);
094: ((Context) new InitialContext().lookup("java:comp/")).bind(
095: "UserTransaction", ref);
096: }
097:
098: public void stop() {
099: // bind java:comp/UserTransaction
100: try {
101: ((Context) new InitialContext().lookup("java:comp/"))
102: .unbind("UserTransaction");
103: } catch (Exception e) {
104: //ignore
105: }
106: }
107:
108: // Protected ----------------------------------------------------
109:
110: /*
111: * This method calls the next interceptor in the chain.
112: *
113: * It handles the suspension of any client transaction, and the
114: * association of the calling thread with the instance transaction.
115: * And it takes care that any lookup of
116: * <code>java:comp/UserTransaction</code> will return the right
117: * UserTransaction for the bean instance.
118: *
119: * @param remoteInvocation If <code>true</code> this is an invocation
120: * of a method in the remote interface, otherwise
121: * it is an invocation of a method in the home
122: * interface.
123: * @param mi The <code>Invocation</code> of this call.
124: */
125: protected Object invokeNext(Invocation mi) throws Exception {
126: // Save the transaction that comes with the MI
127: Transaction oldTransaction = mi.getTransaction();
128:
129: // Get old threadlocal: It may be non-null if one BMT bean does a local
130: // call to another.
131: Object oldUserTx = userTransaction.get();
132:
133: // Suspend any transaction associated with the thread: It may be
134: // non-null on optimized local calls.
135: Transaction threadTx = tm.suspend();
136:
137: try {
138: EnterpriseContext ctx = ((EnterpriseContext) mi
139: .getEnterpriseContext());
140:
141: // Set the threadlocal to the userTransaction of the instance
142: try {
143: AllowedOperationsAssociation
144: .pushInMethodFlag(IN_INTERCEPTOR_METHOD);
145: userTransaction.set(ctx.getEJBContext()
146: .getUserTransaction());
147: } finally {
148: AllowedOperationsAssociation.popInMethodFlag();
149: }
150:
151: // Get the bean instance transaction
152: Transaction beanTx = ctx.getTransaction();
153:
154: // Resume the bean instance transaction
155: // only if it not null, some TMs can't resume(null), e.g. Tyrex
156: if (beanTx != null)
157: tm.resume(beanTx);
158:
159: // Let the MI know about our new transaction
160: mi.setTransaction(beanTx);
161:
162: try {
163: // Let the superclass call next interceptor and do the exception
164: // handling
165: return super .invokeNext(mi, false);
166: } finally {
167: try {
168: if (stateless)
169: checkStatelessDone();
170: else
171: checkBadStateful();
172: } finally {
173: tm.suspend();
174: }
175: }
176: } finally {
177: // Reset threadlocal to its old value
178: userTransaction.set(oldUserTx);
179:
180: // Restore old MI transaction
181: // OSH: Why ???
182: mi.setTransaction(oldTransaction);
183:
184: // If we had a Tx associated with the thread reassociate
185: if (threadTx != null)
186: tm.resume(threadTx);
187: }
188: }
189:
190: private void checkStatelessDone() throws RemoteException {
191: int status = Status.STATUS_NO_TRANSACTION;
192:
193: try {
194: status = tm.getStatus();
195: } catch (SystemException ex) {
196: log.error("Failed to get status", ex);
197: }
198:
199: try {
200: switch (status) {
201: case Status.STATUS_ACTIVE:
202: case Status.STATUS_COMMITTING:
203: case Status.STATUS_MARKED_ROLLBACK:
204: case Status.STATUS_PREPARING:
205: case Status.STATUS_ROLLING_BACK:
206: try {
207: tm.rollback();
208: } catch (Exception ex) {
209: log.error("Failed to rollback", ex);
210: }
211: // fall through...
212: case Status.STATUS_PREPARED:
213: String msg = "Application error: BMT stateless bean "
214: + container.getBeanMetaData().getEjbName()
215: + " should complete transactions before"
216: + " returning (ejb1.1 spec, 11.6.1)";
217: log.error(msg);
218:
219: // the instance interceptor will discard the instance
220: throw new RemoteException(msg);
221: }
222: } finally {
223: Transaction tx = null;
224: try {
225: tx = tm.suspend();
226: } catch (SystemException ex) {
227: log.error("Failed to suspend transaction", ex);
228: }
229: if (tx != null) {
230: String msg = "Application error: BMT stateless bean "
231: + container.getBeanMetaData().getEjbName()
232: + " should complete transactions before "
233: + " returning (ejb1.1 spec, 11.6.1), suspended tx="
234: + tx;
235: log.error(msg);
236: throw new RemoteException(msg);
237: }
238: }
239: }
240:
241: private void checkBadStateful() throws RemoteException {
242: int status = Status.STATUS_NO_TRANSACTION;
243:
244: try {
245: status = tm.getStatus();
246: } catch (SystemException ex) {
247: log.error("Failed to get status", ex);
248: }
249:
250: try {
251:
252: switch (status) {
253: case Status.STATUS_COMMITTING:
254: case Status.STATUS_MARKED_ROLLBACK:
255: case Status.STATUS_PREPARING:
256: case Status.STATUS_ROLLING_BACK:
257: try {
258: tm.rollback();
259: } catch (Exception ex) {
260: log.error("Failed to rollback", ex);
261: }
262: String msg = "BMT stateful bean '"
263: + container.getBeanMetaData().getEjbName()
264: + "' did not complete user transaction properly status="
265: + TxUtils.getStatusAsString(status);
266: log.error(msg);
267: }
268: } finally {
269: Transaction tx = null;
270: try {
271: tx = tm.suspend();
272: } catch (SystemException ex) {
273: log.error("Failed to suspend transaction", ex);
274: }
275: if (tx != null) {
276: String msg = "BMT stateful bean "
277: + container.getBeanMetaData().getEjbName()
278: + " did not complete user transaction properly tx="
279: + tx;
280: log.error(msg);
281: }
282: }
283: }
284:
285: // Inner classes -------------------------------------------------
286:
287: public static class UserTxFactory implements ObjectFactory {
288: public Object getObjectInstance(Object ref, Name name,
289: Context nameCtx, Hashtable environment)
290: throws Exception {
291: // The ref is a list with only one RefAddr ...
292: RefAddr refAddr = ((Reference) ref).get(0);
293: // ... whose content is the threadlocal
294: ThreadLocal threadLocal = (ThreadLocal) refAddr
295: .getContent();
296:
297: // The threadlocal holds the right UserTransaction
298: return threadLocal.get();
299: }
300: }
301:
302: }
|