001: /*
002: * Copyright 2004 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: NonmanagedTransaction.java,v 1.10 2004/01/18 03:01:05 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo;
012:
013: import com.triactive.jdo.store.DatabaseAdapter;
014: import java.sql.Connection;
015: import java.sql.SQLException;
016: import javax.jdo.JDOFatalDataStoreException;
017: import javax.jdo.JDOUnsupportedOptionException;
018: import javax.jdo.PersistenceManager;
019: import javax.sql.DataSource;
020: import javax.transaction.Status;
021: import javax.transaction.Synchronization;
022: import org.apache.log4j.Category;
023:
024: class NonmanagedTransaction implements Transaction {
025: /**
026: * The system property that selects whether SELECT FOR UPDATE should be used
027: * to fetch objects when the transaction isolation level is READ COMMITTED
028: * or lower. This is the string "com.triactive.jdo.useUpdateLock".
029: */
030: public static final String USE_UPDATE_LOCK_PROPERTY = "com.triactive.jdo.useUpdateLock";
031:
032: private static final Category LOG = Category
033: .getInstance(NonmanagedTransaction.class);
034: private static boolean useUpdateLockProperty = Boolean
035: .getBoolean(USE_UPDATE_LOCK_PROPERTY);
036:
037: private final PersistenceManagerImpl pm;
038: private final DatabaseAdapter dba;
039: private final DataSource tds;
040: private final DataSource ntds;
041: private final String userName;
042: private final String password;
043:
044: private boolean retainValues;
045: private boolean restoreValues;
046: private boolean optimistic;
047: private boolean nontransactionalRead;
048: private boolean nontransactionalWrite;
049: private int isolationLevel;
050: private boolean useUpdateLock;
051:
052: private Synchronization sync = null;
053: private Connection conn = null;
054: private boolean active = false;
055: private boolean completing = false;
056:
057: public NonmanagedTransaction(PersistenceManagerImpl pm,
058: String userName, String password) {
059: this .pm = pm;
060: this .dba = pm.getStoreManager().getDatabaseAdapter();
061:
062: PersistenceManagerFactoryImpl pmf = (PersistenceManagerFactoryImpl) pm
063: .getPersistenceManagerFactory();
064:
065: this .tds = pmf.getTransactionalDataSource();
066: this .ntds = pmf.getNontransactionalDataSource();
067: this .userName = userName;
068: this .password = password;
069:
070: setRetainValues(pmf.getRetainValues());
071: setRestoreValues(pmf.getRestoreValues());
072: setOptimistic(pmf.getOptimistic());
073: setNontransactionalRead(pmf.getNontransactionalRead());
074: setNontransactionalWrite(pmf.getNontransactionalWrite());
075: setTransactionIsolation(pmf.getTransactionIsolation());
076: }
077:
078: public PersistenceManager getPersistenceManager() {
079: return pm;
080: }
081:
082: public synchronized Connection getConnection(boolean forWriting)
083: throws SQLException {
084: if (active) {
085: /*
086: * Before performing any (more) SQL operations in this transaction,
087: * flush out any updates pending in the persistence manager.
088: */
089: pm.flushDirty();
090: } else {
091: if (!nontransactionalRead
092: || (forWriting && !nontransactionalWrite))
093: throw new TransactionNotActiveException();
094:
095: if (conn != null)
096: throw new ConnectionInUseException();
097:
098: conn = dba.getConnection(ntds, userName, password,
099: Connection.TRANSACTION_NONE);
100: }
101:
102: return conn;
103: }
104:
105: public synchronized void releaseConnection(Connection conn)
106: throws SQLException {
107: if (conn == this .conn) {
108: if (!active)
109: closeConnection();
110: }
111: }
112:
113: public boolean useUpdateLockOnFetch() {
114: return active ? useUpdateLock : false;
115: }
116:
117: private void closeConnection() throws SQLException {
118: try {
119: dba.closeConnection(conn);
120: } finally {
121: conn = null;
122: }
123: }
124:
125: private void assertNotInUse() {
126: if (active)
127: throw new TransactionActiveException();
128:
129: if (conn != null)
130: throw new ConnectionInUseException();
131: }
132:
133: private void assertNotCompleting() {
134: if (completing)
135: throw new TransactionCompletionException();
136: }
137:
138: private void assertCanComplete() {
139: assertNotCompleting();
140:
141: if (!active)
142: throw new TransactionNotActiveException();
143: }
144:
145: public synchronized void begin() {
146: assertNotInUse();
147:
148: try {
149: conn = dba.getConnection(tds, userName, password,
150: isolationLevel);
151:
152: active = true;
153:
154: if (LOG.isDebugEnabled())
155: LOG.debug("Transaction begun, connection = " + conn);
156: } catch (SQLException e) {
157: throw new JDOFatalDataStoreException(
158: "Failed beginning transaction", e);
159: }
160: }
161:
162: public synchronized void commit() {
163: assertCanComplete();
164:
165: completing = true;
166:
167: try {
168: boolean success = false;
169:
170: try {
171: try {
172: if (LOG.isDebugEnabled())
173: LOG
174: .debug("Transaction committing, connection = "
175: + conn);
176:
177: pm.flushDirty();
178:
179: if (sync != null)
180: sync.beforeCompletion();
181:
182: conn.commit();
183:
184: success = true;
185: } finally {
186: if (!success) {
187: try {
188: pm.preRollback();
189: } finally {
190: conn.rollback();
191: }
192: }
193: }
194: } finally {
195: try {
196: active = false;
197: closeConnection();
198: } finally {
199: try {
200: if (success)
201: pm.postCommit();
202: } finally {
203: if (sync != null) {
204: if (success)
205: sync
206: .afterCompletion(Status.STATUS_COMMITTED);
207: else
208: sync
209: .afterCompletion(Status.STATUS_ROLLEDBACK);
210: }
211: }
212: }
213: }
214: } catch (SQLException e) {
215: throw new JDOFatalDataStoreException(
216: "Failed committing transaction", e);
217: } finally {
218: completing = false;
219: }
220: }
221:
222: public synchronized void rollback() {
223: assertCanComplete();
224:
225: completing = true;
226:
227: try {
228: try {
229: try {
230: if (LOG.isDebugEnabled())
231: LOG
232: .debug("Transaction rolling back, connection = "
233: + conn);
234:
235: pm.preRollback();
236: } finally {
237: conn.rollback();
238: }
239: } finally {
240: try {
241: active = false;
242: closeConnection();
243: } finally {
244: if (sync != null)
245: sync.afterCompletion(Status.STATUS_ROLLEDBACK);
246: }
247: }
248: } catch (SQLException e) {
249: throw new JDOFatalDataStoreException(
250: "Failed rolling back transaction", e);
251: } finally {
252: completing = false;
253: }
254: }
255:
256: public boolean isActive() {
257: return active;
258: }
259:
260: public synchronized void setNontransactionalRead(
261: boolean nontransactionalRead) {
262: assertNotCompleting();
263:
264: this .nontransactionalRead = nontransactionalRead;
265: }
266:
267: public boolean getNontransactionalRead() {
268: return nontransactionalRead;
269: }
270:
271: public synchronized void setNontransactionalWrite(
272: boolean nontransactionalWrite) {
273: assertNotCompleting();
274:
275: if (nontransactionalWrite)
276: throw new JDOUnsupportedOptionException(
277: "Non-transactional write mode not (yet) supported");
278:
279: this .nontransactionalWrite = nontransactionalWrite;
280: }
281:
282: public boolean getNontransactionalWrite() {
283: return nontransactionalWrite;
284: }
285:
286: public synchronized void setRetainValues(boolean retainValues) {
287: assertNotCompleting();
288:
289: this .retainValues = retainValues;
290:
291: if (retainValues)
292: nontransactionalRead = true;
293: }
294:
295: public boolean getRetainValues() {
296: return retainValues;
297: }
298:
299: public synchronized void setRestoreValues(boolean restoreValues) {
300: assertNotInUse();
301:
302: this .restoreValues = restoreValues;
303: }
304:
305: public boolean getRestoreValues() {
306: return restoreValues;
307: }
308:
309: public synchronized void setOptimistic(boolean optimistic) {
310: assertNotInUse();
311:
312: if (optimistic)
313: throw new JDOUnsupportedOptionException(
314: "Optimistic mode not (yet) supported");
315:
316: this .optimistic = optimistic;
317: }
318:
319: public boolean getOptimistic() {
320: return optimistic;
321: }
322:
323: public synchronized void setTransactionIsolation(int isolationLevel) {
324: assertNotInUse();
325:
326: switch (isolationLevel) {
327: case Connection.TRANSACTION_READ_UNCOMMITTED:
328: case Connection.TRANSACTION_READ_COMMITTED:
329: useUpdateLock = useUpdateLockProperty;
330: break;
331:
332: case Connection.TRANSACTION_REPEATABLE_READ:
333: case Connection.TRANSACTION_SERIALIZABLE:
334: useUpdateLock = false;
335: break;
336:
337: default:
338: throw new IllegalArgumentException(
339: "Illegal isolation level: " + isolationLevel);
340: }
341:
342: this .isolationLevel = isolationLevel;
343: }
344:
345: public int getTransactionIsolation() {
346: return isolationLevel;
347: }
348:
349: public synchronized void setSynchronization(Synchronization sync) {
350: assertNotCompleting();
351:
352: this .sync = sync;
353: }
354:
355: public Synchronization getSynchronization() {
356: return sync;
357: }
358: }
|