Coverage Report - org.apache.ojb.broker.accesslayer.ConnectionManagerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ConnectionManagerImpl
N/A
N/A
4.706
 
 1  
 package org.apache.ojb.broker.accesslayer;
 2  
 
 3  
 /* Copyright 2003-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import java.sql.Connection;
 19  
 import java.sql.SQLException;
 20  
 
 21  
 import org.apache.ojb.broker.OJBRuntimeException;
 22  
 import org.apache.ojb.broker.PBKey;
 23  
 import org.apache.ojb.broker.PersistenceBroker;
 24  
 import org.apache.ojb.broker.PersistenceBrokerException;
 25  
 import org.apache.ojb.broker.TransactionAbortedException;
 26  
 import org.apache.ojb.broker.TransactionInProgressException;
 27  
 import org.apache.ojb.broker.TransactionNotInProgressException;
 28  
 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
 29  
 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
 30  
 import org.apache.ojb.broker.metadata.MetadataManager;
 31  
 import org.apache.ojb.broker.platforms.Platform;
 32  
 import org.apache.ojb.broker.platforms.PlatformFactory;
 33  
 import org.apache.ojb.broker.util.batch.BatchConnection;
 34  
 import org.apache.ojb.broker.util.logging.Logger;
 35  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 36  
 
 37  
 /**
 38  
  * Manages Connection ressources.
 39  
  *
 40  
  * @see ConnectionManagerIF
 41  
  * @author Thomas Mahler
 42  
  * @version $Id: ConnectionManagerImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
 43  
  */
 44  
 public class ConnectionManagerImpl implements ConnectionManagerIF
 45  
 {
 46  
     private Logger log = LoggerFactory.getLogger(ConnectionManagerImpl.class);
 47  
 
 48  
     private PersistenceBrokerImpl broker = null;
 49  
     private ConnectionFactory connectionFactory;
 50  
     private JdbcConnectionDescriptor jcd;
 51  
     private Platform platform;
 52  
     private Connection con = null;
 53  
     private PBKey pbKey;
 54  
     private boolean originalAutoCommitState;
 55  
     private boolean isInLocalTransaction;
 56  
     private boolean batchMode;
 57  
     private BatchConnection batchCon = null;
 58  
 
 59  
     public ConnectionManagerImpl(PersistenceBroker broker)
 60  
     {
 61  
         // TODO: avoid this cast
 62  
         this.broker = (PersistenceBrokerImpl) broker;
 63  
         this.pbKey = broker.getPBKey();
 64  
         this.jcd = MetadataManager.getInstance().connectionRepository().getDescriptor(pbKey);
 65  
         this.connectionFactory = ConnectionFactoryFactory.getInstance().createConnectionFactory();
 66  
         this.platform = PlatformFactory.getPlatformFor(jcd);
 67  
         /*
 68  
         by default batch mode is not enabled and after use of a PB
 69  
         instance, before instance was returned to pool, batch mode
 70  
         was set to false again (PB implementation close method)
 71  
         Be carefully in modify this behaviour, changes could cause
 72  
         unexpected behaviour
 73  
         */
 74  
         setBatchMode(false);
 75  
     }
 76  
 
 77  
     /**
 78  
      * Returns the associated {@link org.apache.ojb.broker.metadata.JdbcConnectionDescriptor}
 79  
      */
 80  
     public JdbcConnectionDescriptor getConnectionDescriptor()
 81  
     {
 82  
         return jcd;
 83  
     }
 84  
 
 85  
     public Platform getSupportedPlatform()
 86  
     {
 87  
         return this.platform;
 88  
     }
 89  
 
 90  
     /**
 91  
      * Returns the underlying connection, requested from
 92  
      * {@link org.apache.ojb.broker.accesslayer.ConnectionFactory}.
 93  
      * <p>
 94  
      * PB#beginTransaction() opens a single jdbc connection via
 95  
          * PB#serviceConnectionManager().localBegin().
 96  
          * If you call PB#serviceConnectionManager().getConnection() later
 97  
          * it returns the already opened connection.
 98  
          * The PB instance will release the used connection during
 99  
          * PB#commitTransaction() or PB#abortTransaction() or PB#close().
 100  
      * </p>
 101  
      * <p>
 102  
      * NOTE: Never call Connection.close() on the connection requested from the ConnectionManager.
 103  
      * Cleanup of used connection is done by OJB itself. If you need to release a used connection
 104  
      * call {@link #releaseConnection()}.
 105  
      * </p>
 106  
      */
 107  
     public Connection getConnection() throws LookupException
 108  
     {
 109  
         /*
 110  
         if the connection is not null and we are not in a local tx, we check
 111  
         the connection state and release "dead" connections.
 112  
         if connection is in local tx we do nothing, the dead connection will cause
 113  
         an exception and PB instance have to handle rollback
 114  
         */
 115  
         if(con != null && !isInLocalTransaction() && !isAlive(con))
 116  
         {
 117  
             releaseConnection();
 118  
         }
 119  
         if (con == null)
 120  
         {
 121  
             con = this.connectionFactory.lookupConnection(jcd);
 122  
             if (con == null) throw new PersistenceBrokerException("Cannot get connection for " + jcd);
 123  
             if (jcd.getUseAutoCommit() == JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE)
 124  
             {
 125  
                 try
 126  
                 {
 127  
                     this.originalAutoCommitState = con.getAutoCommit();
 128  
                 }
 129  
                 catch (SQLException e)
 130  
                 {
 131  
                     throw new PersistenceBrokerException("Cannot request autoCommit state on the connection", e);
 132  
                 }
 133  
             }
 134  
             if (log.isDebugEnabled()) log.debug("Request new connection from ConnectionFactory: " + con);
 135  
         }
 136  
 
 137  
         if (isBatchMode())
 138  
         {
 139  
             if (batchCon == null)
 140  
             {
 141  
                 batchCon = new BatchConnection(con, broker);
 142  
             }
 143  
             return batchCon;
 144  
         }
 145  
         else
 146  
         {
 147  
             return con;
 148  
         }
 149  
     }
 150  
 
 151  
     /**
 152  
      * Start transaction on the underlying connection.
 153  
      */
 154  
     public void localBegin()
 155  
     {
 156  
         if (this.isInLocalTransaction)
 157  
         {
 158  
             throw new TransactionInProgressException("Connection is already in transaction");
 159  
         }
 160  
         Connection connection = null;
 161  
         try
 162  
         {
 163  
             connection = this.getConnection();
 164  
         }
 165  
         catch (LookupException e)
 166  
         {
 167  
             /**
 168  
              * must throw to notify user that we couldn't start a connection
 169  
              */
 170  
             throw new PersistenceBrokerException("Can't lookup a connection", e);
 171  
         }
 172  
         if (log.isDebugEnabled()) log.debug("localBegin was called for con " + connection);
 173  
         // change autoCommit state only if we are not in a managed environment
 174  
         // and it is enabled by user
 175  
         if(!broker.isManaged())
 176  
         {
 177  
             if (jcd.getUseAutoCommit() == JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE)
 178  
             {
 179  
                 if (log.isDebugEnabled()) log.debug("Try to change autoCommit state to 'false'");
 180  
                 platform.changeAutoCommitState(jcd, connection, false);
 181  
             }
 182  
         }
 183  
         else
 184  
         {
 185  
             if(log.isDebugEnabled()) log.debug(
 186  
                         "Found managed environment setting in PB, will skip Platform.changeAutoCommitState(...) call");
 187  
         }
 188  
         this.isInLocalTransaction = true;
 189  
     }
 190  
 
 191  
     /**
 192  
      * Call commit on the underlying connection.
 193  
      */
 194  
     public void localCommit()
 195  
     {
 196  
         if (log.isDebugEnabled()) log.debug("commit was called");
 197  
         if (!this.isInLocalTransaction)
 198  
         {
 199  
             throw new TransactionNotInProgressException("Not in transaction, call begin() before commit()");
 200  
         }
 201  
         try
 202  
         {
 203  
             if(!broker.isManaged())
 204  
             {
 205  
                 if (batchCon != null)
 206  
                 {
 207  
                     batchCon.commit();
 208  
                 }
 209  
                 else if (con != null)
 210  
                 {
 211  
                     con.commit();
 212  
                 }
 213  
             }
 214  
             else
 215  
             {
 216  
                 if(log.isDebugEnabled()) log.debug(
 217  
                         "Found managed environment setting in PB, will skip Connection.commit() call");
 218  
             }
 219  
         }
 220  
         catch (SQLException e)
 221  
         {
 222  
             log.error("Commit on underlying connection failed, try to rollback connection", e);
 223  
             this.localRollback();
 224  
             throw new TransactionAbortedException("Commit on connection failed", e);
 225  
         }
 226  
         finally
 227  
         {
 228  
             this.isInLocalTransaction = false;
 229  
             restoreAutoCommitState();
 230  
             this.releaseConnection();
 231  
         }
 232  
     }
 233  
 
 234  
     /**
 235  
      * Call rollback on the underlying connection.
 236  
      */
 237  
     public void localRollback()
 238  
     {
 239  
         log.info("Rollback was called, do rollback on current connection " + con);
 240  
         if (!this.isInLocalTransaction)
 241  
         {
 242  
             throw new PersistenceBrokerException("Not in transaction, cannot abort");
 243  
         }
 244  
         try
 245  
         {
 246  
             //truncate the local transaction
 247  
             this.isInLocalTransaction = false;
 248  
             if(!broker.isManaged())
 249  
             {
 250  
                 if (batchCon != null)
 251  
                 {
 252  
                     batchCon.rollback();
 253  
                 }
 254  
                 else if (con != null && !con.isClosed())
 255  
                 {
 256  
                     con.rollback();
 257  
                 }
 258  
             }
 259  
             else
 260  
             {
 261  
                 if(log.isEnabledFor(Logger.INFO)) log.info(
 262  
                         "Found managed environment setting in PB, will ignore rollback call on connection, this should be done by JTA");
 263  
             }
 264  
         }
 265  
         catch (SQLException e)
 266  
         {
 267  
             log.error("Rollback on the underlying connection failed", e);
 268  
         }
 269  
         finally
 270  
         {
 271  
             try
 272  
             {
 273  
                     restoreAutoCommitState();
 274  
                     }
 275  
             catch(OJBRuntimeException ignore)
 276  
             {
 277  
                             // Ignore or log exception
 278  
                     }
 279  
             releaseConnection();
 280  
         }
 281  
     }
 282  
 
 283  
     /**
 284  
      * Reset autoCommit state.
 285  
      */
 286  
     protected void restoreAutoCommitState()
 287  
     {
 288  
         try
 289  
         {
 290  
             if(!broker.isManaged())
 291  
             {
 292  
                 if (jcd.getUseAutoCommit() == JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE
 293  
                         && originalAutoCommitState == true && con != null && !con.isClosed())
 294  
                 {
 295  
                     platform.changeAutoCommitState(jcd, con, true);
 296  
                 }
 297  
             }
 298  
             else
 299  
             {
 300  
                 if(log.isDebugEnabled()) log.debug(
 301  
                         "Found managed environment setting in PB, will skip Platform.changeAutoCommitState(...) call");
 302  
             }
 303  
         }
 304  
         catch (SQLException e)
 305  
         {
 306  
             // should never be reached
 307  
             throw new OJBRuntimeException("Restore of connection autocommit state failed", e);
 308  
         }
 309  
     }
 310  
 
 311  
     /**
 312  
      * Check if underlying connection was alive.
 313  
      */
 314  
     public boolean isAlive(Connection conn)
 315  
     {
 316  
         try
 317  
         {
 318  
             return con != null ? !con.isClosed() : false;
 319  
         }
 320  
         catch (SQLException e)
 321  
         {
 322  
             log.error("IsAlive check failed, running connection was invalid!!", e);
 323  
             return false;
 324  
         }
 325  
     }
 326  
 
 327  
     public boolean isInLocalTransaction()
 328  
     {
 329  
         return this.isInLocalTransaction;
 330  
     }
 331  
 
 332  
     /**
 333  
      * Release connection to the {@link org.apache.ojb.broker.accesslayer.ConnectionFactory}, make
 334  
      * sure that you call the method in either case, it's the only way to free the connection.
 335  
      */
 336  
     public void releaseConnection()
 337  
     {
 338  
         if (this.con == null)
 339  
         {
 340  
             return;
 341  
         }
 342  
         if(isInLocalTransaction())
 343  
         {
 344  
             log.error("Release connection: connection is in local transaction, missing 'localCommit' or" +
 345  
                     " 'localRollback' call - try to rollback the connection");
 346  
             localRollback();
 347  
         }
 348  
         else
 349  
         {
 350  
             this.connectionFactory.releaseConnection(this.jcd, this.con);
 351  
             this.con = null;
 352  
             this.batchCon = null;
 353  
         }
 354  
     }
 355  
 
 356  
     /**
 357  
      * Returns the underlying used {@link org.apache.ojb.broker.accesslayer.ConnectionFactory}
 358  
      * implementation.
 359  
      */
 360  
     public ConnectionFactory getUnderlyingConnectionFactory()
 361  
     {
 362  
         return connectionFactory;
 363  
     }
 364  
 
 365  
     /**
 366  
      * Sets the batch mode on or off - this
 367  
      * switch only works if you set attribute <code>batch-mode</code>
 368  
      * in <code>jdbc-connection-descriptor</code> true and your database
 369  
      * support batch mode.
 370  
      *
 371  
      * @param mode the batch mode
 372  
      */
 373  
     public void setBatchMode(boolean mode)
 374  
     {
 375  
         /*
 376  
         arminw:
 377  
         if batch mode was set 'false' in repository,
 378  
         never enable it.
 379  
         There are many users having weird problems
 380  
         when batch mode was enabled behind the scenes
 381  
         */
 382  
         batchMode = mode && jcd.getBatchMode();
 383  
     }
 384  
 
 385  
     /**
 386  
      * @return the batch mode.
 387  
      */
 388  
     public boolean isBatchMode()
 389  
     {
 390  
         return batchMode && platform.supportsBatchOperations();
 391  
     }
 392  
 
 393  
     /**
 394  
      * Execute batch (if the batch mode where used).
 395  
      */
 396  
     public void executeBatch() throws OJBBatchUpdateException
 397  
     {
 398  
         if (batchCon != null)
 399  
         {
 400  
             try
 401  
             {
 402  
                 batchCon.executeBatch();
 403  
             }
 404  
             catch (Throwable th)
 405  
             {
 406  
                 throw new OJBBatchUpdateException(th);
 407  
             }
 408  
         }
 409  
     }
 410  
 
 411  
     /**
 412  
      * Execute batch if the number of statements in it
 413  
      * exceeded the limit (if the batch mode where used).
 414  
      */
 415  
     public void executeBatchIfNecessary() throws OJBBatchUpdateException
 416  
     {
 417  
         if (batchCon != null)
 418  
         {
 419  
             try
 420  
             {
 421  
                 batchCon.executeBatchIfNecessary();
 422  
             }
 423  
             catch (Throwable th)
 424  
             {
 425  
                 throw new OJBBatchUpdateException(th);
 426  
             }
 427  
         }
 428  
     }
 429  
 
 430  
     /**
 431  
      * Clear batch (if the batch mode where used).
 432  
      */
 433  
     public void clearBatch()
 434  
     {
 435  
         if (batchCon != null)
 436  
         {
 437  
             batchCon.clearBatch();
 438  
         }
 439  
     }
 440  
 }