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: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.jca;
031:
032: import com.caucho.log.Log;
033: import com.caucho.transaction.TransactionImpl;
034: import com.caucho.transaction.TransactionManagerImpl;
035: import com.caucho.util.L10N;
036: import com.caucho.webbeans.component.*;
037:
038: import javax.resource.spi.ConnectionRequestInfo;
039: import javax.resource.spi.ManagedConnectionFactory;
040: import javax.security.auth.Subject;
041: import javax.transaction.*;
042: import javax.transaction.xa.Xid;
043: import java.util.ArrayList;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: /**
048: * Implementation of the UserTransactionImpl for a thread instance.
049: */
050: public class UserTransactionImpl implements UserTransaction {
051: private static final Logger log = Logger
052: .getLogger(UserTransactionImpl.class.getName());
053: private static final L10N L = new L10N(UserTransactionImpl.class);
054:
055: private TransactionManagerImpl _transactionManager;
056:
057: private ArrayList<UserPoolItem> _resources = new ArrayList<UserPoolItem>();
058: private ArrayList<PoolItem> _poolItems = new ArrayList<PoolItem>();
059: private ArrayList<BeginResource> _beginResources = new ArrayList<BeginResource>();
060: private ArrayList<CloseResource> _closeResources = new ArrayList<CloseResource>();
061:
062: private int _xaDepth;
063:
064: /**
065: * Creates the proxy.
066: */
067: public UserTransactionImpl(TransactionManagerImpl tm) {
068: _transactionManager = tm;
069: }
070:
071: /**
072: * Sets the transaction's timeout.
073: */
074: public void setTransactionTimeout(int seconds)
075: throws SystemException {
076: _transactionManager.setTransactionTimeout(seconds);
077: }
078:
079: /**
080: * Gets the transaction's status
081: */
082: public int getStatus() throws SystemException {
083: return _transactionManager.getStatus();
084: }
085:
086: /**
087: * Enlist a resource.
088: */
089: void enlistResource(UserPoolItem resource) throws SystemException,
090: RollbackException {
091: if (_resources.contains(resource))
092: return;
093:
094: Transaction xa = _transactionManager.getTransaction();
095: if (xa != null) {
096: PoolItem poolItem = resource.getXAPoolItem();
097:
098: enlistPoolItem(xa, poolItem);
099: }
100:
101: _resources.add(resource);
102: }
103:
104: private void enlistPoolItem(Transaction xa, PoolItem poolItem)
105: throws SystemException, RollbackException {
106: if (poolItem == null)
107: return;
108: else if (!poolItem.supportsTransaction()) {
109: // server/164j
110: return;
111: }
112:
113: // XXX: new
114: if (_poolItems.contains(poolItem))
115: return;
116:
117: poolItem.setTransaction(this );
118:
119: if (xa instanceof TransactionImpl) {
120: TransactionImpl xaImpl = (TransactionImpl) xa;
121:
122: // server/164l
123: if (xaImpl.allowLocalTransactionOptimization())
124: poolItem.enableLocalTransactionOptimization(true);
125: }
126:
127: if (poolItem.getXid() == null)
128: xa.enlistResource(poolItem);
129:
130: _poolItems.add(poolItem);
131: }
132:
133: /**
134: * Delist a pool item
135: */
136: void delistPoolItem(PoolItem poolItem, int flags)
137: throws SystemException, RollbackException {
138: Transaction xa = _transactionManager.getTransaction();
139:
140: try {
141: if (xa != null)
142: xa.delistResource(poolItem, flags);
143: } finally {
144: _poolItems.remove(poolItem);
145: }
146: }
147:
148: /**
149: * Delist a resource.
150: */
151: void delistResource(UserPoolItem resource) {
152: _resources.remove(resource);
153: }
154:
155: /**
156: * Enlist a resource automatically called when a transaction begins
157: */
158: public void enlistBeginResource(BeginResource resource) {
159: _beginResources.add(resource);
160:
161: try {
162: Transaction xa = _transactionManager.getTransaction();
163: if (xa != null)
164: resource.begin(xa);
165: } catch (Exception e) {
166: throw new RuntimeException(e);
167: }
168: }
169:
170: /**
171: * Enlist a resource automatically closed when the context ends.
172: */
173: public void enlistCloseResource(CloseResource resource) {
174: _closeResources.add(resource);
175: }
176:
177: /**
178: * Allocates a resource matching the parameters. If none matches,
179: * return null.
180: */
181: UserPoolItem allocate(ManagedConnectionFactory mcf,
182: Subject subject, ConnectionRequestInfo info) {
183: if (_xaDepth == 0)
184: return null;
185:
186: ArrayList<PoolItem> poolItems = _poolItems;
187: int length = poolItems.size();
188:
189: for (int i = 0; i < length; i++) {
190: PoolItem poolItem = poolItems.get(i);
191:
192: UserPoolItem item = poolItem.allocateXA(mcf, subject, info);
193:
194: if (item != null)
195: return item;
196: }
197:
198: return null;
199: }
200:
201: /**
202: * Finds the pool item joined to this one.
203: * return null.
204: */
205: PoolItem findJoin(PoolItem item) {
206: if (_xaDepth == 0)
207: return null;
208:
209: ArrayList<PoolItem> poolItems = _poolItems;
210: int length = poolItems.size();
211:
212: for (int i = 0; i < length; i++) {
213: PoolItem poolItem = poolItems.get(i);
214:
215: if (poolItem.isJoin(item))
216: return poolItem;
217: }
218:
219: return null;
220: }
221:
222: /**
223: * Returns the XID.
224: */
225: public Xid getXid() throws SystemException, RollbackException {
226: TransactionImpl xa = (TransactionImpl) _transactionManager
227: .getTransaction();
228:
229: if (xa != null)
230: return xa.getXid();
231: else
232: return null;
233: }
234:
235: /**
236: * Returns the XID.
237: */
238: public int getEnlistedResourceCount() throws SystemException,
239: RollbackException {
240: TransactionImpl xa = (TransactionImpl) _transactionManager
241: .getTransaction();
242:
243: if (xa != null)
244: return xa.getEnlistedResourceCount();
245: else
246: return 0;
247: }
248:
249: /**
250: * Start the transaction.
251: */
252: public void begin() throws NotSupportedException, SystemException {
253: _transactionManager.begin();
254: _xaDepth++;
255: boolean isOkay = false;
256:
257: try {
258: TransactionImpl xa = (TransactionImpl) _transactionManager
259: .getTransaction();
260: xa.setUserTransaction(this );
261:
262: _poolItems.clear();
263:
264: // enlist "cached" connections
265: int length = _resources.size();
266:
267: for (int i = 0; i < length; i++) {
268: UserPoolItem userPoolItem = _resources.get(i);
269:
270: for (int j = _poolItems.size() - 1; j >= 0; j--) {
271: PoolItem poolItem = _poolItems.get(j);
272:
273: if (poolItem.share(userPoolItem)) {
274: break;
275: }
276: }
277:
278: PoolItem xaPoolItem = userPoolItem.getXAPoolItem();
279: if (!_poolItems.contains(xaPoolItem))
280: _poolItems.add(xaPoolItem);
281: }
282:
283: for (int i = 0; i < _poolItems.size(); i++) {
284: PoolItem poolItem = _poolItems.get(i);
285:
286: poolItem.enableLocalTransactionOptimization(_poolItems
287: .size() == 1);
288:
289: try {
290: xa.enlistResource(poolItem);
291: } catch (Exception e) {
292: throw new SystemException(e);
293: }
294: }
295:
296: // enlist begin resources
297: for (int i = 0; i < _beginResources.size(); i++) {
298: try {
299: BeginResource resource = _beginResources.get(i);
300:
301: resource.begin(xa);
302: } catch (Throwable e) {
303: log.log(Level.WARNING, e.toString(), e);
304: }
305: }
306:
307: isOkay = true;
308: } finally {
309: if (!isOkay) {
310: log
311: .warning("Rolling back transaction from failed begin()");
312:
313: // something has gone very wrong
314: _xaDepth--;
315:
316: ArrayList<PoolItem> recoveryList = new ArrayList<PoolItem>(
317: _poolItems);
318: _poolItems.clear();
319: _resources.clear();
320:
321: for (int i = 0; i < recoveryList.size(); i++) {
322: try {
323: PoolItem item = recoveryList.get(i);
324:
325: item.abortConnection();
326:
327: item.destroy();
328: } catch (Throwable e) {
329: log.log(Level.FINE, e.toString(), e);
330: }
331: }
332:
333: _transactionManager.rollback();
334: }
335: }
336: }
337:
338: /**
339: * Suspends the transaction.
340: */
341: public UserTransactionSuspendState userSuspend() {
342: if (_xaDepth == 0)
343: throw new IllegalStateException(L
344: .l("suspend may only be called in a transaction."));
345:
346: _xaDepth--;
347:
348: UserTransactionSuspendState state;
349: state = new UserTransactionSuspendState(_poolItems);
350: _poolItems.clear();
351:
352: return state;
353: }
354:
355: /**
356: * Resumes the transaction.
357: */
358: public void userResume(UserTransactionSuspendState state) {
359: /*
360: if (_inTransaction)
361: throw new IllegalStateException(L.l("resume may only be called outside of a transaction."));
362: */
363:
364: _xaDepth++;
365:
366: _poolItems.addAll(state.getPoolItems());
367: }
368:
369: /**
370: * Marks the transaction as rollback only.
371: */
372: public void setRollbackOnly() throws IllegalStateException,
373: SystemException {
374: _transactionManager.setRollbackOnly();
375: }
376:
377: /**
378: * Marks the transaction as rollback only.
379: */
380: public void setRollbackOnly(Exception e)
381: throws IllegalStateException {
382: _transactionManager.setRollbackOnly(e);
383: }
384:
385: /**
386: * Commits the transaction
387: */
388: public void commit() throws IllegalStateException,
389: RollbackException, HeuristicMixedException,
390: HeuristicRollbackException, SecurityException,
391: SystemException {
392: try {
393: /* XXX: interaction with hessian XA
394: if (_xaDepth == 0)
395: throw new IllegalStateException("Can't commit outside of a transaction. Either the UserTransaction.begin() is missing or the transaction has already been committed or rolled back.");
396: */
397:
398: _transactionManager.commit();
399: } finally {
400: _poolItems.clear();
401: if (_xaDepth > 0)
402: _xaDepth--;
403: }
404: }
405:
406: /**
407: * Rolls the transaction back
408: */
409: public void rollback() throws IllegalStateException,
410: SecurityException, SystemException {
411: try {
412: _transactionManager.rollback();
413: } finally {
414: _poolItems.clear();
415: if (_xaDepth > 0)
416: _xaDepth--;
417: }
418: }
419:
420: /**
421: * Aborts the transaction.
422: */
423: public void abortTransaction() throws IllegalStateException {
424: IllegalStateException exn = null;
425:
426: boolean inTransaction = _xaDepth > 0;
427: _xaDepth = 0;
428:
429: if (!inTransaction && _poolItems.size() > 0) {
430: Exception e = new IllegalStateException(
431: "user transaction pool broken");
432: log.log(Level.WARNING, e.toString(), e);
433: }
434:
435: _poolItems.clear();
436:
437: if (inTransaction) {
438: try {
439: TransactionImpl xa = (TransactionImpl) _transactionManager
440: .getTransaction();
441:
442: exn = new IllegalStateException(
443: L
444: .l("Transactions must have a commit() or rollback() in a finally block."));
445:
446: log
447: .warning("Rolling back dangling transaction. All transactions must have a commit() or rollback() in a finally block.");
448:
449: _transactionManager.rollback();
450: } catch (Throwable e) {
451: log.log(Level.WARNING, e.toString());
452: }
453:
454: }
455:
456: _beginResources.clear();
457:
458: while (_closeResources.size() > 0) {
459: try {
460: CloseResource resource;
461:
462: resource = _closeResources.remove(_closeResources
463: .size() - 1);
464: resource.close();
465: } catch (Throwable e) {
466: log.log(Level.WARNING, e.toString(), e);
467: }
468: }
469:
470: boolean hasWarning = false;
471:
472: while (_resources.size() > 0) {
473: UserPoolItem userPoolItem = _resources.remove(_resources
474: .size() - 1);
475:
476: if (!userPoolItem.isCloseDanglingConnections())
477: continue;
478:
479: if (!hasWarning) {
480: hasWarning = true;
481:
482: log
483: .warning("Closing dangling connections. All connections must have a close() in a finally block.");
484: }
485:
486: try {
487: IllegalStateException stackTrace = userPoolItem
488: .getAllocationStackTrace();
489:
490: if (stackTrace != null)
491: log.log(Level.WARNING, stackTrace.getMessage(),
492: stackTrace);
493: else {
494: // start saving the allocation stack trace.
495: userPoolItem.setSaveAllocationStackTrace(true);
496: }
497:
498: if (exn == null)
499: exn = new IllegalStateException(
500: L
501: .l(
502: "Connection {0} was not closed. Connections must have a close() in a finally block.",
503: userPoolItem
504: .getUserConnection()));
505:
506: userPoolItem.abortConnection();
507: } catch (Throwable e) {
508: log.log(Level.WARNING, e.toString(), e);
509: }
510: }
511:
512: _poolItems.clear();
513:
514: try {
515: _transactionManager.setTransactionTimeout(0);
516: } catch (Throwable e) {
517: log.log(Level.WARNING, e.toString(), e);
518: }
519:
520: if (exn != null)
521: throw exn;
522: }
523: }
|