KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > Yasna > forum > database > DbConnectionDefaultPool


1 /**
2  * Copyright (C) 2001 Yasna.com. All rights reserved.
3  *
4  * ===================================================================
5  * The Apache Software License, Version 1.1
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  * if any, must include the following acknowledgment:
21  * "This product includes software developed by
22  * Yasna.com (http://www.yasna.com)."
23  * Alternately, this acknowledgment may appear in the software itself,
24  * if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Yazd" and "Yasna.com" must not be used to
27  * endorse or promote products derived from this software without
28  * prior written permission. For written permission, please
29  * contact yazd@yasna.com.
30  *
31  * 5. Products derived from this software may not be called "Yazd",
32  * nor may "Yazd" appear in their name, without prior written
33  * permission of Yasna.com.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL YASNA.COM OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of Yasna.com. For more information
51  * on Yasna.com, please see <http://www.yasna.com>.
52  */

53
54 /**
55  * Copyright (C) 2000 CoolServlets.com. All rights reserved.
56  *
57  * ===================================================================
58  * The Apache Software License, Version 1.1
59  *
60  * Redistribution and use in source and binary forms, with or without
61  * modification, are permitted provided that the following conditions
62  * are met:
63  *
64  * 1. Redistributions of source code must retain the above copyright
65  * notice, this list of conditions and the following disclaimer.
66  *
67  * 2. Redistributions in binary form must reproduce the above copyright
68  * notice, this list of conditions and the following disclaimer in
69  * the documentation and/or other materials provided with the
70  * distribution.
71  *
72  * 3. The end-user documentation included with the redistribution,
73  * if any, must include the following acknowledgment:
74  * "This product includes software developed by
75  * CoolServlets.com (http://www.coolservlets.com)."
76  * Alternately, this acknowledgment may appear in the software itself,
77  * if and wherever such third-party acknowledgments normally appear.
78  *
79  * 4. The names "Jive" and "CoolServlets.com" must not be used to
80  * endorse or promote products derived from this software without
81  * prior written permission. For written permission, please
82  * contact webmaster@coolservlets.com.
83  *
84  * 5. Products derived from this software may not be called "Jive",
85  * nor may "Jive" appear in their name, without prior written
86  * permission of CoolServlets.com.
87  *
88  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
89  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
90  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
91  * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR
92  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
93  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
94  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
95  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
96  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
97  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
98  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
99  * SUCH DAMAGE.
100  * ====================================================================
101  *
102  * This software consists of voluntary contributions made by many
103  * individuals on behalf of CoolServlets.com. For more information
104  * on CoolServlets.com, please see <http://www.coolservlets.com>.
105  */

106
107 package com.Yasna.forum.database;
108
109 import java.sql.*;
110 import java.util.*;
111 import java.io.*;
112 import java.text.*;
113 import java.util.Date JavaDoc;
114 import com.Yasna.forum.*;
115
116 /**
117  * Default Yazd connection provider. It uses the excellent connection pool
118  * available from http://www.javaexchange.com. This connection provider is a
119  * a good choice unless you can use a container-managed one.
120  */

