001: package org.apache.ojb.broker.accesslayer;
002:
003: /* Copyright 2003-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.sql.Connection;
019: import java.sql.SQLException;
020:
021: import org.apache.ojb.broker.OJBRuntimeException;
022: import org.apache.ojb.broker.PBKey;
023: import org.apache.ojb.broker.PersistenceBroker;
024: import org.apache.ojb.broker.PersistenceBrokerException;
025: import org.apache.ojb.broker.TransactionAbortedException;
026: import org.apache.ojb.broker.TransactionInProgressException;
027: import org.apache.ojb.broker.TransactionNotInProgressException;
028: import org.apache.ojb.broker.core.PersistenceBrokerImpl;
029: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
030: import org.apache.ojb.broker.metadata.MetadataManager;
031: import org.apache.ojb.broker.platforms.Platform;
032: import org.apache.ojb.broker.platforms.PlatformFactory;
033: import org.apache.ojb.broker.util.batch.BatchConnection;
034: import org.apache.ojb.broker.util.logging.Logger;
035: import org.apache.ojb.broker.util.logging.LoggerFactory;
036:
037: /**
038: * Manages Connection ressources.
039: *
040: * @see ConnectionManagerIF
041: * @author Thomas Mahler
042: * @version $Id: ConnectionManagerImpl.java,v 1.17.2.4 2005/12/21 22:22:58 tomdz Exp $
043: */
044: public class ConnectionManagerImpl implements ConnectionManagerIF {
045: private Logger log = LoggerFactory
046: .getLogger(ConnectionManagerImpl.class);
047:
048: private PersistenceBrokerImpl broker = null;
049: private ConnectionFactory connectionFactory;
050: private JdbcConnectionDescriptor jcd;
051: private Platform platform;
052: private Connection con = null;
053: private PBKey pbKey;
054: private boolean originalAutoCommitState;
055: private boolean isInLocalTransaction;
056: private boolean batchMode;
057: private BatchConnection batchCon = null;
058:
059: public ConnectionManagerImpl(PersistenceBroker broker) {
060: // TODO: avoid this cast
061: this .broker = (PersistenceBrokerImpl) broker;
062: this .pbKey = broker.getPBKey();
063: this .jcd = MetadataManager.getInstance().connectionRepository()
064: .getDescriptor(pbKey);
065: this .connectionFactory = ConnectionFactoryFactory.getInstance()
066: .createConnectionFactory();
067: this .platform = PlatformFactory.getPlatformFor(jcd);
068: /*
069: by default batch mode is not enabled and after use of a PB
070: instance, before instance was returned to pool, batch mode
071: was set to false again (PB implementation close method)
072: Be carefully in modify this behaviour, changes could cause
073: unexpected behaviour
074: */
075: setBatchMode(false);
076: }
077:
078: /**
079: * Returns the associated {@link org.apache.ojb.broker.metadata.JdbcConnectionDescriptor}
080: */
081: public JdbcConnectionDescriptor getConnectionDescriptor() {
082: return jcd;
083: }
084:
085: public Platform getSupportedPlatform() {
086: return this .platform;
087: }
088:
089: /**
090: * Returns the underlying connection, requested from
091: * {@link org.apache.ojb.broker.accesslayer.ConnectionFactory}.
092: * <p>
093: * PB#beginTransaction() opens a single jdbc connection via
094: * PB#serviceConnectionManager().localBegin().
095: * If you call PB#serviceConnectionManager().getConnection() later
096: * it returns the already opened connection.
097: * The PB instance will release the used connection during
098: * PB#commitTransaction() or PB#abortTransaction() or PB#close().
099: * </p>
100: * <p>
101: * NOTE: Never call Connection.close() on the connection requested from the ConnectionManager.
102: * Cleanup of used connection is done by OJB itself. If you need to release a used connection
103: * call {@link #releaseConnection()}.
104: * </p>
105: */
106: public Connection getConnection() throws LookupException {
107: /*
108: if the connection is not null and we are not in a local tx, we check
109: the connection state and release "dead" connections.
110: if connection is in local tx we do nothing, the dead connection will cause
111: an exception and PB instance have to handle rollback
112: */
113: if (con != null && !isInLocalTransaction() && !isAlive(con)) {
114: releaseConnection();
115: }
116: if (con == null) {
117: con = this .connectionFactory.lookupConnection(jcd);
118: if (con == null)
119: throw new PersistenceBrokerException(
120: "Cannot get connection for " + jcd);
121: if (jcd.getUseAutoCommit() == JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE) {
122: try {
123: this .originalAutoCommitState = con.getAutoCommit();
124: } catch (SQLException e) {
125: throw new PersistenceBrokerException(
126: "Cannot request autoCommit state on the connection",
127: e);
128: }
129: }
130: if (log.isDebugEnabled())
131: log
132: .debug("Request new connection from ConnectionFactory: "
133: + con);
134: }
135:
136: if (isBatchMode()) {
137: if (batchCon == null) {
138: batchCon = new BatchConnection(con, broker);
139: }
140: return batchCon;
141: } else {
142: return con;
143: }
144: }
145:
146: /**
147: * Start transaction on the underlying connection.
148: */
149: public void localBegin() {
150: if (this .isInLocalTransaction) {
151: throw new TransactionInProgressException(
152: "Connection is already in transaction");
153: }
154: Connection connection = null;
155: try {
156: connection = this .getConnection();
157: } catch (LookupException e) {
158: /**
159: * must throw to notify user that we couldn't start a connection
160: */
161: throw new PersistenceBrokerException(
162: "Can't lookup a connection", e);
163: }
164: if (log.isDebugEnabled())
165: log.debug("localBegin was called for con " + connection);
166: // change autoCommit state only if we are not in a managed environment
167: // and it is enabled by user
168: if (!broker.isManaged()) {
169: if (jcd.getUseAutoCommit() == JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE) {
170: if (log.isDebugEnabled())
171: log
172: .debug("Try to change autoCommit state to 'false'");
173: platform.changeAutoCommitState(jcd, connection, false);
174: }
175: } else {
176: if (log.isDebugEnabled())
177: log
178: .debug("Found managed environment setting in PB, will skip Platform.changeAutoCommitState(...) call");
179: }
180: this .isInLocalTransaction = true;
181: }
182:
183: /**
184: * Call commit on the underlying connection.
185: */
186: public void localCommit() {
187: if (log.isDebugEnabled())
188: log.debug("commit was called");
189: if (!this .isInLocalTransaction) {
190: throw new TransactionNotInProgressException(
191: "Not in transaction, call begin() before commit()");
192: }
193: try {
194: if (!broker.isManaged()) {
195: if (batchCon != null) {
196: batchCon.commit();
197: } else if (con != null) {
198: con.commit();
199: }
200: } else {
201: if (log.isDebugEnabled())
202: log
203: .debug("Found managed environment setting in PB, will skip Connection.commit() call");
204: }
205: } catch (SQLException e) {
206: log
207: .error(
208: "Commit on underlying connection failed, try to rollback connection",
209: e);
210: this .localRollback();
211: throw new TransactionAbortedException(
212: "Commit on connection failed", e);
213: } finally {
214: this .isInLocalTransaction = false;
215: restoreAutoCommitState();
216: this .releaseConnection();
217: }
218: }
219:
220: /**
221: * Call rollback on the underlying connection.
222: */
223: public void localRollback() {
224: log
225: .info("Rollback was called, do rollback on current connection "
226: + con);
227: if (!this .isInLocalTransaction) {
228: throw new PersistenceBrokerException(
229: "Not in transaction, cannot abort");
230: }
231: try {
232: //truncate the local transaction
233: this .isInLocalTransaction = false;
234: if (!broker.isManaged()) {
235: if (batchCon != null) {
236: batchCon.rollback();
237: } else if (con != null && !con.isClosed()) {
238: con.rollback();
239: }
240: } else {
241: if (log.isEnabledFor(Logger.INFO))
242: log
243: .info("Found managed environment setting in PB, will ignore rollback call on connection, this should be done by JTA");
244: }
245: } catch (SQLException e) {
246: log
247: .error(
248: "Rollback on the underlying connection failed",
249: e);
250: } finally {
251: try {
252: restoreAutoCommitState();
253: } catch (OJBRuntimeException ignore) {
254: // Ignore or log exception
255: }
256: releaseConnection();
257: }
258: }
259:
260: /**
261: * Reset autoCommit state.
262: */
263: protected void restoreAutoCommitState() {
264: try {
265: if (!broker.isManaged()) {
266: if (jcd.getUseAutoCommit() == JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE
267: && originalAutoCommitState == true
268: && con != null && !con.isClosed()) {
269: platform.changeAutoCommitState(jcd, con, true);
270: }
271: } else {
272: if (log.isDebugEnabled())
273: log
274: .debug("Found managed environment setting in PB, will skip Platform.changeAutoCommitState(...) call");
275: }
276: } catch (SQLException e) {
277: // should never be reached
278: throw new OJBRuntimeException(
279: "Restore of connection autocommit state failed", e);
280: }
281: }
282:
283: /**
284: * Check if underlying connection was alive.
285: */
286: public boolean isAlive(Connection conn) {
287: try {
288: return con != null ? !con.isClosed() : false;
289: } catch (SQLException e) {
290: log
291: .error(
292: "IsAlive check failed, running connection was invalid!!",
293: e);
294: return false;
295: }
296: }
297:
298: public boolean isInLocalTransaction() {
299: return this .isInLocalTransaction;
300: }
301:
302: /**
303: * Release connection to the {@link org.apache.ojb.broker.accesslayer.ConnectionFactory}, make
304: * sure that you call the method in either case, it's the only way to free the connection.
305: */
306: public void releaseConnection() {
307: if (this .con == null) {
308: return;
309: }
310: if (isInLocalTransaction()) {
311: log
312: .error("Release connection: connection is in local transaction, missing 'localCommit' or"
313: + " 'localRollback' call - try to rollback the connection");
314: localRollback();
315: } else {
316: this .connectionFactory
317: .releaseConnection(this .jcd, this .con);
318: this .con = null;
319: this .batchCon = null;
320: }
321: }
322:
323: /**
324: * Returns the underlying used {@link org.apache.ojb.broker.accesslayer.ConnectionFactory}
325: * implementation.
326: */
327: public ConnectionFactory getUnderlyingConnectionFactory() {
328: return connectionFactory;
329: }
330:
331: /**
332: * Sets the batch mode on or off - this
333: * switch only works if you set attribute <code>batch-mode</code>
334: * in <code>jdbc-connection-descriptor</code> true and your database
335: * support batch mode.
336: *
337: * @param mode the batch mode
338: */
339: public void setBatchMode(boolean mode) {
340: /*
341: arminw:
342: if batch mode was set 'false' in repository,
343: never enable it.
344: There are many users having weird problems
345: when batch mode was enabled behind the scenes
346: */
347: batchMode = mode && jcd.getBatchMode();
348: }
349:
350: /**
351: * @return the batch mode.
352: */
353: public boolean isBatchMode() {
354: return batchMode && platform.supportsBatchOperations();
355: }
356:
357: /**
358: * Execute batch (if the batch mode where used).
359: */
360: public void executeBatch() throws OJBBatchUpdateException {
361: if (batchCon != null) {
362: try {
363: batchCon.executeBatch();
364: } catch (Throwable th) {
365: throw new OJBBatchUpdateException(th);
366: }
367: }
368: }
369:
370: /**
371: * Execute batch if the number of statements in it
372: * exceeded the limit (if the batch mode where used).
373: */
374: public void executeBatchIfNecessary()
375: throws OJBBatchUpdateException {
376: if (batchCon != null) {
377: try {
378: batchCon.executeBatchIfNecessary();
379: } catch (Throwable th) {
380: throw new OJBBatchUpdateException(th);
381: }
382: }
383: }
384:
385: /**
386: * Clear batch (if the batch mode where used).
387: */
388: public void clearBatch() {
389: if (batchCon != null) {
390: batchCon.clearBatch();
391: }
392: }
393: }
|