Coverage Report - org.apache.ojb.broker.accesslayer.ConnectionFactoryPooledImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ConnectionFactoryPooledImpl
N/A
N/A
4.333
ConnectionFactoryPooledImpl$ConPoolFactory
N/A
N/A
4.333
 
 1  
 package org.apache.ojb.broker.accesslayer;
 2  
 
 3  
 /* Copyright 2002-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 org.apache.commons.pool.BasePoolableObjectFactory;
 19  
 import org.apache.commons.pool.ObjectPool;
 20  
 import org.apache.commons.pool.PoolableObjectFactory;
 21  
 import org.apache.commons.pool.impl.GenericObjectPool;
 22  
 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
 23  
 import org.apache.ojb.broker.util.logging.Logger;
 24  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 25  
 import org.apache.ojb.broker.OJBRuntimeException;
 26  
 
 27  
 import java.sql.Connection;
 28  
 import java.sql.ResultSet;
 29  
 import java.sql.SQLException;
 30  
 import java.sql.PreparedStatement;
 31  
 import java.util.Collection;
 32  
 import java.util.HashMap;
 33  
 import java.util.Iterator;
 34  
 import java.util.Map;
 35  
 import java.util.NoSuchElementException;
 36  
 
 37  
 /**
 38  
  * Connection factory which pools the requested
 39  
  * connections for different JdbcConnectionDescriptors
 40  
  * using Commons Pool API.
 41  
  *
 42  
  * @version $Id: ConnectionFactoryPooledImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
 43  
  * @see <a href="http://jakarta.apache.org/commons/pool/">Commons Pool Website</a>
 44  
  */
 45  
 public class ConnectionFactoryPooledImpl extends ConnectionFactoryAbstractImpl
 46  
 {
 47  
 
 48  
     private Logger log = LoggerFactory.getLogger(ConnectionFactoryPooledImpl.class);
 49  
     /** Key=PBKey, value=ObjectPool. */
 50  
     private Map poolMap = new HashMap();
 51  
     /** Synchronize object for operations not synchronized on Map only. */
 52  
     private final Object poolSynch = new Object();
 53  
 
 54  
     public void releaseJdbcConnection(JdbcConnectionDescriptor jcd, Connection con)
 55  
             throws LookupException
 56  
     {
 57  
         final ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey());
 58  
         try
 59  
         {
 60  
             /* mkalen: NB - according to the Commons Pool API we should _not_ perform
 61  
              * any additional checks here since we will then break testOnX semantics
 62  
              *
 63  
              * To enable Connection validation on releaseJdbcConnection,
 64  
              * set a validation query and specify testOnRelease=true
 65  
              *
 66  
              * Destruction of pooled objects is performed by the actual Commons Pool
 67  
              * ObjectPool implementation when the object factory's validateObject method
 68  
              * returns false. See ConPoolFactory#validateObject.
 69  
              */
 70  
             op.returnObject(con);
 71  
         }
 72  
         catch (Exception e)
 73  
         {
 74  
             throw new LookupException(e);
 75  
         }
 76  
     }
 77  
 
 78  
     public Connection checkOutJdbcConnection(JdbcConnectionDescriptor jcd) throws LookupException
 79  
     {
 80  
         ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey());
 81  
         if (op == null)
 82  
         {
 83  
             synchronized (poolSynch)
 84  
             {
 85  
                 log.info("Create new connection pool:" + jcd);
 86  
                 op = createConnectionPool(jcd);
 87  
                 poolMap.put(jcd.getPBKey(), op);
 88  
             }
 89  
         }
 90  
         final Connection conn;
 91  
         try
 92  
         {
 93  
             conn = (Connection) op.borrowObject();
 94  
         }
 95  
         catch (NoSuchElementException e)
 96  
         {
 97  
             int active = 0;
 98  
             int idle = 0;
 99  
             try
 100  
             {
 101  
                 active = op.getNumActive();
 102  
                 idle = op.getNumIdle();
 103  
             }
 104  
             catch(Exception ignore){}
 105  
             throw new LookupException("Could not borrow connection from pool, seems ObjectPool is exhausted." +
 106  
                     " Active/Idle instances in pool=" + active + "/" +  idle
 107  
                     + ". "+ JdbcConnectionDescriptor.class.getName() + ":  " + jcd, e);
 108  
         }
 109  
         catch (Exception e)
 110  
         {
 111  
             int active = 0;
 112  
             int idle = 0;
 113  
             try
 114  
             {
 115  
                 active = op.getNumActive();
 116  
                 idle = op.getNumIdle();
 117  
             }
 118  
             catch(Exception ignore){}
 119  
             throw new LookupException("Could not borrow connection from pool." +
 120  
                     " Active/Idle instances in pool=" + active + "/" +  idle
 121  
                     + ". "+ JdbcConnectionDescriptor.class.getName() + ":  " + jcd, e);
 122  
         }
 123  
         return conn;
 124  
     }
 125  
 
 126  
     /**
 127  
      * Create the pool for pooling the connections of the given connection descriptor.
 128  
      * Override this method to implement your on {@link org.apache.commons.pool.ObjectPool}.
 129  
      */
 130  
     public ObjectPool createConnectionPool(JdbcConnectionDescriptor jcd)
 131  
     {
 132  
         if (log.isDebugEnabled()) log.debug("createPool was called");
 133  
         PoolableObjectFactory pof = new ConPoolFactory(this, jcd);
 134  
         GenericObjectPool.Config conf = jcd.getConnectionPoolDescriptor().getObjectPoolConfig();
 135  
         return (ObjectPool)new GenericObjectPool(pof, conf);
 136  
     }
 137  
 
 138  
     /**
 139  
      * Closes all managed pools.
 140  
      */
 141  
     public void releaseAllResources()
 142  
     {
 143  
         synchronized (poolSynch)
 144  
         {
 145  
             Collection pools = poolMap.values();
 146  
             poolMap = new HashMap(poolMap.size());
 147  
             ObjectPool op = null;
 148  
             for (Iterator iterator = pools.iterator(); iterator.hasNext();)
 149  
             {
 150  
                 try
 151  
                 {
 152  
                     op = ((ObjectPool) iterator.next());
 153  
                     op.close();
 154  
                 }
 155  
                 catch (Exception e)
 156  
                 {
 157  
                     log.error("Exception occured while closing pool " + op, e);
 158  
                 }
 159  
             }
 160  
         }
 161  
         super.releaseAllResources();
 162  
     }
 163  
 
 164  
     //**************************************************************************************
 165  
     // Inner classes
 166  
     //************************************************************************************
 167  
 
 168  
     /**
 169  
      * Inner class - {@link org.apache.commons.pool.PoolableObjectFactory}
 170  
      * used as factory for connection pooling.
 171  
      */
 172  
     class ConPoolFactory extends BasePoolableObjectFactory
 173  
     {
 174  
         final private JdbcConnectionDescriptor jcd;
 175  
         final private ConnectionFactoryPooledImpl cf;
 176  
         private int failedValidationQuery;
 177  
 
 178  
         public ConPoolFactory(ConnectionFactoryPooledImpl cf, JdbcConnectionDescriptor jcd)
 179  
         {
 180  
             this.cf = cf;
 181  
             this.jcd = jcd;
 182  
         }
 183  
 
 184  
         public boolean validateObject(Object obj)
 185  
         {
 186  
             boolean isValid = false;
 187  
             if (obj != null)
 188  
             {
 189  
                 final Connection con = (Connection) obj;
 190  
                 try
 191  
                 {
 192  
                     isValid = !con.isClosed();
 193  
                 }
 194  
                 catch (SQLException e)
 195  
                 {
 196  
                     log.warn("Connection validation failed: " + e.getMessage());
 197  
                     if (log.isDebugEnabled()) log.debug(e);
 198  
                     isValid = false;
 199  
                 }
 200  
                 if (isValid)
 201  
                 {
 202  
                     final String validationQuery;
 203  
                     validationQuery = jcd.getConnectionPoolDescriptor().getValidationQuery();
 204  
                     if (validationQuery != null)
 205  
                     {
 206  
                         isValid = validateConnection(con, validationQuery);
 207  
                     }
 208  
                 }
 209  
             }
 210  
             return isValid;
 211  
         }
 212  
 
 213  
         private boolean validateConnection(Connection conn, String query)
 214  
         {
 215  
             PreparedStatement stmt = null;
 216  
             ResultSet rset = null;
 217  
             boolean isValid = false;
 218  
             if (failedValidationQuery > 100)
 219  
             {
 220  
                 --failedValidationQuery;
 221  
                 throw new OJBRuntimeException("Validation of connection "+conn+" using validation query '"+
 222  
                         query + "' failed more than 100 times.");
 223  
             }
 224  
             try
 225  
             {
 226  
                 stmt = conn.prepareStatement(query);
 227  
                 stmt.setMaxRows(1);
 228  
                 stmt.setFetchSize(1);
 229  
                 rset = stmt.executeQuery();
 230  
                 if (rset.next())
 231  
                 {
 232  
                     failedValidationQuery = 0;
 233  
                     isValid = true;
 234  
                 }
 235  
                 else
 236  
                 {
 237  
                     ++failedValidationQuery;
 238  
                     log.warn("Validation query '" + query +
 239  
                             "' result set does not match, discard connection");
 240  
                     isValid = false;
 241  
                 }
 242  
             }
 243  
             catch (SQLException e)
 244  
             {
 245  
                 ++failedValidationQuery;
 246  
                 log.warn("Validation query for connection failed, discard connection. Query was '" +
 247  
                         query + "', Message was " + e.getMessage());
 248  
                 if (log.isDebugEnabled()) log.debug(e);
 249  
             }
 250  
             finally
 251  
             {
 252  
                 try
 253  
                 {
 254  
                     if(rset != null) rset.close();
 255  
                 }
 256  
                 catch (SQLException t)
 257  
                 {
 258  
                     if (log.isDebugEnabled()) log.debug("ResultSet already closed.", t);
 259  
                 }
 260  
                 try
 261  
                 {
 262  
                     if(stmt != null) stmt.close();
 263  
                 }
 264  
                 catch (SQLException t)
 265  
                 {
 266  
                     if (log.isDebugEnabled()) log.debug("Statement already closed.", t);
 267  
                 }
 268  
             }
 269  
             return isValid;
 270  
         }
 271  
 
 272  
         public Object makeObject() throws Exception
 273  
         {
 274  
             if (log.isDebugEnabled()) log.debug("makeObject called");
 275  
             return cf.newConnectionFromDriverManager(jcd);
 276  
         }
 277  
 
 278  
         public void destroyObject(Object obj)
 279  
                 throws Exception
 280  
         {
 281  
             log.info("Destroy object was called, try to close connection: " + obj);
 282  
             try
 283  
             {
 284  
                 ((Connection) obj).close();
 285  
             }
 286  
             catch (SQLException ignore)
 287  
             {
 288  
                 //ignore it
 289  
             }
 290  
         }
 291  
     }
 292  
 
 293  
 }