001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.transaction;
030:
031: import com.caucho.config.types.Period;
032: import com.caucho.loader.ClassLoaderListener;
033: import com.caucho.loader.DynamicClassLoader;
034: import com.caucho.loader.Environment;
035: import com.caucho.log.Log;
036: import com.caucho.transaction.xalog.AbstractXALogManager;
037: import com.caucho.transaction.xalog.AbstractXALogStream;
038: import com.caucho.util.Crc64;
039: import com.caucho.util.L10N;
040: import com.caucho.util.RandomUtil;
041: import com.caucho.webbeans.component.*;
042:
043: import javax.transaction.*;
044: import javax.transaction.xa.XAException;
045: import javax.transaction.xa.XAResource;
046: import javax.transaction.xa.Xid;
047: import java.io.Serializable;
048: import java.lang.ref.WeakReference;
049: import java.util.ArrayList;
050: import java.util.logging.Level;
051: import java.util.logging.Logger;
052:
053: /**
054: * Implementation of the transaction manager.
055: */
056: public class TransactionManagerImpl implements TransactionManager,
057: Serializable, ClassLoaderListener {
058: private static L10N L = new L10N(TransactionManagerImpl.class);
059: private static Logger log = Logger
060: .getLogger(TransactionManagerImpl.class.getName());
061:
062: private static TransactionManagerImpl _tm = new TransactionManagerImpl();
063:
064: private int _serverId;
065:
066: private AbstractXALogManager _xaLogManager;
067:
068: // thread local is dependent on the transaction manager
069: private ThreadLocal<TransactionImpl> _threadTransaction = new ThreadLocal<TransactionImpl>();
070:
071: private ArrayList<WeakReference<TransactionImpl>> _transactionList = new ArrayList<WeakReference<TransactionImpl>>();
072:
073: // listeners for transaction begin
074: private ArrayList<TransactionListener> _transactionListeners = new ArrayList<TransactionListener>();
075:
076: private long _timeout = -1;
077:
078: public TransactionManagerImpl() {
079: }
080:
081: /**
082: * Returns the local transaction manager.
083: */
084: public static TransactionManagerImpl getInstance() {
085: return _tm;
086: }
087:
088: /**
089: * Returns the local transaction manager.
090: */
091: public static TransactionManagerImpl getLocal() {
092: return _tm;
093: }
094:
095: /**
096: * Sets the timeout.
097: */
098: public void setTimeout(Period timeout) {
099: _timeout = timeout.getPeriod();
100: }
101:
102: /**
103: * Gets the timeout.
104: */
105: public long getTimeout() {
106: return _timeout;
107: }
108:
109: /**
110: * Sets the XA log manager.
111: */
112: public void setXALogManager(AbstractXALogManager xaLogManager) {
113: _xaLogManager = xaLogManager;
114: }
115:
116: /**
117: * Create a new transaction and associate it with the thread.
118: */
119: public void begin() throws NotSupportedException, SystemException {
120: getCurrent().begin();
121: }
122:
123: /**
124: * Creates a new transaction id.
125: */
126: XidImpl createXID() {
127: return new XidImpl(getServerId(), RandomUtil.getRandomLong());
128: }
129:
130: /**
131: * Creates a new transaction id.
132: */
133: AbstractXALogStream getXALogStream() {
134: if (_xaLogManager != null)
135: return _xaLogManager.getStream();
136: else
137: return null;
138: }
139:
140: /**
141: * Returns the server id.
142: */
143: private int getServerId() {
144: if (_serverId == 0) {
145: String server = (String) Environment
146: .getAttribute("caucho.server-id");
147:
148: if (server == null)
149: _serverId = 1;
150: else
151: _serverId = (int) Crc64.generate(server);
152: }
153:
154: return _serverId;
155: }
156:
157: /**
158: * Returns the transaction for the current thread.
159: */
160: public Transaction getTransaction() throws SystemException {
161: TransactionImpl trans = _threadTransaction.get();
162:
163: if (trans == null
164: || trans.getStatus() == Status.STATUS_NO_TRANSACTION
165: || trans.getStatus() == Status.STATUS_UNKNOWN
166: || trans.isSuspended()) {
167: return null;
168: } else {
169: return trans;
170: }
171: }
172:
173: /**
174: * Suspend the transaction.
175: */
176: public Transaction suspend() throws SystemException {
177: TransactionImpl trans = _threadTransaction.get();
178:
179: if (trans == null
180: || (!trans.hasResources() && (trans.getStatus() == Status.STATUS_NO_TRANSACTION || trans
181: .getStatus() == Status.STATUS_UNKNOWN)))
182: return null;
183:
184: _threadTransaction.set(null);
185: trans.suspend();
186:
187: return trans;
188: }
189:
190: /**
191: * Resume the transaction.
192: */
193: public void resume(Transaction tobj)
194: throws InvalidTransactionException, SystemException {
195: Transaction old = _threadTransaction.get();
196:
197: if (old != null
198: && old.getStatus() != Status.STATUS_NO_TRANSACTION)
199: throw new SystemException(
200: L
201: .l(
202: "can't resume transaction with active transaction {0}",
203: String.valueOf(old)));
204:
205: TransactionImpl impl = (TransactionImpl) tobj;
206:
207: impl.resume();
208:
209: _threadTransaction.set(impl);
210: }
211:
212: /**
213: * Force any completion to be a rollback.
214: */
215: public void setRollbackOnly() throws SystemException {
216: getCurrent().setRollbackOnly();
217: }
218:
219: /**
220: * Force any completion to be a rollback.
221: */
222: public void setRollbackOnly(Exception e) {
223: getCurrent().setRollbackOnly(e);
224: }
225:
226: /**
227: * Returns the transaction's status
228: */
229: public int getStatus() throws SystemException {
230: return getCurrent().getStatus();
231: }
232:
233: /**
234: * sets the timeout for the transaction
235: */
236: public void setTransactionTimeout(int seconds)
237: throws SystemException {
238: getCurrent().setTransactionTimeout(seconds);
239: }
240:
241: /**
242: * Commit the transaction.
243: */
244: public void commit() throws RollbackException,
245: HeuristicMixedException, HeuristicRollbackException,
246: SystemException {
247: getCurrent().commit();
248: }
249:
250: /**
251: * Rollback the transaction.
252: */
253: public void rollback() {
254: getCurrent().rollback();
255: }
256:
257: /**
258: * Returns the corresponding user transaction.
259: */
260: /*
261: public UserTransaction getUserTransaction()
262: {
263: return this;
264: }
265: */
266:
267: /**
268: * Returns the current TransactionImpl, creating if necessary.
269: *
270: * <p/>The TransactionImpl is not an official externally
271: * visible Transaction if the status == NO_TRANSACTION.
272: */
273: public TransactionImpl getCurrent() {
274: TransactionImpl trans = _threadTransaction.get();
275:
276: if (trans == null || trans.isDead()) {
277: trans = new TransactionImpl(this );
278: _threadTransaction.set(trans);
279:
280: addTransaction(trans);
281: }
282:
283: return trans;
284: }
285:
286: private void addTransaction(TransactionImpl trans) {
287: synchronized (_transactionList) {
288: for (int i = _transactionList.size() - 1; i >= 0; i--) {
289: WeakReference<TransactionImpl> ref = _transactionList
290: .get(i);
291:
292: if (ref.get() == null)
293: _transactionList.remove(i);
294: }
295:
296: _transactionList.add(new WeakReference<TransactionImpl>(
297: trans));
298: }
299: }
300:
301: /**
302: * Returns the corresponding user transaction.
303: */
304: public void recover(XAResource xaRes) throws XAException {
305: Xid[] xids;
306:
307: xids = xaRes.recover(XAResource.TMSTARTRSCAN
308: | XAResource.TMENDRSCAN);
309:
310: if (xids == null)
311: return;
312:
313: for (int i = 0; i < xids.length; i++) {
314: byte[] global = xids[i].getGlobalTransactionId();
315:
316: if (global.length != XidImpl.GLOBAL_LENGTH)
317: continue;
318:
319: XidImpl xidImpl = new XidImpl(xids[i]
320: .getGlobalTransactionId());
321:
322: if (_xaLogManager != null
323: && _xaLogManager.hasCommittedXid(xidImpl)) {
324: log.fine(L.l("XAResource {0} commit xid {1}", xaRes,
325: xidImpl));
326:
327: try {
328: xaRes.commit(xidImpl, false);
329: } catch (Throwable e) {
330: log.log(Level.WARNING, e.toString(), e);
331: }
332: } else {
333: // XXX: need to check if the transaction belongs to this TM
334: // the ownership is encoded in the xid
335:
336: log.fine(L.l("XAResource {0} forget xid {1}", xaRes,
337: xidImpl));
338:
339: try {
340: xaRes.forget(xidImpl);
341: } catch (Throwable e) {
342: log.log(Level.WARNING, e.toString(), e);
343: }
344: }
345: }
346: }
347:
348: /**
349: * Flushes the log stream (primarily for QA).
350: */
351: public void flush() {
352: if (_xaLogManager != null)
353: _xaLogManager.flush();
354: }
355:
356: /**
357: * Handles the case where a class loader has completed initialization
358: */
359: public void classLoaderInit(DynamicClassLoader loader) {
360: }
361:
362: /**
363: * Handles the case where a class loader is dropped.
364: */
365: public void classLoaderDestroy(DynamicClassLoader loader) {
366: AbstractXALogManager xaLogManager = _xaLogManager;
367: _xaLogManager = null;
368:
369: if (xaLogManager != null)
370: xaLogManager.close();
371:
372: _serverId = 0;
373:
374: synchronized (_transactionList) {
375: for (int i = _transactionList.size() - 1; i >= 0; i--) {
376: WeakReference<TransactionImpl> ref = _transactionList
377: .get(i);
378: TransactionImpl xa = ref.get();
379:
380: try {
381: if (xa != null) {
382: xa.rollback();
383: }
384: } catch (Throwable e) {
385: log.log(Level.WARNING, e.toString(), e);
386: }
387: }
388: }
389: }
390:
391: /**
392: * Clearing for test purposes.
393: */
394: public void testClear() {
395: _serverId = 0;
396: _timeout = -1;
397: AbstractXALogManager logManager = _xaLogManager;
398: _xaLogManager = null;
399:
400: if (logManager != null)
401: logManager.close();
402: }
403:
404: /**
405: * Serialize to a handle
406: */
407: private Object writeReplace() {
408: return new WebBeansHandle(TransactionManager.class);
409: }
410:
411: public String toString() {
412: return "TransactionManagerImpl[]";
413: }
414: }
|