Coverage report

  %line %branch
org.apache.torque.pool.ConnectionPool$Monitor
0% 
0% 

 1  
 package org.apache.torque.pool;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2004 The Apache Software Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License")
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 import java.sql.SQLException;
 20  
 import java.util.HashMap;
 21  
 import java.util.Map;
 22  
 import java.util.Stack;
 23  
 
 24  
 import javax.sql.ConnectionEvent;
 25  
 import javax.sql.ConnectionEventListener;
 26  
 import javax.sql.ConnectionPoolDataSource;
 27  
 import javax.sql.PooledConnection;
 28  
 
 29  
 import org.apache.commons.logging.Log;
 30  
 import org.apache.commons.logging.LogFactory;
 31  
 
 32  
 /**
 33  
  * This class implements a simple connection pooling scheme.
 34  
  *
 35  
  * @author <a href="mailto:csterg@aias.gr">Costas Stergiou</a>
 36  
  * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
 37  
  * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
 38  
  * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
 39  
  * @author <a href="mailto:dlr@collab.net">Daniel L. Rall</a>
 40  
  * @author <a href="mailto:paul@evolventtech.com">Paul O'Leary</a>
 41  
  * @author <a href="mailto:magnus@handtolvur.is">Magn�s ��r Torfason</a>
 42  
  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
 43  
  * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
 44  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 45  
  * @version $Id: ConnectionPool.java,v 1.27.2.2 2004/05/20 04:36:05 seade Exp $
 46  
  * @deprecated as of version 3.1
 47  
  */
 48  
 class ConnectionPool implements ConnectionEventListener
 49  
 {
 50  
 
 51  
     /** Default maximum Number of connections from this pool: One */
 52  
     public static final int DEFAULT_MAX_CONNECTIONS = 1;
 53  
 
 54  
     /** Default Expiry Time for a pool: 1 hour */
 55  
     public static final int DEFAULT_EXPIRY_TIME = 60 * 60 * 1000;
 56  
 
 57  
     /** Default Connect Wait Timeout: 10 Seconds */
 58  
     public static final int DEFAULT_CONNECTION_WAIT_TIMEOUT = 10 * 1000;
 59  
 
 60  
     /** Pool containing database connections. */
 61  
     private Stack pool;
 62  
 
 63  
     /** The url for this pool. */
 64  
     private String url;
 65  
 
 66  
     /** The user name for this pool. */
 67  
     private String username;
 68  
 
 69  
     /** The password for this pool. */
 70  
     private String password;
 71  
 
 72  
     /** The current number of database connections that have been created. */
 73  
     private int totalConnections;
 74  
 
 75  
     /** The maximum number of database connections that can be created. */
 76  
     private int maxConnections = DEFAULT_MAX_CONNECTIONS;
 77  
 
 78  
     /** The amount of time in milliseconds that a connection will be pooled. */
 79  
     private long expiryTime = DEFAULT_EXPIRY_TIME;
 80  
 
 81  
     /**
 82  
      * Counter that keeps track of the number of threads that are in
 83  
      * the wait state, waiting to aquire a connection.
 84  
      */
 85  
     private int waitCount = 0;
 86  
 
 87  
     /** The logging logger. */
 88  
     private static Log log = LogFactory.getLog(ConnectionPool.class);
 89  
 
 90  
     /** Interval (in seconds) that the monitor thread reports the pool state */
 91  
     private int logInterval = 0;
 92  
 
 93  
     /** Monitor thread reporting the pool state */
 94  
     private Monitor monitor;
 95  
 
 96  
     /**
 97  
      * Amount of time a thread asking the pool for a cached connection will
 98  
      * wait before timing out and throwing an error.
 99  
      */
 100  
     private long connectionWaitTimeout = DEFAULT_CONNECTION_WAIT_TIMEOUT;
 101  
 
 102  
     /** The ConnectionPoolDataSource  */
 103  
     private ConnectionPoolDataSource cpds;
 104  
 
 105  
     /**
 106  
      * Keep track of when connections were created.  Keyed by a
 107  
      * PooledConnection and value is a java.util.Date
 108  
      */
 109  
     private Map timeStamps;
 110  
 
 111  
     /**
 112  
      * Creates a <code>ConnectionPool</code> with the default
 113  
      * attributes.
 114  
      *
 115  
      * @param cpds The datasource
 116  
      * @param username The user name for this pool.
 117  
      * @param password The password for this pool.
 118  
      * @param maxConnections max number of connections
 119  
      * @param expiryTime connection expiry time
 120  
      * @param connectionWaitTimeout timeout
 121  
      * @param logInterval log interval
 122  
      */
 123  
     ConnectionPool(ConnectionPoolDataSource cpds, String username,
 124  
                    String password, int maxConnections, class="keyword">int expiryTime,
 125  
                    int connectionWaitTimeout, class="keyword">int logInterval)
 126  
     {
 127  
         totalConnections = 0;
 128  
         pool = new Stack();
 129  
         timeStamps = new HashMap();
 130  
 
 131  
         this.cpds = cpds;
 132  
         this.username = username;
 133  
         this.password = password;
 134  
 
 135  
         this.maxConnections =
 136  
             (maxConnections > 0) ? maxConnections : DEFAULT_MAX_CONNECTIONS;
 137  
 
 138  
         this.expiryTime =
 139  
             ((expiryTime > 0) ? expiryTime * 1000 : DEFAULT_EXPIRY_TIME);
 140  
 
 141  
         this.connectionWaitTimeout =
 142  
             ((connectionWaitTimeout > 0)
 143  
              ? connectionWaitTimeout * 1000
 144  
              : DEFAULT_CONNECTION_WAIT_TIMEOUT);
 145  
 
 146  
         this.logInterval = 1000 * logInterval;
 147  
 
 148  
         if (logInterval > 0)
 149  
         {
 150  
             log.debug("Starting Pool Monitor Thread with Log Interval "
 151  
                            + logInterval + " Milliseconds");
 152  
 
 153  
             // Create monitor thread
 154  
             monitor = new Monitor();
 155  
 
 156  
             // Indicate that this is a system thread. JVM will quit only
 157  
             // when there are no more active user threads. Settings threads
 158  
             // spawned internally by Torque as daemons allows commandline
 159  
             // applications using Torque to terminate in an orderly manner.
 160  
             monitor.setDaemon(true);
 161  
             monitor.start();
 162  
         }
 163  
     }
 164  
 
 165  
     /**
 166  
      * Returns a connection that maintains a link to the pool it came from.
 167  
      *
 168  
      * @param username The name of the database user.
 169  
      * @param password The password of the database user.
 170  
      * @return         A database connection.
 171  
      * @exception SQLException if there is aproblem with the db connection
 172  
      */
 173  
     final synchronized PooledConnection getConnection(String username,
 174  
             String password)
 175  
             throws SQLException
 176  
     {
 177  
         if (username != this.username || password != class="keyword">this.password)
 178  
         {
 179  
             throw new SQLException("Username and password are invalid.");
 180  
         }
 181  
 
 182  
         PooledConnection pcon = null;
 183  
         if (pool.empty() && totalConnections < maxConnections)
 184  
         {
 185  
             pcon = getNewConnection();
 186  
         }
 187  
         else
 188  
         {
 189  
             try
 190  
             {
 191  
                 pcon = getInternalPooledConnection();
 192  
             }
 193  
             catch (Exception e)
 194  
             {
 195  
                 throw new SQLException(e.getMessage());
 196  
             }
 197  
         }
 198  
         return pcon;
 199  
     }
 200  
 
 201  
     /**
 202  
      * Returns a fresh connection to the database.  The database type
 203  
      * is specified by <code>driver</code>, and its connection
 204  
      * information by <code>url</code>, <code>username</code>, and
 205  
      * <code>password</code>.
 206  
      *
 207  
      * @return A database connection.
 208  
      * @exception SQLException if there is aproblem with the db connection
 209  
      */
 210  
     private PooledConnection getNewConnection()
 211  
         throws SQLException
 212  
     {
 213  
         PooledConnection pc = null;
 214  
         if (username == null)
 215  
         {
 216  
             pc = cpds.getPooledConnection();
 217  
         }
 218  
         else
 219  
         {
 220  
             pc = cpds.getPooledConnection(username, password);
 221  
         }
 222  
         pc.addConnectionEventListener(this);
 223  
 
 224  
         // Age some connections so that there will not be a run on the db,
 225  
         // when connections start expiring
 226  
         //
 227  
         // I did some experimentation here with integers but as this
 228  
         // is not a really time critical path, we keep the floating
 229  
         // point calculation.
 230  
         long currentTime = System.currentTimeMillis();
 231  
 
 232  
         double ratio = new Long(maxConnections - totalConnections).class="keyword">doubleValue()
 233  
             / maxConnections;
 234  
 
 235  
         long ratioTime = new Double(currentTime - (expiryTime * ratio) / 4)
 236  
             .longValue();
 237  
 
 238  
         ratioTime = (expiryTime < 0) ? currentTime : ratioTime;
 239  
 
 240  
         timeStamps.put(pc, new Long(ratioTime));
 241  
         totalConnections++;
 242  
         return pc;
 243  
     }
 244  
 
 245  
     /**
 246  
      * Gets a pooled database connection.
 247  
      *
 248  
      * @return A database connection.
 249  
      * @exception ConnectionWaitTimeoutException Wait time exceeded.
 250  
      * @exception Exception No pooled connections.
 251  
      */
 252  
     private synchronized PooledConnection getInternalPooledConnection()
 253  
         throws ConnectionWaitTimeoutException, Exception
 254  
     {
 255  
         // We test waitCount > 0 to make sure no other threads are
 256  
         // waiting for a connection.
 257  
         if (waitCount > 0 || pool.empty())
 258  
         {
 259  
             // The connection pool is empty and we cannot allocate any new
 260  
             // connections.  Wait the prescribed amount of time and see if
 261  
             // a connection is returned.
 262  
             try
 263  
             {
 264  
                 waitCount++;
 265  
                 wait(connectionWaitTimeout);
 266  
             }
 267  
             catch (InterruptedException ignored)
 268  
             {
 269  
                 // Don't care how we come out of the wait state.
 270  
             }
 271  
             finally
 272  
             {
 273  
                 waitCount--;
 274  
             }
 275  
 
 276  
             // Check for a returned connection.
 277  
             if (pool.empty())
 278  
             {
 279  
                 // If the pool is still empty here, we were not awoken by
 280  
                 // someone returning a connection.
 281  
                 throw new ConnectionWaitTimeoutException(url);
 282  
             }
 283  
         }
 284  
         return popConnection();
 285  
     }
 286  
     /**
 287  
      * Helper function that attempts to pop a connection off the pool's stack,
 288  
      * handling the case where the popped connection has become invalid by
 289  
      * creating a new connection.
 290  
      *
 291  
      * @return An existing or new database connection.
 292  
      * @throws Exception if the pool is empty
 293  
      */
 294  
     private PooledConnection popConnection()
 295  
         throws Exception
 296  
     {
 297  
         while (!pool.empty())
 298  
         {
 299  
             PooledConnection con = (PooledConnection) pool.pop();
 300  
 
 301  
             // It's really not safe to assume this connection is
 302  
             // valid even though it's checked before being pooled.
 303  
             if (isValid(con))
 304  
             {
 305  
                 return con;
 306  
             }
 307  
             else
 308  
             {
 309  
                 // Close invalid connection.
 310  
                 con.close();
 311  
                 totalConnections--;
 312  
 
 313  
                 // If the pool is now empty, create a new connection.  We're
 314  
                 // guaranteed not to exceed the connection limit since we
 315  
                 // just killed off one or more invalid connections, and no
 316  
                 // one else can be accessing this cache right now.
 317  
                 if (pool.empty())
 318  
                 {
 319  
                     return getNewConnection();
 320  
                 }
 321  
             }
 322  
         }
 323  
 
 324  
         // The connection pool was empty to start with--don't call this
 325  
         // routine if there's no connection to pop!
 326  
         // TODO: Propose general Turbine assertion failure exception? -PGO
 327  
         throw new Exception("Assertion failure: Attempted to pop "
 328  
                 + "connection from empty pool!");
 329  
     }
 330  
 
 331  
     /**
 332  
      * Helper method which determines whether a connection has expired.
 333  
      *
 334  
      * @param pc The connection to test.
 335  
      * @return True if the connection is expired, false otherwise.
 336  
      */
 337  
     private boolean isExpired(PooledConnection pc)
 338  
     {
 339  
         // Test the age of the connection (defined as current time
 340  
         // minus connection birthday) against the connection pool
 341  
         // expiration time.
 342  
         long birth = ((Long) timeStamps.get(pc)).class="keyword">longValue();
 343  
         long age   = System.currentTimeMillis() - birth;
 344  
 
 345  
         boolean dead = (expiryTime > 0)
 346  
             ? age > expiryTime
 347  
             : age > DEFAULT_EXPIRY_TIME;
 348  
 
 349  
         return dead; // He is dead, Jim.
 350  
     }
 351  
 
 352  
     /**
 353  
      * Determines if a connection is still valid.
 354  
      *
 355  
      * @param connection The connection to test.
 356  
      * @return True if the connection is valid, false otherwise.
 357  
      */
 358  
     private boolean isValid(PooledConnection connection)
 359  
     {
 360  
         // all this code is commented out because
 361  
         // connection.getConnection() is called when the connection
 362  
         // is returned to the pool and it will open a new logical Connection
 363  
         // which does not get closed, then when it is called again
 364  
         // when a connection is requested it likely fails because a
 365  
         // new Connection has been requested and the old one is still
 366  
         // open.  need to either do it right or skip it.  null check
 367  
         // was not working either.
 368  
 
 369  
         //try
 370  
         //{
 371  
             // This will throw an exception if:
 372  
             //     The connection is null
 373  
             //     The connection is closed
 374  
             // Therefore, it would be false.
 375  
         //connection.getConnection();
 376  
             // Check for expiration
 377  
             return !isExpired(connection);
 378  
             /*
 379  
         }
 380  
         catch (SQLException e)
 381  
         {
 382  
             return false;
 383  
         }
 384  
             */
 385  
     }
 386  
 
 387  
 
 388  
     /**
 389  
      * Close any open connections when this object is garbage collected.
 390  
      *
 391  
      * @exception Throwable Anything might happen...
 392  
      */
 393  
     protected void finalize()
 394  
         throws Throwable
 395  
     {
 396  
         shutdown();
 397  
     }
 398  
 
 399  
     /**
 400  
      * Close all connections to the database,
 401  
      */
 402  
     void shutdown()
 403  
     {
 404  
         if (pool != null)
 405  
         {
 406  
             while (!pool.isEmpty())
 407  
             {
 408  
                 try
 409  
                 {
 410  
                     ((PooledConnection) pool.pop()).close();
 411  
                 }
 412  
                 catch (SQLException ignore)
 413  
                 {
 414  
                 }
 415  
                 finally
 416  
                 {
 417  
                     totalConnections--;
 418  
                 }
 419  
             }
 420  
         }
 421  
         monitor.shutdown();
 422  
     }
 423  
 
 424  
     /**
 425  
      * Returns the Total connections in the pool
 426  
      *
 427  
      * @return total connections in the pool
 428  
      */
 429  
     int getTotalCount()
 430  
     {
 431  
         return totalConnections;
 432  
     }
 433  
 
 434  
     /**
 435  
      * Returns the available connections in the pool
 436  
      *
 437  
      * @return number of available connections in the pool
 438  
      */
 439  
     int getNbrAvailable()
 440  
     {
 441  
         return pool.size();
 442  
     }
 443  
 
 444  
     /**
 445  
      * Returns the checked out connections in the pool
 446  
      *
 447  
      * @return number of checked out connections in the pool
 448  
      */
 449  
     int getNbrCheckedOut()
 450  
     {
 451  
         return (totalConnections - pool.size());
 452  
     }
 453  
 
 454  
     /**
 455  
      * Decreases the count of connections in the pool
 456  
      * and also calls <code>notify()</code>.
 457  
      */
 458  
     void decrementConnections()
 459  
     {
 460  
         totalConnections--;
 461  
         notify();
 462  
     }
 463  
 
 464  
     /**
 465  
      * Get the name of the pool
 466  
      *
 467  
      * @return the name of the pool
 468  
      */
 469  
     String getPoolName()
 470  
     {
 471  
         return toString();
 472  
     }
 473  
 
 474  
     // ***********************************************************************
 475  
     // java.sql.ConnectionEventListener implementation
 476  
     // ***********************************************************************
 477  
 
 478  
     /**
 479  
      * This will be called if the Connection returned by the getConnection
 480  
      * method came from a PooledConnection, and the user calls the close()
 481  
      * method of this connection object. What we need to do here is to
 482  
      * release this PooledConnection from our pool...
 483  
      *
 484  
      * @param event the connection event
 485  
      */
 486  
     public void connectionClosed(ConnectionEvent event)
 487  
     {
 488  
         releaseConnection((PooledConnection) event.getSource());
 489  
     }
 490  
 
 491  
     /**
 492  
      * If a fatal error occurs, close the underlying physical connection so as
 493  
      * not to be returned in the future
 494  
      *
 495  
      * @param event the connection event
 496  
      */
 497  
     public void connectionErrorOccurred(ConnectionEvent event)
 498  
     {
 499  
         try
 500  
         {
 501  
             System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR");
 502  
             //remove this from the listener list because we are no more
 503  
             //interested in errors since we are about to close this connection
 504  
             ((PooledConnection) event.getSource())
 505  
                     .removeConnectionEventListener(this);
 506  
         }
 507  
         catch (Exception ignore)
 508  
         {
 509  
             //just ignore
 510  
         }
 511  
 
 512  
         try
 513  
         {
 514  
             closePooledConnection((PooledConnection) event.getSource());
 515  
         }
 516  
         catch (Exception ignore)
 517  
         {
 518  
             //just ignore
 519  
         }
 520  
     }
 521  
 
 522  
     /**
 523  
      * This method returns a connection to the pool, and <b>must</b>
 524  
      * be called by the requestor when finished with the connection.
 525  
      *
 526  
      * @param pcon The database connection to release.
 527  
      */
 528  
     private synchronized void releaseConnection(PooledConnection pcon)
 529  
     {
 530  
         if (isValid(pcon))
 531  
         {
 532  
             pool.push(pcon);
 533  
             notify();
 534  
         }
 535  
         else
 536  
         {
 537  
             closePooledConnection(pcon);
 538  
         }
 539  
     }
 540  
 
 541  
     /**
 542  
      *
 543  
      * @param pcon The database connection to close.
 544  
      */
 545  
     private void closePooledConnection(PooledConnection pcon)
 546  
     {
 547  
         try
 548  
         {
 549  
             pcon.close();
 550  
             timeStamps.remove(pcon);
 551  
         }
 552  
         catch (Exception e)
 553  
         {
 554  
             log.error("Error occurred trying to close a PooledConnection.", e);
 555  
         }
 556  
         finally
 557  
         {
 558  
             decrementConnections();
 559  
         }
 560  
     }
 561  
 
 562  
     ///////////////////////////////////////////////////////////////////////////
 563  
 
 564  
     /**
 565  
      * This inner class monitors the <code>PoolBrokerService</code>.
 566  
      *
 567  
      * This class is capable of logging the number of connections available in
 568  
      * the pool periodically. This can prove useful if you application
 569  
      * frozes after certain amount of time/requests and you suspect
 570  
      * that you have connection leakage problem.
 571  
      *
 572  
      * Set the <code>logInterval</code> property of your pool definition
 573  
      * to the number of seconds you want to elapse between loging the number of
 574  
      * connections.
 575  
      */
 576  0
     protected class Monitor extends Thread
 577  
     {
 578  
         /** true if the monot is running */
 579  0
         private boolean isRun = true;
 580  
 
 581  
         /**
 582  
          * run method for the monitor thread
 583  
          */
 584  
         public void run()
 585  
         {
 586  0
             StringBuffer buf = new StringBuffer();
 587  0
             while (logInterval > 0 && isRun)
 588  
             {
 589  0
                 buf.setLength(0);
 590  
 
 591  0
                 buf.append(getPoolName());
 592  0
                 buf.append(" avail: ").append(getNbrAvailable());
 593  0
                 buf.append(" in use: ").append(getNbrCheckedOut());
 594  0
                 buf.append(" total: ").append(getTotalCount());
 595  0
                 log.info(buf.toString());
 596  
 
 597  
                 // Wait for a bit.
 598  
                 try
 599  
                 {
 600  0
                     Thread.sleep(logInterval);
 601  
                 }
 602  0
                 catch (InterruptedException ignored)
 603  
                 {
 604  0
                 }
 605  
             }
 606  0
         }
 607  
 
 608  
         /**
 609  
          * Shut down the monitor
 610  
          */
 611  
         public void shutdown()
 612  
         {
 613  0
             isRun = false;
 614  0
         }
 615  
     }
 616  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.