001: ///////////////////////////////////////////////////////////////////////////////
002: //
003: // Copyright (C) 2003-@year@ by Thomas M. Hazel, MyOODB (www.myoodb.org)
004: //
005: // All Rights Reserved
006: //
007: // This program is free software; you can redistribute it and/or modify
008: // it under the terms of the GNU General Public License and GNU Library
009: // General Public License as published by the Free Software Foundation;
010: // either version 2, or (at your option) any later version.
011: //
012: // This program 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
015: // GNU General Public License and GNU Library General Public License
016: // for more details.
017: //
018: // You should have received a copy of the GNU General Public License
019: // and GNU Library General Public License along with this program; if
020: // not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
021: // MA 02139, USA.
022: //
023: ///////////////////////////////////////////////////////////////////////////////
024: package org.myoodb.core;
025:
026: import java.io.*;
027: import java.util.*;
028:
029: import org.myoodb.*;
030: import org.myoodb.exception.*;
031: import org.myoodb.core.command.*;
032:
033: public abstract class AbstractTransaction {
034: private static final org.myoodb.util.Logger LOGGER = org.myoodb.util.Logger
035: .getLogger(AbstractTransaction.class);
036:
037: public final static int STATUS_NULL = 0;
038: public final static int STATUS_NONE = 1;
039: public final static int STATUS_STARTING = 2;
040: public final static int STATUS_STARTED = 3;
041: public final static int STATUS_PREPARING = 4;
042: public final static int STATUS_PREPARED = 5;
043: public final static int STATUS_COMMITING = 6;
044: public final static int STATUS_COMMITED = 7;
045: public final static int STATUS_ABORTING = 8;
046: public final static int STATUS_ABORTED = 9;
047:
048: public final static int IMPLICIT_TRANSACTION = 1;
049: public final static int EXPLICIT_TRANSACTION = 2;
050:
051: private User m_owner;
052: private int m_status;
053: private int m_maxLockLevel;
054: private long m_transactionIdentifier;
055:
056: private boolean m_snapshotFlag;
057: private boolean m_explicitPiggyBack;
058: private boolean m_stoppedTransactionFlag;
059: private int m_transactionType = IMPLICIT_TRANSACTION;
060:
061: private AbstractClient m_client;
062: private AbstractTransaction m_parentTransaction;
063:
064: protected AbstractTransaction(User owner) {
065: m_owner = owner;
066:
067: m_transactionIdentifier = MyOodbManager.getTheManager()
068: .getIdentifierManager().getNextTransactionId();
069:
070: // XXX: let the JVM initialize the rest of our attributes
071: reset();
072: }
073:
074: protected AbstractTransaction(long transactionIdentifier) {
075: m_transactionIdentifier = transactionIdentifier;
076:
077: // XXX: let the JVM initialize the rest of our attributes
078: reset();
079: }
080:
081: private boolean snapShot() {
082: boolean result = false;
083:
084: try {
085: setSnapShotFlag(true);
086:
087: MyOodbManager.getTheManager().getStoreManager()
088: .prepareTransaction(this );
089: MyOodbManager.getTheManager().getStoreManager()
090: .commitTransaction(this );
091:
092: result = true;
093: } catch (Exception e) {
094: LOGGER.error(null, e);
095: } finally {
096: setSnapShotFlag(false);
097: }
098:
099: return result;
100: }
101:
102: private void removeDeadLocks(AbstractObjectContainer container) {
103: long lockerId = container.getLock().getLockIdentifier();
104: AbstractTransaction tx = MyOodbManager.getTheManager()
105: .getTransactionManager().getTransaction(lockerId);
106: if ((tx == null) || (tx.getStatus() >= STATUS_ABORTING)) {
107: container.getLock().release(null);
108: }
109: }
110:
111: private AbstractObjectContainer acquireAccess(AbstractObjectContainer container, int lockLevel) throws Exception
112: {
113: if (m_stoppedTransactionFlag == true)
114: {
115: throw new TransactionException("Transaction has been Stopped");
116: }
117:
118: if (container.getLock().getLockIdentifier() != -1)
119: {
120: removeDeadLocks(container);
121: }
122:
123: if (lockLevel > getMaxLockLevel())
124: {
125: setMaxLockLevel(lockLevel);
126: }
127:
128: // if this is not a "dirty read" transaction, try to aquire the lock of the container; wait until the locks is
129: // successfully aquired
130: if ((@dirtyRead@ != true) || (lockLevel > org.myoodb.MyOodbAccess.READ))
131: {
132: int prevLevel = container.getLock().tryAcquire(this , lockLevel);
133:
134: // as the name indicates, "mighty write" can always write
135: if ((@mightyWrite@ == true) && (getMaxLockLevel() == org.myoodb.MyOodbAccess.MIGHTY_WRITE))
136: {
137: // since this is a "mighty write" and there is another write in progress, piggy back on this write
138: if (prevLevel == org.myoodb.MyOodbAccess.NONE)
139: {
140: prevLevel = org.myoodb.MyOodbAccess.READ;
141: m_explicitPiggyBack = true;
142: }
143: }
144: else if (prevLevel == org.myoodb.MyOodbAccess.NONE)
145: {
146: // allow clients to nest their explicit transactions that are on the same thread
147: if (m_transactionType == AbstractTransaction.EXPLICIT_TRANSACTION)
148: {
149: AbstractTransaction parent = getParentTransaction();
150: while ((parent != null))
151: {
152: if (container.getLock().getLockIdentifier() == parent.getTransactionIdentifier())
153: {
154: prevLevel = org.myoodb.MyOodbAccess.READ;
155: break;
156: }
157:
158: parent = parent.getParentTransaction();
159: }
160: }
161: else if (@nestImplicitWrite@ == true) // allow clients to nest their implicit transactions regardless if they are on the same thread
162: {
163: long lockerId = container.getLock().getLockIdentifier();
164: AbstractTransaction tx = MyOodbManager.getTheManager().getTransactionManager().getTransaction(lockerId);
165: if ((tx != null) && (tx.getTransactionType() == AbstractTransaction.IMPLICIT_TRANSACTION))
166: {
167: prevLevel = org.myoodb.MyOodbAccess.READ;
168: lockLevel = org.myoodb.MyOodbAccess.READ;
169: }
170: }
171: }
172:
173: // since the lock was not acquire, we need to wait
174: if (prevLevel == org.myoodb.MyOodbAccess.NONE)
175: {
176: synchronized (this )
177: {
178: while (prevLevel == org.myoodb.MyOodbAccess.NONE)
179: {
180: // explicit transactions cannot block on another user's write lock (only implicit)
181: if (m_transactionType == AbstractTransaction.EXPLICIT_TRANSACTION)
182: {
183: String objType = null;
184: String objName = null;
185: String objOwner = null;
186:
187: MyOodbLocal target = container.getTarget(0);
188: if (target != null)
189: {
190: objType = target.getClass().getName();
191: objName = target.toString();
192: }
193:
194: long lockerId = container.getLock().getLockIdentifier();
195: AbstractTransaction tx = MyOodbManager.getTheManager().getTransactionManager().getTransaction(lockerId);
196: if (tx != null)
197: {
198: objOwner = tx.getOwner().getName();
199: }
200:
201: throw new NestedTransactionException("Explicit Transactions cannot be blocked", objType, objName, objOwner);
202: }
203:
204: wait();
205:
206: if (m_stoppedTransactionFlag == true)
207: {
208: throw new TransactionException("Transaction has been Stopped");
209: }
210:
211: prevLevel = container.getLock().tryAcquire(this , lockLevel);
212:
213: } // end try loop
214:
215: } // end try loop (synchronize)
216:
217: } // end try loop (begin if check)
218:
219: // after acquiring the lock we associate the container with this transaction
220: if (lockLevel > org.myoodb.MyOodbAccess.READ)
221: {
222: try
223: {
224: MyOodbManager.getTheManager().getStoreManager().setAssociatedTransaction(this , container);
225: container.setDirtyFlag(true);
226: }
227: catch (RuntimeException e)
228: {
229: throw e;
230: }
231: catch (Exception e)
232: {
233: throw new InternalException("Caught during transaction container association: " + e, e);
234: }
235: }
236: }
237:
238: return container;
239: }
240:
241: private AbstractObjectContainer acquireContainer(Identifier id,
242: int lockLevel) {
243: AbstractObjectContainer container = null;
244:
245: try {
246: container = MyOodbManager.getTheManager().getStoreManager()
247: .getContainer(null, id);
248: } catch (RuntimeException e) {
249: throw e;
250: } catch (Exception e) {
251: throw new InternalException(
252: "Caught during acquire container: " + e, e);
253: }
254:
255: if (container == null) {
256: throw new ObjectNotFoundException("No such object: " + id);
257: }
258:
259: try {
260: return acquireAccess(container, lockLevel);
261: } catch (RuntimeException e) {
262: throw e;
263: } catch (Exception e) {
264: throw new InternalException(
265: "Caught during acquire container: " + e, e);
266: }
267: }
268:
269: public int getStatus() {
270: return m_status;
271: }
272:
273: public User getOwner() {
274: return m_owner;
275: }
276:
277: public long getTransactionIdentifier() {
278: return m_transactionIdentifier;
279: }
280:
281: public boolean isExplicitPiggyBack() {
282: return m_explicitPiggyBack;
283: }
284:
285: public void setMaxLockLevel(int level) {
286: m_maxLockLevel = level;
287: }
288:
289: public int getMaxLockLevel() {
290: return m_maxLockLevel;
291: }
292:
293: public void setTransactionType(int type) {
294: m_transactionType = type;
295: }
296:
297: public int getTransactionType() {
298: return m_transactionType;
299: }
300:
301: public void setSnapShotFlag(boolean flag) {
302: m_snapshotFlag = flag;
303: }
304:
305: public boolean getSnapShotFlag() {
306: return m_snapshotFlag;
307: }
308:
309: public void setParentTransaction(AbstractTransaction parent) {
310: m_parentTransaction = parent;
311: }
312:
313: public AbstractTransaction getParentTransaction() {
314: return m_parentTransaction;
315: }
316:
317: public void setClient(AbstractClient client) {
318: m_client = client;
319: }
320:
321: public AbstractClient getClient() {
322: return m_client;
323: }
324:
325: public void stop() {
326: m_stoppedTransactionFlag = true;
327:
328: m_maxLockLevel = org.myoodb.MyOodbAccess.READ;
329: }
330:
331: public void reset() {
332: m_status = STATUS_STARTING;
333: m_transactionType = IMPLICIT_TRANSACTION;
334: }
335:
336: protected boolean processCommand(AbstractCommand command) {
337: boolean result = true;
338: try {
339: command.process(this );
340: } catch (RuntimeException e) {
341: command.setResult(e);
342: result = false;
343: } catch (Exception e) {
344: command.setResult(new InternalException(
345: "Caught during process command: " + e, e));
346: result = false;
347: } finally {
348: if ((result == false)
349: && (getMaxLockLevel() > org.myoodb.MyOodbAccess.READ)
350: && (getTransactionType() == AbstractTransaction.IMPLICIT_TRANSACTION)) {
351: abort();
352: }
353: }
354:
355: return result;
356: }
357:
358: protected void prepare() {
359: synchronized (this ) {
360: m_status = STATUS_PREPARING;
361:
362: try {
363: MyOodbManager.getTheManager().getStoreManager()
364: .prepareTransaction(this );
365: } catch (RuntimeException e) {
366: throw e;
367: } catch (Exception e) {
368: throw new InternalException("Caught during prepare: "
369: + e, e);
370: }
371:
372: m_status = STATUS_PREPARED;
373: }
374: }
375:
376: public void commit() {
377: synchronized (this ) {
378: m_status = STATUS_COMMITING;
379:
380: try {
381: MyOodbManager.getTheManager().getStoreManager()
382: .commitTransaction(this );
383: } catch (RuntimeException e) {
384: throw e;
385: } catch (Exception e) {
386: throw new InternalException("Caught during commit: "
387: + e, e);
388: } finally {
389: stop();
390: }
391:
392: m_status = STATUS_COMMITED;
393: }
394: }
395:
396: public void abort() {
397: synchronized (this ) {
398: m_status = STATUS_ABORTING;
399:
400: try {
401: MyOodbManager.getTheManager().getStoreManager()
402: .abortTransaction(this );
403: } catch (RuntimeException e) {
404: throw e;
405: } catch (Exception e) {
406: throw new InternalException(
407: "Caught during abort: " + e, e);
408: } finally {
409: stop();
410: }
411:
412: m_status = STATUS_ABORTED;
413: }
414: }
415:
416: public AbstractObjectContainer create(String rootName,
417: String className, String sig, Object[] args) {
418: if ((rootName != null)
419: && (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION)) {
420: throw new PermissionException(
421: "Explicit Transactions cannot create roots");
422: }
423:
424: m_status = STATUS_STARTED;
425:
426: try {
427: boolean noException = false;
428: boolean rootObject = (rootName != null);
429: String[] classNames = className.split(",");
430: Class[] classTypes = new Class[classNames.length];
431:
432: for (int i = 0; i < classTypes.length; i++) {
433: classTypes[i] = MyOodbManager.getTheManager()
434: .getClassManager().getClass(classNames[i]);
435: }
436:
437: Identifier id = new Identifier(MyOodbManager
438: .getTheManager().getIdentifierManager()
439: .getNextObjectId(), rootObject);
440: AbstractObjectContainer container = MyOodbManager
441: .getTheManager().getStoreManager().createContainer(
442: this , id, rootName);
443: setMaxLockLevel(org.myoodb.MyOodbAccess.WRITE);
444:
445: try {
446: container.createTarget(classTypes, sig, args);
447: noException = true;
448: return container;
449: } finally {
450: if (noException) {
451: if (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
452: container.setDirtyFlag(true);
453: snapShot();
454: }
455: }
456: }
457: } catch (java.lang.reflect.InvocationTargetException e) {
458: Throwable ee = e.getTargetException();
459:
460: if (ee instanceof ObjectException) {
461: throw (ObjectException) ee;
462: }
463:
464: throw new ObjectException("caught", ee);
465: } catch (RuntimeException e) {
466: throw e;
467: } catch (Exception e) {
468: throw new InternalException("Caught during create: " + e, e);
469: }
470: }
471:
472: public void delete(Identifier id) {
473: m_status = STATUS_STARTED;
474:
475: try {
476: boolean noException = false;
477: AbstractObjectContainer container = acquireContainer(id,
478: org.myoodb.MyOodbAccess.WRITE);
479:
480: try {
481: container.deleteTarget();
482: noException = true;
483: } finally {
484: if (noException) {
485: if (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
486: container.setDirtyFlag(true);
487: snapShot();
488: }
489: }
490: }
491: } catch (java.lang.reflect.InvocationTargetException e) {
492: Throwable ee = e.getTargetException();
493:
494: if (ee instanceof ObjectException) {
495: throw (ObjectException) ee;
496: }
497:
498: throw new ObjectException("caught", ee);
499: } catch (RuntimeException e) {
500: throw e;
501: } catch (Exception e) {
502: throw new InternalException("Caught during delete: " + e, e);
503: }
504: }
505:
506: public MyOodbProxy getRoot(String rootName) {
507: try {
508: AbstractObjectContainer container = MyOodbManager
509: .getTheManager().getStoreManager().getContainer(
510: rootName, null);
511: return (container != null) ? container.getProxy() : null;
512: } catch (RuntimeException e) {
513: throw e;
514: } catch (Exception e) {
515: throw new InternalException("Caught during get root: " + e,
516: e);
517: }
518: }
519:
520: public MyOodbProxy getObject(Identifier id) {
521: try {
522: AbstractObjectContainer container = MyOodbManager
523: .getTheManager().getStoreManager().getContainer(
524: null, id);
525: return (container != null) ? container.getProxy() : null;
526: } catch (RuntimeException e) {
527: throw e;
528: } catch (Exception e) {
529: throw new InternalException("Caught during get object: "
530: + e, e);
531: }
532: }
533:
534: public void setBean(Identifier id, MyOodbBean bean) {
535: m_status = STATUS_STARTED;
536:
537: try {
538: boolean noException = false;
539: AbstractObjectContainer container = acquireContainer(id,
540: org.myoodb.MyOodbAccess.WRITE);
541:
542: try {
543: container.setBean(bean);
544: noException = true;
545: } finally {
546: if (noException) {
547: if (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
548: container.setDirtyFlag(true);
549: snapShot();
550: }
551: }
552: }
553: } catch (java.lang.reflect.InvocationTargetException e) {
554: Throwable ee = e.getTargetException();
555:
556: if (ee instanceof ObjectException) {
557: throw (ObjectException) ee;
558: }
559:
560: throw new ObjectException("caught", ee);
561: } catch (RuntimeException e) {
562: throw e;
563: } catch (Exception e) {
564: throw new InternalException("Caught during set bean: " + e,
565: e);
566: }
567: }
568:
569: public MyOodbBean getBean(Identifier id) {
570: try {
571: AbstractObjectContainer container = MyOodbManager
572: .getTheManager().getStoreManager().getContainer(
573: null, id);
574: return (container != null) ? container.getBean() : null;
575: } catch (RuntimeException e) {
576: throw e;
577: } catch (Exception e) {
578: throw new InternalException("Caught during get bean: " + e,
579: e);
580: }
581: }
582:
583: public void setXML(Identifier id, String xml) {
584: m_status = STATUS_STARTED;
585:
586: try {
587: boolean noException = false;
588: AbstractObjectContainer container = acquireContainer(id,
589: org.myoodb.MyOodbAccess.WRITE);
590:
591: try {
592: container.setXML(xml);
593: noException = true;
594: } finally {
595: if (noException) {
596: if (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
597: container.setDirtyFlag(true);
598: snapShot();
599: }
600: }
601: }
602: } catch (java.lang.reflect.InvocationTargetException e) {
603: Throwable ee = e.getTargetException();
604:
605: if (ee instanceof ObjectException) {
606: throw (ObjectException) ee;
607: }
608:
609: throw new ObjectException("caught", ee);
610: } catch (RuntimeException e) {
611: throw e;
612: } catch (Exception e) {
613: throw new InternalException("Caught during set xml: " + e,
614: e);
615: }
616: }
617:
618: public String getXML(Identifier id) {
619: try {
620: AbstractObjectContainer container = MyOodbManager
621: .getTheManager().getStoreManager().getContainer(
622: null, id);
623: return (container != null) ? container.getXML() : null;
624: } catch (RuntimeException e) {
625: throw e;
626: } catch (Exception e) {
627: throw new InternalException("Caught during get xml: " + e,
628: e);
629: }
630: }
631:
632: public Object invokeMethod(Identifier id, int methodIndex,
633: Object[] args, int lockLevel) {
634: m_status = STATUS_STARTED;
635:
636: try {
637: Object result = null;
638: boolean noException = false;
639: AbstractObjectContainer container = acquireContainer(id,
640: lockLevel);
641:
642: try {
643: result = container.invokeTarget(methodIndex, args,
644: lockLevel);
645: noException = true;
646: } finally {
647: if (noException) {
648: if (lockLevel > org.myoodb.MyOodbAccess.READ) {
649: if (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
650: container.setDirtyFlag(true);
651: snapShot();
652: }
653: }
654: }
655: }
656:
657: return result;
658: } catch (java.lang.reflect.InvocationTargetException e) {
659: Throwable ee = e.getTargetException();
660:
661: if (ee instanceof ObjectException) {
662: throw (ObjectException) ee;
663: }
664:
665: throw new ObjectException("caught", ee);
666: } catch (RuntimeException e) {
667: throw e;
668: } catch (Exception e) {
669: throw new InternalException("Caught during invoke method: "
670: + e, e);
671: }
672: }
673:
674: public Object invokeMethod(Identifier id, String methodName,
675: String sig, Object[] args, int lockLevel) {
676: m_status = STATUS_STARTED;
677:
678: try {
679: Object result = null;
680: boolean noException = false;
681: AbstractObjectContainer container = acquireContainer(id,
682: lockLevel);
683:
684: try {
685: result = container.invokeTarget(methodName, sig, args,
686: lockLevel);
687: noException = true;
688: } finally {
689: if (noException) {
690: if (lockLevel > org.myoodb.MyOodbAccess.READ) {
691: if (getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
692: container.setDirtyFlag(true);
693: snapShot();
694: }
695: }
696: }
697: }
698:
699: return result;
700:
701: } catch (java.lang.reflect.InvocationTargetException e) {
702: Throwable ee = e.getTargetException();
703:
704: if (ee instanceof ObjectException) {
705: throw (ObjectException) ee;
706: }
707:
708: throw new ObjectException("caught", ee);
709: } catch (RuntimeException e) {
710: throw e;
711: } catch (Exception e) {
712: throw new InternalException("Caught during invoke method: "
713: + e, e);
714: }
715: }
716:
717: public boolean equals(Object obj) {
718: return hashCode() == obj.hashCode();
719: }
720:
721: public String toString() {
722: long pid = (m_parentTransaction != null) ? m_parentTransaction
723: .getTransactionIdentifier() : 0;
724: return "tx=" + m_transactionIdentifier + "-"
725: + m_transactionType + "-" + pid + "," + getOwner();
726: }
727: }
|