001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.transact.impl;
010:
011: import com.completex.objective.components.OdalRuntimeException;
012: import com.completex.objective.components.log.Log;
013: import com.completex.objective.components.persistency.OdalPersistencyException;
014: import com.completex.objective.components.persistency.OdalRuntimePersistencyException;
015: import com.completex.objective.components.persistency.mapper.ThreadSession;
016: import com.completex.objective.components.persistency.transact.Transaction;
017: import com.completex.objective.components.persistency.transact.TransactionManager;
018:
019: import java.sql.CallableStatement;
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.sql.Statement;
025:
026: /**
027: * @author Gennady Krizhevsky
028: */
029: public abstract class AbstractTransactionManager implements
030: TransactionManager {
031:
032: private Log logger = Log.NULL_LOGGER;
033: private TransactionContainer transactionContainer = new TransactionContainer();
034: private boolean checkForBadConnection;
035: private int maxAttempts = 5;
036: private boolean clearSessionOnTransactionRelease = true;
037:
038: protected AbstractTransactionManager() {
039: }
040:
041: /**
042: * @param logger
043: */
044: protected AbstractTransactionManager(Log logger) {
045: setLogger(logger);
046: }
047:
048: /**
049: * @return Log
050: */
051: public Log getLogger() {
052: return logger;
053: }
054:
055: /**
056: * @param logger
057: */
058: public void setLogger(Log logger) {
059: if (logger != null) {
060: this .logger = logger;
061: }
062: }
063:
064: /**
065: * @return created Connection
066: * @throws SQLException
067: */
068: abstract protected Connection createConnection()
069: throws SQLException;
070:
071: /**
072: * Close/release connection
073: *
074: * @param connection
075: * @throws SQLException
076: */
077: abstract protected void destroyConnection(Connection connection)
078: throws SQLException;
079:
080: /**
081: * @param connection
082: * @param sql
083: * @return new PreparedStatement
084: * @throws SQLException
085: */
086: abstract protected PreparedStatement prepareStatement(
087: Connection connection, String sql) throws SQLException;
088:
089: /**
090: * @param connection
091: * @param sql
092: * @return new CallableStatement
093: * @throws SQLException
094: */
095: abstract protected CallableStatement prepareCall(
096: Connection connection, String sql) throws SQLException;
097:
098: /**
099: * @param connection
100: * @param statement
101: * @throws SQLException
102: */
103: abstract protected void releaseStatement(Connection connection,
104: PreparedStatement statement) throws SQLException;
105:
106: /**
107: * Something like "select 1 from dual" for oracle.Should delegate it to
108: * underlying DatabasePolicy
109: *
110: * @return Connection Validation Statement - String
111: */
112: abstract protected String getConnectionValidationStatement();
113:
114: /**
115: * @return new Transaction
116: * @throws OdalPersistencyException
117: * @see com.completex.objective.components.persistency.transact.TransactionManager#begin()
118: */
119: public Transaction begin() throws OdalPersistencyException {
120: Transaction transaction = beginAssureActive();
121: if (getLogger().isDebugEnabled()) {
122: getLogger().debug(
123: "BEGIN TRANSACTION "
124: + transaction.connectionToString());
125: }
126: return transaction;
127: }
128:
129: public Transaction beginUnchecked() {
130: try {
131: return begin();
132: } catch (OdalPersistencyException e) {
133: throw new OdalRuntimeException(e.getClass().getName()
134: + ": " + e.getMessage());
135: }
136: }
137:
138: public void rollbackUnchecked(Transaction transaction) {
139: try {
140: rollback(transaction);
141: } catch (SQLException e) {
142: throw new OdalRuntimeException(e.getClass().getName()
143: + ": " + e.getMessage());
144: }
145: }
146:
147: public void commitUnchecked(Transaction transaction) {
148: try {
149: commit(transaction);
150: } catch (SQLException e) {
151: throw new OdalRuntimeException(e.getClass().getName()
152: + ": " + e.getMessage());
153: }
154: }
155:
156: public void rollbackSilently(Transaction transaction) {
157: if (transaction != null) {
158: try {
159: rollback(transaction);
160: } catch (Exception e) {
161: // Silently!
162: getLogger().error("", e);
163: }
164: }
165: }
166:
167: /**
168: * @return Transaction
169: * @throws SQLException
170: */
171: private Transaction begin0() throws SQLException {
172: return new TransactionImpl(createConnection(), getLogger());
173: }
174:
175: /**
176: * @return current transaction
177: * @see com.completex.objective.components.persistency.transact.TransactionManager#getCurrentTransaction()
178: */
179: public Transaction getCurrentTransaction() {
180: return transactionContainer.peekTransaction();
181: }
182:
183: public boolean hasCurrentTransaction() {
184: return !transactionContainer.isEmpty();
185: }
186:
187: /**
188: * Get Transaction from the pool with validaty check
189: *
190: * @return new Transaction
191: * @throws OdalPersistencyException if Validating statement does not return result
192: * @throws OdalRuntimePersistencyException
193: * if cannot get connection in Max attempts
194: */
195: protected Transaction beginAssureActive()
196: throws OdalPersistencyException {
197: Throwable cause = null;
198: int currentSize = 0;
199: int maxAttempts = getMaxAttempts();
200: while (currentSize < maxAttempts) {
201: try {
202: currentSize++;
203: Transaction transaction = begin0();
204: if (!checkForBadConnection) {
205: return transaction;
206: }
207: Connection connection = transaction.getConnection();
208: PreparedStatement st = null;
209: ResultSet rs = null;
210: try {
211: if (getConnectionValidationStatement() != null) {
212: st = prepareStatement(transaction
213: .getConnection(),
214: getConnectionValidationStatement());
215: rs = st.executeQuery();
216: if (!rs.next()) {
217: throw new OdalPersistencyException(
218: "Validating statement did not return result");
219: }
220: connection.commit();
221: } else {
222: if (connection.isClosed()) {
223: throw new OdalPersistencyException(
224: "Connection is closed");
225: }
226: }
227: return transaction;
228: } catch (Exception e) {
229: try {
230: connection.close();
231: } catch (SQLException e1) {
232: // debug("Cannot close connection: " + e1.getMessage());
233: }
234: releaseBad(connection);
235: connection = null;
236: cause = e;
237: } finally {
238: if (rs != null) {
239: try {
240: rs.close();
241: } catch (Exception e) {
242: logger.error("", e);
243: }
244: }
245: if (connection != null && st != null) {
246: try {
247: releaseStatement(connection, st);
248: } catch (Exception e) {
249: logger.error("", e);
250: }
251: }
252: }
253: } catch (Exception e) {
254: //
255: // If we are here - the database is dead
256: //
257: try {
258: Thread.sleep(500);
259: } catch (InterruptedException e1) {
260: // Do nothing
261: }
262: if (e instanceof NullPointerException) {
263: throw ((NullPointerException) e);
264: }
265: cause = e;
266: }
267: }
268: throw new OdalPersistencyException("Cannot get connection in ["
269: + getMaxAttempts() + "] attempts", cause);
270: }
271:
272: /**
273: * @param checkForBadConnection true if check is required
274: */
275: public void setCheckForBadConnection(boolean checkForBadConnection) {
276: this .checkForBadConnection = checkForBadConnection;
277: }
278:
279: /**
280: * Does not reset default value if maxAttempts <= 1
281: *
282: * @param maxAttempts
283: */
284: public void setMaxAttempts(int maxAttempts) {
285: if (maxAttempts > 1) {
286: this .maxAttempts = maxAttempts;
287: }
288: }
289:
290: /**
291: * @return max attempts until giving up on beginnign transaction
292: */
293: public int getMaxAttempts() {
294: return maxAttempts;
295: }
296:
297: /**
298: * Remove bad connection from connection pool
299: *
300: * @param connection
301: */
302: protected abstract void releaseBad(Connection connection);
303:
304: /**
305: * @see TransactionManager#commit(com.completex.objective.components.persistency.transact.Transaction)
306: */
307: public void commit(Transaction transaction) throws SQLException {
308: transactionContainer.validateTransactionForRelease(transaction);
309: transaction.commit();
310: }
311:
312: /**
313: * @see TransactionManager#rollback(com.completex.objective.components.persistency.transact.Transaction)
314: */
315: public void rollback(Transaction transaction) throws SQLException {
316: transactionContainer.validateTransactionForRelease(transaction);
317: transaction.rollback();
318: }
319:
320: /**
321: * @see TransactionManager#release(com.completex.objective.components.persistency.transact.Transaction)
322: */
323: public Transaction releaseTransaction(Transaction transaction) {
324: try {
325: transactionContainer
326: .validateTransactionForRelease(transaction);
327: transactionContainer.popTransaction();
328: if (clearSessionOnTransactionRelease) {
329: ThreadSession.clear();
330: }
331: if (getLogger().isDebugEnabled()) {
332: getLogger().debug(
333: "RELEASE TRANSACTION "
334: + transaction.connectionToString());
335: }
336: } finally {
337: if (transaction != null) {
338: transaction.clearListerners();
339: }
340: }
341:
342: return transaction;
343: }
344:
345: /**
346: * Make transaction current
347: *
348: * @param transaction
349: * @return transaction that is used as parameter
350: */
351: public Transaction beginTransaction(Transaction transaction) {
352: return transactionContainer.pushTransaction(transaction);
353: }
354:
355: /**
356: * @param connection
357: * @return new Statement
358: * @throws OdalPersistencyException
359: */
360: protected Statement createStatement(Connection connection)
361: throws OdalPersistencyException {
362: try {
363: return connection.createStatement();
364: } catch (SQLException e) {
365: throw new OdalPersistencyException(e);
366: }
367: }
368:
369: public boolean isClearSessionOnTransactionRelease() {
370: return clearSessionOnTransactionRelease;
371: }
372:
373: public void setClearSessionOnTransactionRelease(
374: boolean clearSessionOnTransactionRelease) {
375: this .clearSessionOnTransactionRelease = clearSessionOnTransactionRelease;
376: }
377:
378: /**
379: * Basic Transaction implementation
380: */
381: private class TransactionImpl extends AbstractTransactionImpl {
382:
383: public TransactionImpl(Connection connection, Log logger) {
384: super (connection, logger);
385: }
386:
387: public TransactionImpl(Connection connection) {
388: super (connection);
389: }
390:
391: public Statement createStatement()
392: throws OdalPersistencyException {
393: return AbstractTransactionManager.this
394: .createStatement(getConnection());
395: }
396:
397: public PreparedStatement prepareStatement(String sql)
398: throws SQLException {
399: return AbstractTransactionManager.this .prepareStatement(
400: getConnection(), sql);
401: }
402:
403: public CallableStatement prepareCall(String sql)
404: throws SQLException {
405: return AbstractTransactionManager.this .prepareCall(
406: getConnection(), sql);
407: }
408:
409: public void releaseStatement(PreparedStatement statement)
410: throws SQLException {
411: AbstractTransactionManager.this.releaseStatement(
412: getConnection(), statement);
413: }
414: }
415:
416: }
|