001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Library License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: OzoneODMGTransaction.java,v 1.1 2002/05/08 15:03:21 per_nyfelt Exp $
008:
009: package org.ozoneDB.odmg;
010:
011: import org.odmg.*;
012: import org.ozoneDB.*;
013: import org.ozoneDB.DxLib.*;
014:
015: /**
016: * Implementation of the ODMG {@link Transaction} interface. A transaction works
017: * against all databases that are known by the <code>OzoneODMG</odmg> factory
018: * object when this transaction is created.
019: *
020: *
021: * @author <a href="http://www.softwarebuero.de/">SMB</a>
022: * @version $Revision: 1.1 $Date: 2002/05/08 15:03:21 $
023: * @see org.odmg.Transaction
024: */
025: public final class OzoneODMGTransaction implements Transaction {
026:
027: /**
028: * Holds the ExternalTransaction objects that this ODMG transaction has
029: * to handle.
030: */
031: private ExternalTransaction[] subTxs;
032:
033: /**
034: * Maps Threads into OzoneODMGTransaction.
035: */
036: private static DxMap txTable = new DxHashMap();
037:
038: protected static OzoneODMGTransaction current() {
039: return (OzoneODMGTransaction) txTable.elementForKey(Thread
040: .currentThread());
041: }
042:
043: /**
044: * Remove all threads that have joined the specified transaction.
045: * @param tx Transaction to remove from the global thread table.
046: */
047: protected static void remove(OzoneODMGTransaction tx) {
048: DxIterator it = txTable.iterator();
049: while (it.next() != null) {
050: OzoneODMGTransaction cursor = (OzoneODMGTransaction) it
051: .object();
052: if (cursor.equals(tx)) {
053: it.removeObject();
054: }
055: }
056: }
057:
058: public OzoneODMGTransaction(DxCollection _dbs) {
059: boolean readOnly = false;
060:
061: if (_dbs.count() == 0) {
062: throw new DatabaseClosedException(
063: "There is no open database.");
064: }
065:
066: subTxs = new ExternalTransaction[_dbs.count()];
067: DxIterator it = _dbs.iterator();
068: for (int i = 0; it.next() != null; i++) {
069: OzoneODMGDatabase odb = (OzoneODMGDatabase) it.object();
070: subTxs[i] = odb.underlying().newTransaction();
071:
072: if (odb.mode() == org.odmg.Database.OPEN_READ_ONLY) {
073: readOnly = true;
074: }
075: }
076:
077: // set all transactions to roll back only if one is opened in
078: // OPEN_READ_ONLY mode
079: try {
080: if (readOnly) {
081: for (int i = 0; i < subTxs.length; i++) {
082: subTxs[i].setRollbackOnly();
083: }
084: }
085: } catch (Exception e) {
086: subTxs = null;
087: throw new ODMGRuntimeException(e.toString());
088: }
089: }
090:
091: public void begin() {
092: if (current() != null) {
093: throw new TransactionInProgressException(
094: "Thread already joined.");
095: }
096:
097: if (subTxs == null) {
098: throw new TransactionNotInProgressException(
099: "Transaction already ended.");
100: }
101:
102: try {
103: for (int i = 0; i < subTxs.length; i++) {
104: subTxs[i].begin();
105: }
106: txTable.addForKey(this , Thread.currentThread());
107: } catch (Exception e) {
108: subTxs = null;
109: throw new ODMGRuntimeException(e.toString());
110: }
111: }
112:
113: public void join() {
114: if (subTxs == null) {
115: throw new TransactionNotInProgressException(
116: "Transaction already ended.");
117: }
118:
119: leave();
120:
121: if (!txTable.addForKey(this , Thread.currentThread())) {
122: throw new ODMGRuntimeException("Unable to join thread.");
123: }
124:
125: try {
126: for (int i = 0; i < subTxs.length; i++) {
127: subTxs[i].join();
128: }
129: } catch (Exception e) {
130: }
131: }
132:
133: public void leave() {
134: if (subTxs == null) {
135: throw new TransactionNotInProgressException(
136: "Transaction already ended.");
137: }
138:
139: txTable.removeForKey(Thread.currentThread());
140:
141: try {
142: for (int i = 0; i < subTxs.length; i++) {
143: subTxs[i].leave();
144: }
145: } catch (Exception e) {
146: throw new ODMGRuntimeException(e.toString());
147: }
148: }
149:
150: public boolean isOpen() {
151: return subTxs != null;
152: }
153:
154: public void commit() {
155: // ODMG 3.0 lets us commit only if the threads has joined the transaction
156: if (!this .equals(current())) {
157: throw new TransactionNotInProgressException(
158: "Thread has not joined the transaction.");
159: }
160:
161: if (subTxs == null) {
162: throw new TransactionNotInProgressException(
163: "Thread already ended.");
164: }
165:
166: // make one-phase commit if there is only one sub-transaction
167: if (subTxs.length == 1) {
168: try {
169: subTxs[0].commit(true);
170: } catch (Exception e) {
171: // if something was wrong the transaction is rolled back
172: // ODMG does not specify which exception to throw, so...
173: throw new ODMGRuntimeException(e.toString());
174: } finally {
175: subTxs = null;
176: remove(this );
177: }
178: } else {
179: try {
180: for (int i = 0; i < subTxs.length; i++) {
181: subTxs[i].prepare();
182: }
183:
184: for (int i = 0; i < subTxs.length; i++) {
185: subTxs[i].commit(false);
186: }
187: } catch (Exception e) {
188: abort();
189: throw new ODMGRuntimeException(e.toString());
190: } finally {
191: subTxs = null;
192: remove(this );
193: }
194: }
195: }
196:
197: public void abort() {
198: // ODMG 3.0 lets us commit only if the threads has joined the transaction
199: if (!this .equals(current())) {
200: throw new TransactionNotInProgressException(
201: "Thread has not joined the transaction.");
202: }
203:
204: // do not throw an exception to allow multiple calls of abort()
205: if (subTxs == null) {
206: return;
207: }
208:
209: try {
210: for (int i = 0; i < subTxs.length; i++) {
211: subTxs[i].rollback();
212: }
213: } catch (Exception e) {
214: // we can only throw the exception here
215: throw new ODMGRuntimeException(e.toString());
216: } finally {
217: subTxs = null;
218: remove(this );
219: }
220: }
221:
222: public void checkpoint() {
223: // ODMG 3.0 lets us commit only if the threads has joined the transaction
224: if (!this .equals(current())) {
225: throw new TransactionNotInProgressException(
226: "Thread has not joined the transaction.");
227: }
228:
229: if (subTxs == null) {
230: throw new TransactionNotInProgressException(
231: "Thread already ended.");
232: }
233:
234: try {
235: for (int i = 0; i < subTxs.length; i++) {
236: subTxs[i].checkpoint();
237: }
238: } catch (Exception e) {
239: // Hmmm... should we abort here?
240: abort();
241: throw new ODMGRuntimeException(e.toString());
242: }
243: }
244:
245: /**
246: * Upgrade the lock on the given object to the given lock mode. The call
247: * has no effect if the object's current lock is already at or above that
248: * level of lock mode.
249: * <p>
250: * This method does nothing currently. We rely on the class descriptor for
251: * lock modes for now.
252: */
253: public void lock(Object obj, int lockMode)
254: throws LockNotGrantedException {
255: }
256:
257: /**
258: * Upgrade the lock on the given object to the given lock mode.
259: * <p>
260: * This method does nothing currently. We rely on the class descriptor for
261: * lock modes for now.
262: */
263: public boolean tryLock(Object obj, int lockMode) {
264: // we have to implement something here; for now rely on the class
265: // descriptor for lock modes
266: return true;
267: }
268:
269: // protected void finalize throws Throwable {
270: // abort();
271: // }
272: }
|