121 public class DbConnectionDefaultPool extends DbConnectionProvider {
122
123     private static final String JavaDoc NAME = "Default Connection Pool";
124     private static final String JavaDoc DESCRIPTION = "The default connection provider "
125         + "that uses the connection pool from javaexchange.com. It works with "
126         + "almost any database setup, is customizable, and offers good performance. "
127         + "Use this connection provider unless you have your own or can use a "
128         + "container managed connection pool.";
129     private static final String JavaDoc AUTHOR = "Yazd.Yasna.com";
130     private static final int MAJOR_VERSION = 1;
131     private static final int MINOR_VERSION = 0;
132     private static final boolean POOLED = true;
133
134     private ConnectionPool connectionPool = null;
135     private Properties props;
136     private Properties propDescriptions;
137
138     private Object JavaDoc initLock = new Object JavaDoc();
139
140     public DbConnectionDefaultPool() {
141         //this.manager = manager;
142
props = new Properties();
143         propDescriptions = new Properties();
144         //Initialize all property values
145
initializeProperties();
146         //Load any existing property values
147
loadProperties();
148     }
149
150     /**
151      * Returns a database connection.
152      */

153     public Connection getConnection() {
154         if (connectionPool == null) {
155             //block until the init has been done
156
synchronized(initLock) {
157                 //if still null, something has gone wrong
158
if (connectionPool == null) {
159                     System.err.println("Warning: DbConnectionDefaultPool.getConnection() was " +
160                     "called when the internal pool has not been initialized.");
161                     return null;
162                 }
163             }
164         }
165         return new ConnectionWrapper(connectionPool.getConnection(), connectionPool);
166     }
167
168     /**
169      * Starts the pool.
170      */

171     protected void start() {
172         //acquire lock so that no connections can be returned.
173
synchronized (initLock) {
174             //Get properties
175
String JavaDoc driver = props.getProperty("driver");
176             String JavaDoc server = props.getProperty("server");
177             String JavaDoc username = props.getProperty("username");
178             String JavaDoc password = props.getProperty("password");
179             int minConnections = 0, maxConnections = 0;
180             double connectionTimeout = 0.0;
181             try {
182                 minConnections = Integer.parseInt(props.getProperty("minConnections"));
183                 maxConnections = Integer.parseInt(props.getProperty("maxConnections"));
184                 connectionTimeout = Double.parseDouble(props.getProperty("connectionTimeout"));
185             }
186             catch (Exception JavaDoc e) {
187                 System.err.println("Error: could not parse default pool properties. " +
188                     "Make sure the values exist and are correct.");
189                 e.printStackTrace();
190                 return;
191             }
192             String JavaDoc logPath = props.getProperty("logPath");
193
194             try {
195                 connectionPool = new ConnectionPool(driver, server, username, password,
196                     minConnections, maxConnections, logPath, connectionTimeout);
197             }
198             catch (IOException ioe) {
199                 System.err.println("Error starting DbConnectionDefaultPool: " + ioe);
200                 ioe.printStackTrace();
201             }
202         }
203     }
204
205     /**
206      * Restarts the pool to take into account any property changes.
207      */

208     protected void restart() {
209         //Kill off pool.
210
destroy();
211         //Reload properties.
212
loadProperties();
213         //Start a new pool.
214
start();
215     }
216
217     /**
218      * Destroys the connection pool.
219      */

220     protected void destroy() {
221         if (connectionPool != null) {
222             try {
223                 connectionPool.destroy(1);
224             }
225             catch (Exception JavaDoc e) {
226                 e.printStackTrace();
227             }
228         }
229         //Release reference to connectionPool
230
connectionPool = null;
231     }
232
233     /**
234      * Returns the value of a property of the connection provider.
235      *
236      * @param name the name of the property.
237      * @returns the value of the property.
238      */

239     public String JavaDoc getProperty(String JavaDoc name) {
240         return (String JavaDoc)props.get(name);
241     }
242
243     /**
244      * Returns the description of a property of the connection provider.
245      *
246      * @param name the name of the property.
247      * @return the description of the property.
248      */

249     public String JavaDoc getPropertyDescription(String JavaDoc name) {
250         return (String JavaDoc)propDescriptions.get(name);
251     }
252
253     /**
254      * Returns an enumeration of the property names for the connection provider.
255      */

256     public Enumeration propertyNames() {
257         return props.propertyNames();
258     }
259
260     /**
261      * Sets a property of the connection provider. Each provider has a set number
262      * of properties that are determined by the author. Trying to set a non-
263      * existant property will result in an IllegalArgumentException.
264      *
265      * @param name the name of the property to set.
266      * @param value the new value for the property.
267      *
268      */

269     public void setProperty(String JavaDoc name, String JavaDoc value) {
270         props.put(name, value);
271         saveProperties();
272     }
273
274     /**
275      * Give default values to all the properties and descriptions.
276      */

277     private void initializeProperties() {
278         props.put("driver","");
279         props.put("server","");
280         props.put("username","");
281         props.put("password","");
282         props.put("minConnections","");
283         props.put("maxConnections","");
284         props.put("logPath","");
285         props.put("connectionTimeout","");
286
287         propDescriptions.put("driver","JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'");
288         propDescriptions.put("server","JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'");
289         propDescriptions.put("username","Database username. e.g. 'Scott'");
290         propDescriptions.put("password","Database password. e.g. 'Tiger'");
291         propDescriptions.put("minConnections","Minimum # of connections to start with in pool. Three is the recommended minimum");
292         propDescriptions.put("maxConnections","Maximum # of connections in dynamic pool. Fifteen should give good performance for an average load.");
293         propDescriptions.put("logPath","Absolute path name for log file. e.g. 'c:\\logs\\yazdDbLog.log'");
294         propDescriptions.put("connectionTimeout","Time in days between connection resets. e.g. '.5'");
295     }
296
297     /**
298      * Load whatever properties that already exist.
299      */

300     private void loadProperties() {
301         String JavaDoc driver = PropertyManager.getProperty("DbConnectionDefaultPool.driver");
302         String JavaDoc server = PropertyManager.getProperty("DbConnectionDefaultPool.server");
303         String JavaDoc username = PropertyManager.getProperty("DbConnectionDefaultPool.username");
304         String JavaDoc password = PropertyManager.getProperty("DbConnectionDefaultPool.password");
305         String JavaDoc minConnections = PropertyManager.getProperty("DbConnectionDefaultPool.minConnections");
306         String JavaDoc maxConnections = PropertyManager.getProperty("DbConnectionDefaultPool.maxConnections");
307         String JavaDoc logPath = PropertyManager.getProperty("DbConnectionDefaultPool.logPath");
308         String JavaDoc connectionTimeout = PropertyManager.getProperty("DbConnectionDefaultPool.connectionTimeout");
309
310         if (driver != null) { props.setProperty("driver", driver); }
311         if (server != null) { props.setProperty("server", server); }
312         if (username != null) { props.setProperty("username", username); }
313         if (password != null) { props.setProperty("password", password); }
314         if (minConnections != null) { props.setProperty("minConnections", minConnections); }
315         if (maxConnections != null) { props.setProperty("maxConnections", maxConnections); }
316         if (logPath != null) { props.setProperty("logPath", logPath); }
317         if (connectionTimeout != null) { props.setProperty("connectionTimeout", connectionTimeout); }
318     }
319
320     private void saveProperties() {
321         PropertyManager.setProperty("DbConnectionDefaultPool.driver", props.getProperty("driver"));
322         PropertyManager.setProperty("DbConnectionDefaultPool.server", props.getProperty("server"));
323         PropertyManager.setProperty("DbConnectionDefaultPool.username", props.getProperty("username"));
324         PropertyManager.setProperty("DbConnectionDefaultPool.password", props.getProperty("password"));
325         PropertyManager.setProperty("DbConnectionDefaultPool.minConnections", props.getProperty("minConnections"));
326         PropertyManager.setProperty("DbConnectionDefaultPool.maxConnections", props.getProperty("maxConnections"));
327         PropertyManager.setProperty("DbConnectionDefaultPool.logPath", props.getProperty("logPath"));
328         PropertyManager.setProperty("DbConnectionDefaultPool.connectionTimeout", props.getProperty("connectionTimeout"));
329     }
330
331     /**
332      * DbConnectionBroker
333      * @version 1.0.11 12/7/99
334      * @author Marc A. Mnich
335      *
336      * ----------------------------------------
337      * Modified June 18, 2000 by Matt Tucker
338      * Changes:
339      * - New package name, class name to make it nice to embed as
340      * an internal class.
341      * - Source code reformatting.
342      * - Added more error handling code in constructor, createConn method
343      * so that more information is given to Yazd users.
344      * DbConnectionBroker rules! Download it from javaexchange.com
345      * ----------------------------------------
346      *
347      * DbConnectionBroker
348      * A servlet-based broker for database connections.
349      * Creates and manages a pool of database connections.
350      * @version 1.0.11 12/7/99
351      * @author Marc A. Mnich
352      */

353     private class ConnectionPool implements Runnable JavaDoc {
354         private Thread JavaDoc runner;
355
356         private Connection[] connPool;
357         private int[] connStatus;
358
359         private long[] connLockTime, connCreateDate;
360         private String JavaDoc[] connID;
361         private String JavaDoc dbDriver, dbServer, dbLogin, dbPassword, logFileString;
362         private int currConnections, connLast, minConns, maxConns, maxConnMSec;
363
364         //available: set to false on destroy, checked by getConnection()
365
private boolean available=true;
366
367         private PrintWriter log;
368         private SQLWarning currSQLWarning;
369         private String JavaDoc pid;
370
371         /**
372          * Creates a new Connection Broker<br>
373          * dbDriver: JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'<br>
374          * dbServer: JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'<br>
375          * dbLogin: Database login name. e.g. 'Scott'<br>
376          * dbPassword: Database password. e.g. 'Tiger'<br>
377          * minConns: Minimum number of connections to start with.<br>
378          * maxConns: Maximum number of connections in dynamic pool.<br>
379          * logFileString: Absolute path name for log file. e.g. 'c:\temp\mylog.log' <br>
380          * maxConnTime: Time in days between connection resets. (Reset does a basic cleanup)<br>
381          */

382         public ConnectionPool (String JavaDoc dbDriver, String JavaDoc dbServer, String JavaDoc dbLogin,
383                 String JavaDoc dbPassword, int minConns, int maxConns,
384                     String JavaDoc logFileString, double maxConnTime) throws IOException
385         {
386             connPool = new Connection[maxConns];
387             connStatus = new int[maxConns];
388             connLockTime = new long[maxConns];
389             connCreateDate = new long[maxConns];
390             connID = new String JavaDoc[maxConns];
391             currConnections = minConns;
392             this.maxConns = maxConns;
393             this.dbDriver = dbDriver;
394             this.dbServer = dbServer;
395             this.dbLogin = dbLogin;
396             this.dbPassword = dbPassword;
397             this.logFileString = logFileString;
398             maxConnMSec = (int)(maxConnTime * 86400000.0); //86400 sec/day
399
if(maxConnMSec < 30000) { // Recycle no less than 30 seconds.
400
maxConnMSec = 30000;
401             }
402
403             try {
404                 log = new PrintWriter(new FileOutputStream(logFileString),true);
405                 // Can't open the requested file. Open the default file.
406
}
407             catch (IOException e1) {
408                 System.err.println("Warning: DbConnectionDefaultPool could not open \""
409                     + logFileString + "\" to write log to. Make sure that your Java " +
410                     "process has permission to write to the file and that the directory exists."
411                 );
412                 try {
413                     log = new PrintWriter(new FileOutputStream("DCB_" +
414                         System.currentTimeMillis() + ".log"), true
415                     );
416                 }
417                 catch (IOException e2) {
418                     throw new IOException("Can't open any log file");
419                 }
420             }
421
422             // Write the pid file (used to clean up dead/broken connection)
423
SimpleDateFormat formatter
424                 = new SimpleDateFormat ("yyyy.MM.dd G 'at' hh:mm:ss a zzz");
425             java.util.Date JavaDoc nowc = new java.util.Date JavaDoc();
426             pid = formatter.format(nowc);
427
428             BufferedWriter pidout = new BufferedWriter(new
429                                     FileWriter(logFileString + "pid"));
430             pidout.write(pid);
431             pidout.close();
432
433             log.println("Starting ConnectionPool:");
434             log.println("dbDriver = " + dbDriver);
435             log.println("dbServer = " + dbServer);
436             log.println("dbLogin = " + dbLogin);
437             log.println("log file = " + logFileString);
438             log.println("minconnections = " + minConns);
439             log.println("maxconnections = " + maxConns);
440             log.println("Total refresh interval = " + maxConnTime + " days");
441             log.println("-----------------------------------------");
442
443
444             // Initialize the pool of connections with the mininum connections:
445
// Problems creating connections may be caused during reboot when the
446
// servlet is started before the database is ready. Handle this
447
// by waiting and trying again. The loop allows 5 minutes for
448
// db reboot.
449
boolean connectionsSucceeded=false;
450             int dbLoop=20;
451
452             try {
453                 for(int i=1; i < dbLoop; i++) {
454                     try {
455                         for(int j=0; j < currConnections; j++) {
456                             createConn(j);
457                         }
458                         connectionsSucceeded=true;
459                         break;
460                     }
461                     catch (SQLException e){
462                         log.println("--->Attempt (" + String.valueOf(i) +
463                                 " of " + String.valueOf(dbLoop) +
464                                 ") failed to create new connections set at startup: ");
465                         log.println(" " + e);
466                         log.println(" Will try again in 15 seconds...");
467                         try { Thread.sleep(15000); }
468                         catch(InterruptedException JavaDoc e1) {}
469                     }
470                 }
471                 if(!connectionsSucceeded) { // All attempts at connecting to db exhausted
472
log.println("\r\nAll attempts at connecting to Database exhausted");
473                     throw new IOException();
474                 }
475             }
476             catch (Exception JavaDoc e) {
477                 e.printStackTrace();
478                 throw new IOException();
479             }
480
481             // Fire up the background housekeeping thread
482

483             runner = new Thread JavaDoc(this);
484             runner.start();
485
486         } //End ConnectionPool()
487

488
489         /**
490          * Housekeeping thread. Runs in the background with low CPU overhead.
491          * Connections are checked for warnings and closure and are periodically
492          * restarted.
493          * This thread is a catchall for corrupted
494          * connections and prevents the buildup of open cursors. (Open cursors
495          * result when the application fails to close a Statement).
496          * This method acts as fault tolerance for bad connection/statement programming.
497          */

498         public void run() {
499             boolean forever = true;
500             Statement stmt=null;
501             String JavaDoc currCatalog=null;
502
503             while(forever) {
504                 // Make sure the log file is the one this instance opened
505
// If not, clean it up!
506
try {
507                     BufferedReader in = new BufferedReader(new FileReader(logFileString + "pid"));
508                     String JavaDoc curr_pid = in.readLine();
509                     if(curr_pid.equals(pid)) {
510                         //log.println("They match = " + curr_pid);
511
}
512                     else {
513                         //log.println("No match = " + curr_pid);
514
log.close();
515
516                          // Close all connections silently - they are definitely dead.
517
for(int i=0; i < currConnections; i++) {
518                             try {
519                                 connPool[i].close();
520                             }
521                             catch (SQLException e1) {} // ignore
522
}
523                         // Returning from the run() method kills the thread
524
return;
525                     }
526                     in.close();
527                 }
528                 catch (IOException e1) {
529                     log.println("Can't read the file for pid info: " +
530                         logFileString + "pid");
531                 }
532
533                 // Get any Warnings on connections and print to event file
534
for(int i=0; i < currConnections; i++) {
535                     try {
536                         currSQLWarning = connPool[i].getWarnings();
537                         if(currSQLWarning != null) {
538                             log.println("Warnings on connection " +
539                                 String.valueOf(i) + " " + currSQLWarning);
540                             connPool[i].clearWarnings();
541                         }
542                     }
543                     catch(SQLException e) {
544                         log.println("Cannot access Warnings: " + e);
545                     }
546                 }
547
548                 for(int i=0; i < currConnections; i++) { // Do for each connection
549
long age = System.currentTimeMillis() - connCreateDate[i];
550
551                     synchronized(connStatus) {
552                         if(connStatus[i] > 0) { // In use, catch it next time!
553
continue;
554                         }
555                         connStatus[i] = 2; // Take offline (2 indicates housekeeping lock)
556
}
557
558                     try { // Test the connection with createStatement call
559
if(age > maxConnMSec) { // Force a reset at the max conn time
560
throw new SQLException();
561                         }
562
563                         stmt = connPool[i].createStatement();
564                         connStatus[i] = 0; // Connection is O.K.
565
//log.println("Connection confirmed for conn = " +
566
// String.valueOf(i));
567

568                         // Some DBs return an object even if DB is shut down
569
if(connPool[i].isClosed()) {
570                             throw new SQLException();
571                         }
572                         // Connection has a problem, restart it
573
}
574                     catch(SQLException e) {
575                         try {
576                             log.println(new Date JavaDoc().toString() +
577                                 " ***** Recycling connection " +
578                                 String.valueOf(i) + ":"
579                             );
580
581                             connPool[i].close();
582                             createConn(i);
583                         }
584                         catch(SQLException e1) {
585                             log.println("Failed: " + e1);
586                             connStatus[i] = 0; // Can't open, try again next time
587
}
588                     }
589                     finally {
590                         try {
591                             if(stmt != null) {
592                                 stmt.close();
593                             }
594                         }
595                         catch(SQLException e1){};
596                     }
597                 }
598
599                 try {
600                     Thread.sleep(20000);
601                 } // Wait 20 seconds for next cycle
602
catch(InterruptedException JavaDoc e) {
603                     // Returning from the run method sets the internal
604
// flag referenced by Thread.isAlive() to false.
605
// This is required because we don't use stop() to
606
// shutdown this thread.
607
return;
608                 }
609             }
610         } // End run
611

612         /**
613          * This method hands out the connections in round-robin order.
614          * This prevents a faulty connection from locking
615          * up an application entirely. A browser 'refresh' will
616          * get the next connection while the faulty
617          * connection is cleaned up by the housekeeping thread.
618          *
619          * If the min number of threads are ever exhausted, new
620          * threads are added up the the max thread count.
621          * Finally, if all threads are in use, this method waits
622          * 2 seconds and tries again, up to ten times. After that, it
623          * returns a null.
624          */

625         public Connection getConnection() {
626
627             Connection conn=null;
628
629             if(available){
630                 boolean gotOne = false;
631
632                 for(int outerloop=1; outerloop<=10; outerloop++) {
633
634                     try {
635                         int loop=0;
636                         int roundRobin = connLast + 1;
637                         if(roundRobin >= currConnections) roundRobin=0;
638
639                         do {
640                             synchronized(connStatus) {
641                                 if((connStatus[roundRobin] < 1) &&
642                                                     (! connPool[roundRobin].isClosed()))
643                                 {
644                                     conn = connPool[roundRobin];
645                                     connStatus[roundRobin]=1;
646                                     connLockTime[roundRobin] =
647                                                             System.currentTimeMillis();
648                                     connLast = roundRobin;
649                                     gotOne = true;
650                                     break;
651                                 }
652                                 else {
653                                     loop++;
654                                     roundRobin++;
655                                     if(roundRobin >= currConnections) roundRobin=0;
656                                 }
657                             }
658                         }
659                         while((gotOne==false)&&(loop < currConnections));
660                     }
661                     catch (SQLException e1) {}
662
663                     if(gotOne) {
664                         break;
665                     }
666                     else {
667                         synchronized(this) { // Add new connections to the pool
668
if(currConnections < maxConns) {
669                                 try {
670                                     createConn(currConnections);
671                                                     currConnections++;
672                                 }
673                                 catch(SQLException e) {
674                                     log.println("Unable to create new connection: " + e);
675                                 }
676                             }
677                         }
678
679                         try { Thread.sleep(2000); }
680                         catch(InterruptedException JavaDoc e) {}
681                         log.println("-----> Connections Exhausted! Will wait and try " +
682                             "again in loop " +
683                             String.valueOf(outerloop));
684                     }
685                 } // End of try 10 times loop
686

687             }
688             else {
689                 log.println("Unsuccessful getConnection() request during destroy()");
690             } // End if(available)
691

692             return conn;
693         }
694
695         /**
696          * Returns the local JDBC ID for a connection.
697          */

698         public int idOfConnection(Connection conn) {
699             int match;
700             String JavaDoc tag;
701
702             try {
703                 tag = conn.toString();
704             }
705             catch (NullPointerException JavaDoc e1) {
706                 tag = "none";
707             }
708
709             match=-1;
710
711             for(int i=0; i< currConnections; i++) {
712                 if(connID[i].equals(tag)) {
713                     match = i;
714                     break;
715                 }
716             }
717             return match;
718         }
719
720         /**
721          * Frees a connection. Replaces connection back into the main pool for
722          * reuse.
723          */

724         public String JavaDoc freeConnection(Connection conn) {
725             String JavaDoc res="";
726
727             int thisconn = idOfConnection(conn);
728             if(thisconn >= 0) {
729                 connStatus[thisconn]=0;
730                 res = "freed " + conn.toString();
731                 //log.println("Freed connection " + String.valueOf(thisconn) +
732
// " normal exit: ");
733
}
734             else {
735                 log.println("----> Could not free connection!!!");
736             }
737
738             return res;
739         }
740
741         /**
742          * Returns the age of a connection -- the time since it was handed out to
743          * an application.
744          */

745         public long getAge(Connection conn) { // Returns the age of the connection in millisec.
746
int thisconn = idOfConnection(conn);
747             return System.currentTimeMillis() - connLockTime[thisconn];
748         }
749
750         private void createConn(int i) throws SQLException {
751              Date JavaDoc now = new Date JavaDoc();
752              try {
753                 Class.forName (dbDriver);
754                 connPool[i] = DriverManager.getConnection
755                           (dbServer,dbLogin,dbPassword);
756                 connStatus[i]=0;
757                 connID[i]=connPool[i].toString();
758                 connLockTime[i]=0;
759                 connCreateDate[i] = now.getTime();
760
761                 log.println(now.toString() + " Opening connection " + String.valueOf(i) +
762                     " " + connPool[i].toString() + ":");
763             }
764             catch (ClassNotFoundException JavaDoc e2) {
765                 e2.printStackTrace();
766                 throw new SQLException(e2.getMessage());
767             }
768         }
769
770         /**
771          * Shuts down the housekeeping thread and closes all connections
772          * in the pool. Call this method from the destroy() method of the servlet.
773          */

774
775         /**
776          * Multi-phase shutdown. having following sequence:
777          * <OL>
778          * <LI><code>getConnection()</code> will refuse to return connections.
779          * <LI>The housekeeping thread is shut down.<br>
780          * Up to the time of <code>millis</code> milliseconds after shutdown of
781          * the housekeeping thread, <code>freeConnection()</code> can still be
782          * called to return used connections.
783          * <LI>After <code>millis</code> milliseconds after the shutdown of the
784          * housekeeping thread, all connections in the pool are closed.
785          * <LI>If any connections were in use while being closed then a
786          * <code>SQLException</code> is thrown.
787          * <LI>The log is closed.
788          * </OL><br>
789          * Call this method from a servlet destroy() method.
790          *
791          * @param millis the time to wait in milliseconds.
792          * @exception SQLException if connections were in use after
793          * <code>millis</code>.
794          */

795         public void destroy(int millis) throws SQLException {
796
797             // Checking for invalid negative arguments is not necessary,
798
// Thread.join() does this already in runner.join().
799

800             // Stop issuing connections
801
available=false;
802
803             // Shut down the background housekeeping thread
804
runner.interrupt();
805
806             // Wait until the housekeeping thread has died.
807
try { runner.join(millis); }
808             catch(InterruptedException JavaDoc e){} // ignore
809

810             // The housekeeping thread could still be running
811
// (e.g. if millis is too small). This case is ignored.
812
// At worst, this method will throw an exception with the
813
// clear indication that the timeout was too short.
814

815             long startTime=System.currentTimeMillis();
816
817             // Wait for freeConnection() to return any connections
818
// that are still used at this time.
819
int useCount;
820             while((useCount=getUseCount())>0 && System.currentTimeMillis() - startTime <= millis) {
821                 try { Thread.sleep(500); }
822                 catch(InterruptedException JavaDoc e) {} // ignore
823
}
824
825             // Close all connections, whether safe or not
826
for(int i=0; i < currConnections; i++) {
827                 try {
828                     connPool[i].close();
829                 }
830                 catch (SQLException e1)
831                 {
832                     log.println("Cannot close connections on Destroy");
833                 }
834             }
835
836             if(useCount > 0) {
837                 //bt-test successful
838
String JavaDoc msg="Unsafe shutdown: Had to close "+useCount+
839                             " active DB connections after "+millis+"ms";
840                 log.println(msg);
841                 // Close all open files
842
log.close();
843                 // Throwing following Exception is essential because servlet authors
844
// are likely to have their own error logging requirements.
845
throw new SQLException(msg);
846             }
847
848             // Close all open files
849
log.close();
850
851         }//End destroy()
852

853
854         /**
855          * Less safe shutdown. Uses default timeout value.
856          * This method simply calls the <code>destroy()</code> method
857          * with a <code>millis</code>
858          * value of 10000 (10 seconds) and ignores <code>SQLException</code>
859          * thrown by that method.
860          * @see #destroy(int)
861          */

862         public void destroy() {
863             try {
864                 destroy(10000);
865             }
866             catch(SQLException e) {}
867         }
868
869         /**
870          * Returns the number of connections in use.
871          */

872         // This method could be reduced to return a counter that is
873
// maintained by all methods that update connStatus.
874
// However, it is more efficient to do it this way because:
875
// Updating the counter would put an additional burden on the most
876
// frequently used methods; in comparison, this method is
877
// rarely used (although essential).
878
public int getUseCount() {
879             int useCount=0;
880             synchronized(connStatus) {
881                 for(int i=0; i < currConnections; i++) {
882                     if(connStatus[i] > 0) { // In use
883
useCount++;
884                     }
885                 }
886             }
887             return useCount;
888         }//End getUseCount()
889

890         /**
891          * Returns the number of connections in the dynamic pool.
892          */

893         public int getSize() {
894             return currConnections;
895         }//End getSize()
896

897     } // End class
898

899     /**
900      * An implementation of the Connection interface that wraps an underlying
901      * Connection object. It releases the connection back to a connection pool
902      * when Connection.close() is called.
903      */

904     public class ConnectionWrapper implements Connection {
905
906         private Connection connection;
907         private ConnectionPool connectionPool;
908
909         public ConnectionWrapper(Connection connection, ConnectionPool connectionPool) {
910             this.connection = connection;
911             this.connectionPool = connectionPool;
912         }
913
914         /**
915          * Instead of closing the underlying connection, we simply release
916          * it back into the pool.
917          */

918         public void close() throws SQLException {
919             connectionPool.freeConnection(this.connection);
920             //Release object references. Any further method calls on the
921
//connection will fail.
922
connection = null;
923             connectionPool = null;
924         }
925
926         public String JavaDoc toString() {
927             if (connection != null) {
928                 return connection.toString();
929             }
930             else {
931                 return "CoolServlets connection wrapper";
932             }
933         }
934
935         public Statement createStatement() throws SQLException {
936             return connection.createStatement();
937         }
938
939         public void setHoldability(int holdability) throws SQLException {
940             connection.setHoldability(holdability);
941         }
942
943         public int getHoldability() throws SQLException {
944             return connection.getHoldability();
945         }
946
947         public Savepoint setSavepoint() throws SQLException {
948             return connection.setSavepoint();
949         }
950
951         public Savepoint setSavepoint(String JavaDoc name) throws SQLException {
952             return connection.setSavepoint(name);
953         }
954
955         public void rollback(Savepoint savepoint) throws SQLException {
956             connection.rollback(savepoint);
957         }
958
959
960         public PreparedStatement prepareStatement(String JavaDoc sql) throws SQLException {
961             return connection.prepareStatement(sql);
962         }
963
964         public CallableStatement prepareCall(String JavaDoc sql) throws SQLException {
965             return connection.prepareCall(sql);
966         }
967
968         public String JavaDoc nativeSQL(String JavaDoc sql) throws SQLException {
969             return connection.nativeSQL(sql);
970         }
971
972         public void setAutoCommit(boolean autoCommit) throws SQLException {
973             connection.setAutoCommit(autoCommit);
974         }
975
976         public boolean getAutoCommit() throws SQLException {
977             return connection.getAutoCommit();
978         }
979
980         public void commit() throws SQLException {
981             connection.commit();
982         }
983
984         public void rollback() throws SQLException {
985             connection.rollback();
986         }
987
988         public boolean isClosed() throws SQLException {
989             return connection.isClosed();
990         }
991
992         public DatabaseMetaData getMetaData() throws SQLException {
993             return connection.getMetaData();
994         }
995
996         public void setReadOnly(boolean readOnly) throws SQLException {
997             connection.setReadOnly(readOnly);
998         }
999
1000        public boolean isReadOnly() throws SQLException {
1001            return connection.isReadOnly();
1002        }
1003
1004        public void setCatalog(String JavaDoc catalog) throws SQLException {
1005            connection.setCatalog(catalog);
1006        }
1007
1008        public String JavaDoc getCatalog() throws SQLException {
1009            return connection.getCatalog();
1010        }
1011
1012        public void setTransactionIsolation(int level) throws SQLException {
1013            connection.setTransactionIsolation(level);
1014        }
1015
1016        public int getTransactionIsolation() throws SQLException {
1017            return connection.getTransactionIsolation();
1018        }
1019
1020        public SQLWarning getWarnings() throws SQLException {
1021            return connection.getWarnings();
1022        }
1023
1024        public void clearWarnings() throws SQLException {
1025            connection.clearWarnings();
1026        }
1027        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
1028            connection.releaseSavepoint(savepoint);
1029        }
1030        public Statement createStatement(int resultSetType, int resultSetConcurrency)
1031                throws SQLException
1032        {
1033            return connection.createStatement(resultSetType, resultSetConcurrency);
1034        }
1035        public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
1036                throws SQLException
1037        {
1038            return connection.createStatement(resultSetType, resultSetConcurrency,resultSetHoldability);
1039        }
1040
1041
1042        public PreparedStatement prepareStatement(String JavaDoc sql, int resultSetType,
1043                int resultSetConcurrency) throws SQLException
1044        {
1045            return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
1046        }
1047
1048        public CallableStatement prepareCall(String JavaDoc sql, int resultSetType,
1049                int resultSetConcurrency) throws SQLException
1050        {
1051            return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
1052        }
1053
1054        public Map getTypeMap() throws SQLException {
1055            return connection.getTypeMap();
1056        }
1057
1058        public PreparedStatement prepareStatement(String JavaDoc sql, String JavaDoc[] columnNames) throws SQLException {
1059            return connection.prepareStatement(sql,columnNames);
1060        }
1061        public PreparedStatement prepareStatement(String JavaDoc sql, int[] columnIndexes) throws SQLException {
1062            return connection.prepareStatement(sql,columnIndexes);
1063        }
1064        public PreparedStatement prepareStatement(String JavaDoc sql, int autoGeneratedKeys) throws SQLException {
1065            return connection.prepareStatement(sql,autoGeneratedKeys);
1066        }
1067        public PreparedStatement prepareStatement(String JavaDoc sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
1068            return connection.prepareStatement(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
1069        }
1070        public CallableStatement prepareCall(String JavaDoc sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
1071                    return connection.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
1072                }
1073
1074
1075        public void setTypeMap(Map map) throws SQLException {
1076            connection.setTypeMap(map);
1077        }
1078
1079    }
1080}
1081
Popular Tags