KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > protomatter > jdbc > pool > JdbcConnectionPool


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

52
53 import com.protomatter.util.*;
54 import com.protomatter.pool.*;
55 import com.protomatter.syslog.*;
56 import java.util.*;
57 import java.text.*;
58 import java.sql.*;
59 import java.io.*;
60
61 /**
62  * Provides a pool of pre-opened JDBC connections.
63  * The configuration hashtable can contain the following:<P>
64  *
65  * <ul><dl>
66  * <dt><tt>pool.initialSize</tt> (Integer)</dt>
67  * <dd>The initial pool size (default is 0).</dd><P>
68  *
69  * <dt><tt>pool.maxSize</tt> (Integer)</dt>
70  * <dd>The max pool size (default is -1). If the max
71  * pool size is -1, the pool grows infinitely.</dd><P>
72  *
73  * <dt><tt>pool.growBlock</tt> (Integer)</dt>
74  * <dd>The grow size (default is 1). When a new
75  * object is needed, this many are created.</dd><P>
76  *
77  * <dt><tt>pool.createWaitTime</tt> (Integer)</dt>
78  * <dd>The time (in ms) to sleep between pool object creates
79  * (default is 0). This is useful for database connection
80  * pools where it's possible to overload the database by
81  * trying to make too many connections too quickly.</dd><P>
82  *
83  * <dt><tt>jdbc.driver</tt> (String)</dt>
84  * <dd>The name of the JDBC driver class to use</dd><P>
85  *
86  * <dt><tt>jdbc.URL</tt> (String)</dt>
87  * <dd>The URL to use for the underlying. JDBC connections.</dd><P>
88  *
89  * <dt><tt>jdbc.properties</tt> (java.util.Properties)</dt>
90  * <dd>Properties for the connection. Should include at
91  * least "user" and "password" -- see
92  * <tt>DriverManager.getConnection(String, Properties)</tt>
93  * for what the properties should include. Each underlying
94  * JDBC driver has it's own properties that it pays attention
95  * to, so you may need to consult the driver's documentation.</dd><P>
96  *
97  * <dt><tt>jdbc.validityCheckStatement</tt> (String)</dt>
98  * <dd>A SQL statement that is guaranteed to return at
99  * least 1 row. For Oracle, this is "<tt>select 1
100  * from dual</tt>" and for Sybase it is "<tt>select 1</tt>".
101  * This statement is used as a means of checking that a
102  * connection is indeed working.</dd><P>
103  *
104  * <dt><tt>pool.useSyslog</tt> (Boolean)</dt>
105  * <dd>When writing warnings, errors, etc, should the pool
106  * use Syslog, or the <tt>PrintWriter</TT> returned from
107  * <tt>DriverManager.getLogWriter()</tt>? Default is
108  * <tt>true</tt>.
109  * </dd><P>
110  *
111  * <dt><tt>pool.syslogChannelList</tt> (String)</dt>
112  * <dd>A comma-separated list of channel names to
113  * log messages to. The symbolic names
114  * <TT>Syslog.DEFAULT_CHANNEL</TT> and <TT>Syslog.ALL_CHANNEL</TT>
115  * are understood. This is ignored if
116  * <tt>pool.useSyslog</tt> is <tt>false</tt>.
117  * </dd><P>
118  *
119  * <dt><tt>pool.validateOnCheckout</tt> (Boolean)</dt>
120  * <dd>Determine if we should only hand out validated connections
121  * or not. If set to true, each connection is tested with the
122  * <TT>pool.validityCheckStatement</TT> prior to being returned.
123  * Default is false. If this option is used, you <b>must</b> specify
124  * the <tt>jdbc.validityCheckStatement</tt> option also.
125  * </dd><P>
126  *
127  * <dt><tt>pool.maxCheckoutRefreshAttempts</tt> (Integer)</dt>
128  * <dd>Determines the number of times to attempt to refresh
129  * a connection on checkout before giving up. Default is 5,
130  * and setting it to -1 will cause the pool to keep trying
131  * forever. This setting only comes into play if
132  * <tt>pool.validateOnCheckout</tt> is set to <tt>true</tt>.
133  * </dd><P>
134  *
135  * <dt><tt>pool.checkoutRefreshWaitTime</tt> (Integer)</dt>
136  * <dd>The number of milliseconds to wait between attempts
137  * to refresh a connection when checking it out from the pool.
138  * The default is 500ms. Setting this to 0 will cause no
139  * delay. This setting only comes into play if
140  * <tt>pool.validateOnCheckout</tt> is set to <tt>true</tt>.
141  * </dd><P>
142  *
143  * <dt><tt>pool.refreshThreadCheckInterval</tt> (Integer)</dt>
144  * <dd>If present and &gt; 0, this is the number of seconds for
145  * a low-priority thread to sleep between calls to refreshConnections()
146  * on this pool. If this option is used, you <b>must</b> specify
147  * the <tt>jdbc.validityCheckStatement</tt> option also.
148  * </dd><P>
149  *
150  * <dt><tt>pool.verboseRefresh</tt> (Boolean)</dt>
151  * <dd>If present and true, the refresh thread will write
152  * messages when it refreshes connections.
153  * </dd><P>
154  *
155  * <dt><tt>pool.verboseValidate</tt> (Boolean)</dt>
156  * <dd>If present and true, operations while validating
157  * connections before checkout will be logged.
158  * </dd><P>
159  *
160  * <dt><tt>pool.maxConnectionIdleTime</tt> (Integer)</dt>
161  * <dd>If this property is present, and the <tt>pool.maidThreadCheckInterval</tt>
162  * property is also present, then a thread will be created that
163  * looks for connections that have been idle for more than
164  * <tt>pool.maxConnectionIdleTime</tt> seconds. When this thread
165  * finds them, it closed the connection and logs a warning
166  * with a stack trace of when the connection
167  * was checked out of the pool. This is primarily here as a debugging
168  * aid for finding places where connections are not getting close, and
169  * <i><b>should not</b></i> be used in a production environment.
170  * </dd><P>
171  *
172  * <dt><tt>pool.maidThreadCheckInterval</tt> (Integer)</dt>
173  * <dd>This is the number of seconds between attempts by the
174  * maid thread (if present) to find idle connections.
175  * </dd><P>
176  *
177  * </dl></ul>
178  *
179  * @see java.sql.DriverManager
180  * @see JdbcConnectionPoolDriver
181  * @see JdbcConnectionPoolConnection
182  */

183 public class JdbcConnectionPool
184 extends GrowingObjectPool
185 implements SyslogChannelAware
186 {
187   private String JavaDoc url = null;
188   private String JavaDoc driverName = null;
189   private Properties props = null;
190   private String JavaDoc poolName = null;
191   private String JavaDoc checkStatement = null;
192   private boolean initialized = false;
193   private boolean validateOnCheckout = false;
194   private boolean verboseValidate = false;
195
196   private int maxCheckoutRefreshAttempts = 5;
197   private int checkoutRefreshWaitTime = 500;
198
199   private RefreshThread refreshThread = null;
200   private MaidThread maidThread = null;
201   private int maxConnectionIdleTime = 0;
202   private Driver driver = null;
203
204   private Object JavaDoc syslogChannel = null;
205
206   private boolean useSyslog = true;
207
208   protected static Debug DEBUG = Debug.forPackage(JdbcConnectionPool.class);
209   protected static Channel log = Channel.forPackage(JdbcConnectionPool.class);
210   
211   int getMaxCheckoutRefreshAttempts()
212   {
213     return this.maxCheckoutRefreshAttempts;
214   }
215   int getCheckoutRefreshWaitTime()
216   {
217     return this.checkoutRefreshWaitTime;
218   }
219
220   /**
221    * Create a new JDBC connection pool with the given
222    * name, and the given initialization parameters.
223    *
224    * @param name The name of the pool.
225    * @param args Initialization arguments.
226    * @see #init
227    * @exception Exception If there is a problem initializing the pool.
228    */

229   public JdbcConnectionPool(String JavaDoc name, Map args)
230   throws Exception JavaDoc
231   {
232     this(name);
233     init(args);
234   }
235
236   /**
237    * Create a connection pool from properties.
238    * Looks for properties in the following form:<P>
239    *
240    * <blockquote><pre>
241    * ##
242    * ## Pool initialization parameters. These each
243    * ## correspond to parameters passed into the
244    * ## init(ht) method -- the values are each
245    * ## converted to the appropriate type. The
246    * ## "jdbc.properties" option is not specified
247    * ## here.
248    * ##
249    * com.protomatter.jdbc.pool.<i>POOLNAME</i>=\
250    * jdbc.driver=<i>driverclass</i>,\
251    * jdbc.URL=<i>connection_URL</i>,\
252    * jdbc.validityCheckStatement=<i>driverclass</i>,\
253    * pool.maxCheckoutRefreshAttempts=<i>num_attempts</i>,\
254    * pool.checkoutRefreshWaitTime=<i>num_milliseconds</i>,\
255    * pool.refreshThreadCheckInterval=<i>num_seconds</i>,\
256    * pool.verboseRefresh=<i>true or false</i>,\
257    * pool.verboseValidate=<i>true or false</i>,\
258    * pool.initialSize=<i>initial_pool_size</i>,\
259    * pool.maxSize=<i>max_pool_size</i>,\
260    * pool.growBlock=<i>pool_grow_block_size</i>,\
261    * pool.createWaitTime=<i>pool_create_wait_time</i>,\
262    * pool.maidThreadCheckInterval=<i>num_seconds</i>,\
263    * pool.maxConnectionIdleTime=<i>num_seconds</i>,\
264    * pool.validateOnCheckout=<i>true or false</i>,\
265    * pool.syslogChannelList=<i>list-of-channel-names</i>
266    *
267    * ##
268    * ## Connection properties for the underlying driver.
269    * ## These correspond to the properties that are
270    * ## placed in the configuraton hashtable with the
271    * ## key "jdbc.properties". These will be passed as
272    * ## the second argument to DriverManager.getConnection().
273    * ## Usually it just contains a usename and a password, but
274    * ## many drivers allow you to specify other options here.
275    * ##
276    * com.protomatter.jdbc.pool.<i>POOLNAME</i>.jdbcProperties=\
277    * user=<i>username</i>,\
278    * password=<i>password</i>,\
279    * key=<i>val</i>
280    * </pre></blockquote><P>
281    *
282    * Each value specified in the properties is converted to
283    * the correct type (for instance, the "<tt>pool.initialSize</tt>"
284    * property's value is converted to an Integer). The
285    * "<tt><i>POOLNAME</i></tt>" that appears above must match
286    * the "<tt>name</tt>" parameter passed to this method -- this
287    * allows to the same properties object to be passed into
288    * this constructor multiple times (each with a different
289    * "<tt>name</tt>" parameter) to create multiple pools.
290    *
291    * @see #init
292    * @param name The name of the pool.
293    * @param props Properties describing pools.
294    */

295   public JdbcConnectionPool(String JavaDoc name, Properties props)
296   throws Exception JavaDoc
297   {
298     this(name);
299
300     // read system properties that look like this:
301
// com.protomatter.jdbc.pool.POOLNAME = key=value,key=value,...
302
// com.protomatter.jdbc.pool.POOLNAME.jdbcProperties = key=value,key=value,...
303

304     String JavaDoc prefix = "com.protomatter.jdbc.pool.";
305     int prefixLen = prefix.length();
306     String JavaDoc suffix = ".jdbcProperties";
307
308     Enumeration e = props.keys();
309     while (e.hasMoreElements())
310     {
311       String JavaDoc key = (String JavaDoc)e.nextElement();
312       if (key.startsWith(prefix) && !key.endsWith(suffix))
313       {
314         String JavaDoc pName = key.substring(prefixLen);
315         if (pName.equals(name))
316         {
317           String JavaDoc value = props.getProperty(key);
318           Properties poolProps = getProperties(value);
319           Properties jdbcProps = getProperties(props.getProperty(prefix + pName + suffix));
320           createPoolFromProps(poolProps, jdbcProps);
321         }
322       }
323     }
324   }
325
326   /**
327    * Get the channel(s) to log messages to.
328    *
329    * @see SyslogChannelAware
330    */

331   public Object JavaDoc getSyslogChannel()
332   {
333     return syslogChannel;
334   }
335
336   /**
337    * Set the channel that messages will be logged to.
338    *
339    * @see SyslogChannelAware
340    */

341   public void setSyslogChannel(String JavaDoc channelName)
342   {
343     this.syslogChannel = channelName;
344   }
345
346   /**
347    * Set the list of channels that messages will be logged to.
348    *
349    * @see SyslogChannelAware
350    */

351   public void setSyslogChannelList(List channelList)
352   {
353     this.syslogChannel = (Object JavaDoc)channelList.toArray();
354   }
355
356   /**
357    * Use Syslog to log messages?
358    */

359   boolean useSyslog()
360   {
361     return this.useSyslog;
362   }
363
364   /**
365    * Create pools from a properties object. This method looks for
366    * all the properties matching the constraints in the
367    * {@link #JdbcConnectionPool(String, Properties) JdbcConnectionPool(String, Properties) }
368    * constructor, and creates a connection pool for each. The
369    * resulting pools are placed in a List and returned.
370    *
371    * @see #JdbcConnectionPool(String, Properties)
372    */

373   public static List createPools(Properties props)
374   throws Exception JavaDoc
375   {
376     String JavaDoc prefix = "com.protomatter.jdbc.pool.";
377     int prefixLen = prefix.length();
378     String JavaDoc suffix = ".jdbcProperties";
379
380     List list = new ArrayList();
381     Enumeration e = props.keys();
382     while (e.hasMoreElements())
383     {
384       String JavaDoc key = (String JavaDoc)e.nextElement();
385       if (key.startsWith(prefix) && !key.endsWith(suffix))
386       {
387         String JavaDoc pName = key.substring(prefixLen);
388         list.add(new JdbcConnectionPool(pName, props));
389       }
390     }
391     return list;
392   }
393
394   private Properties getProperties(String JavaDoc s)
395   {
396     String JavaDoc tok;
397     StringTokenizer st;
398     Properties props = new Properties();
399     int i;
400
401     if (s == null)
402         return props;
403
404     st = new StringTokenizer(s, ",");
405
406     while (st.hasMoreElements()) {
407         tok= st.nextToken();
408
409         if (-1 == (i= tok.indexOf('=')))
410             throw new IllegalArgumentException JavaDoc ("Invalid property: " + tok);
411
412         props.put(tok.substring(0,i), tok.substring(i+1));
413     }
414
415     return props;
416   }
417
418   private void createPoolFromProps(Properties props, Properties jdbcProps)
419   throws Exception JavaDoc
420   {
421     // take a properties object that contains pool options and
422
// change it into a hashtable with correct values and initialize
423
// this pool with it.
424
Map ht = new HashMap();
425
426     if (props.get("pool.useSyslog") != null)
427     {
428       ht.put("pool.useSyslog", Boolean.valueOf(props.getProperty("pool.useSyslog")));
429     }
430
431     if (props.get("jdbc.driver") != null)
432     {
433       ht.put("jdbc.driver", props.getProperty("jdbc.driver"));
434     }
435     else
436     {
437       throw new PoolException(MessageFormat.format(
438         PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_PROP_MESSAGE),
439         new Object JavaDoc[] { "jdbc.driver" }));
440     }
441
442     if (props.get("jdbc.URL") != null)
443     {
444       ht.put("jdbc.URL", props.getProperty("jdbc.URL"));
445     }
446     else
447     {
448       throw new PoolException(MessageFormat.format(
449         PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_PROP_MESSAGE),
450         new Object JavaDoc[] { "jdbc.URL" }));
451     }
452
453     if (props.get("pool.maxCheckoutRefreshAttempts") != null)
454     {
455       try
456       {
457         ht.put("pool.maxCheckoutRefreshAttempts",
458           new Integer JavaDoc(
459             Integer.parseInt(
460               props.getProperty("pool.maxCheckoutRefreshAttempts"))));
461       }
462       catch (NumberFormatException JavaDoc x)
463       {
464         throw new PoolException(MessageFormat.format(
465           PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_INT_PROP_MESSAGE),
466           new Object JavaDoc[] { "pool.maxCheckoutRefreshAttempts" }));
467       }
468     }
469
470     if (props.get("pool.checkoutRefreshWaitTime") != null)
471     {
472       try
473       {
474         ht.put("pool.checkoutRefreshWaitTime",
475           new Integer JavaDoc(
476             Integer.parseInt(
477               props.getProperty("pool.checkoutRefreshWaitTime"))));
478       }
479       catch (NumberFormatException JavaDoc x)
480       {
481         throw new PoolException(MessageFormat.format(
482           PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_INT_PROP_MESSAGE),
483           new Object JavaDoc[] { "pool.checkoutRefreshWaitTime" }));
484       }
485     }
486
487     ht.put("jdbc.properties", jdbcProps);
488
489     if (props.get("jdbc.validityCheckStatement") != null)
490     {
491       ht.put("jdbc.validityCheckStatement",
492         props.getProperty("jdbc.validityCheckStatement"));
493     }
494
495     if (props.get("pool.syslogChannelList") != null)
496     {
497       ht.put("pool.syslogChannelList",
498         props.getProperty("pool.syslogChannelList"));
499     }
500
501     String JavaDoc tmp = props.getProperty("pool.verboseValidate");
502     boolean verboseValidate = false;
503     if (tmp != null && tmp.equalsIgnoreCase("true"))
504       verboseValidate = true;
505     ht.put("pool.verboseValidate", new Boolean JavaDoc(verboseValidate));
506
507     if (props.get("pool.refreshThreadCheckInterval") != null)
508     {
509       int i = Integer.parseInt(props.getProperty("pool.refreshThreadCheckInterval"));
510       String JavaDoc verboseString = props.getProperty("pool.verboseRefresh");
511       boolean verboseRefresh = false;
512       if (verboseString != null && verboseString.equalsIgnoreCase("true"))
513         verboseRefresh = true;
514       ht.put("pool.verboseRefresh", new Boolean JavaDoc(verboseRefresh));
515       if (i > 0)
516       {
517         if (props.get("jdbc.validityCheckStatement") == null)
518         {
519           throw new PoolException(MessageFormat.format(
520             PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_IF_PROP_MESSAGE),
521             new Object JavaDoc[] { "jdbc.validityCheckStatement", "pool.refreshThreadCheckInterval" }));
522         }
523       }
524       ht.put("pool.refreshThreadCheckInterval", new Integer JavaDoc(i));
525     }
526
527     if (props.get("pool.initialSize") != null)
528     {
529       ht.put("pool.initialSize",
530         new Integer JavaDoc(props.getProperty("pool.initialSize")));
531     }
532
533     if (props.get("pool.maxSize") != null)
534     {
535       ht.put("pool.maxSize",
536         new Integer JavaDoc(props.getProperty("pool.maxSize")));
537     }
538
539     if (props.get("pool.growBlock") != null)
540     {
541       ht.put("pool.growBlock",
542         new Integer JavaDoc(props.getProperty("pool.growBlock")));
543     }
544
545     if (props.get("pool.createWaitTime") != null)
546     {
547       ht.put("pool.createWaitTime",
548         new Integer JavaDoc(props.getProperty("pool.createWaitTime")));
549     }
550
551     if (props.get("pool.maidThreadCheckInterval") != null)
552     {
553       ht.put("pool.maidThreadCheckInterval",
554         new Integer JavaDoc(props.getProperty("pool.maidThreadCheckInterval")));
555     }
556
557     if (props.get("pool.maxConnectionIdleTime") != null)
558     {
559       ht.put("pool.maxConnectionIdleTime",
560         new Integer JavaDoc(props.getProperty("pool.maxConnectionIdleTime")));
561     }
562
563     if (props.get("pool.validateOnCheckout") != null)
564     {
565       ht.put("pool.validateOnCheckout",
566         new Boolean JavaDoc(props.getProperty("pool.validateOnCheckout")));
567     }
568
569     // create the pool
570
init(ht);
571   }
572
573
574   /**
575    * Create a new JDBC connection pool with the given name. You
576    * must call init(...) before using it.
577    *
578    * @param name The name of the pool.
579    * @see #init
580    */

581   public JdbcConnectionPool(String JavaDoc name)
582   {
583     super();
584     this.poolName = name;
585     JdbcConnectionPoolDriver.registerPool(this);
586   }
587
588   /**
589    * Remove this pool from the JdbcConnectionPoolDriver's
590    * list of known pools.
591    */

592   public void unRegisterPool()
593   {
594     JdbcConnectionPoolDriver.unRegisterPool(this);
595   }
596
597   /**
598    * Get the name of this pool.
599    */

600   public String JavaDoc getName()
601   {
602     return this.poolName;
603   }
604
605   /**
606    * Shutdown all the connections that this pool has open
607    * and checked in. This should only be done as part
608    * of a system shutdown of some kind.
609    */

610   public void closeAllConnections()
611   {
612     if (DEBUG.debug())
613         log.debug(this, "Closing connections in pool " + poolName);
614     List pool = getPool();
615     synchronized (getSyncObject())
616     {
617       Iterator i = pool.iterator();
618       while (i.hasNext())
619       {
620         ((JdbcConnectionPoolConnection)i.next()).reallyClose();
621       }
622     }
623   }
624
625   /**
626    * Refresh the connections. This will call <tt>refresh()</tt> on all
627    * the connections that are currently checked-in. If there are SQLExceptions
628    * thrown while refreshing connections, the last one is thrown back. Any of
629    * the connections that fail the refresh are removed from the pool.
630    * If <tt>verbose</TT> is true, messages are written to Syslog
631    * about the refresh operation.
632    *
633    * @see JdbcConnectionPoolConnection#refresh
634    * @exception SQLException If there is a problem refreshing connections.
635    */

636   public void refreshConnections(boolean verbose)
637   throws SQLException
638   {
639     if (DEBUG.debug())
640         log.debug(this, "Refreshing connections in pool " + poolName);
641     SQLException x = null;
642     synchronized (getSyncObject())
643     {
644       List pool = getPool();
645       // put everything from the pool into another vector.
646
List newPool = new ArrayList();
647       Iterator i = pool.iterator();
648       while (i.hasNext())
649         newPool.add(i.next());
650
651       // remove everything from the pool and add them back as we
652
// refresh them.
653
pool.clear();
654       i = newPool.iterator();
655       while (i.hasNext())
656       {
657         JdbcConnectionPoolConnection c = (JdbcConnectionPoolConnection)i.next();
658         if (DEBUG.debug())
659             log.debug(this, "Refreshing connection: " + c);
660         boolean done = false;
661         try
662         {
663           c.refresh(verbose);
664           pool.add(c);
665           done = true;
666         }
667         catch (SQLException sx)
668         {
669           if (DEBUG.debug())
670             log.debug(this, "Caught SQLException refreshing connection: " + sx.toString());
671           x = sx;
672           c.deleteObjectPoolObject();
673         }
674       }
675       getSyncObject().notifyAll();
676     }
677     if (x != null)
678     {
679       throw x;
680     }
681   }
682
683   /**
684    * Performs a non-verbose refresh of the connections.
685    *
686    * @see #refreshConnections(boolean)
687    */

688   public void refreshConnections()
689   throws SQLException
690   {
691     refreshConnections(false);
692   }
693
694   /**
695    * Initialize the pool.
696    * Reads the following from the Map:<p>
697    * <dl>
698    *
699    * <dt><tt>jdbc.driver</tt> (String)</dt>
700    * <dd>The name of the JDBC driver class to use</dd>
701    *
702    * <dt><tt>jdbc.URL</tt> (String)</dt>
703    * <dd>The URL to use for the underlying. JDBC connections.</dd>
704    *
705    * <dt><tt>jdbc.properties</tt> (java.util.Properties)</dt>
706    * <dd>Properties for the connection. Should include at
707    * least "user" and "password" -- see
708    * <tt>DriverManager.getConnection(String, Properties)</tt>
709    * for what the properties should include.</dd>
710    *
711    * <dt><tt>pool.syslogChannelList</tt> (String)</dt>
712    * <dd>A comma-separated list of channel names to
713    * log messages to. The symbolic names
714    * <TT>Syslog.DEFAULT_CHANNEL</TT> and <TT>Syslog.ALL_CHANNEL</TT>
715    * are understood.
716    * </dd>
717    *
718    * <dt><tt>jdbc.validityCheckStatement</tt> (String)</dt>
719    * <dd>A SQL statement that is guaranteed to return at
720    * least 1 row. For Oracle, this is "<tt>select 1
721    * from dual</tt>" and for Sybase it is "<tt>select 1</tt>".
722    * This statement is used as a means of checking that a
723    * connection is indeed working.</dd>
724    *
725    * <dt><tt>pool.refreshThreadCheckInterval</tt> (Integer)</dt>
726    * <dd>If present and &gt; 0, this is the number of seconds for
727    * a low-priority thread to sleep between calls to refreshConnections()
728    * on this pool. If this option is used, you <b>must</b> specify
729    * the <tt>jdbc.validityCheckStatement</tt> option also.
730    * </dd>
731    *
732    * <dt><tt>pool.verboseRefresh</tt> (Boolean)</dt>
733    * <dd>If present and true, the refresh thread will write
734    * messages using Syslog when it refreshes connections.
735    * </dd>
736    *
737    * <dt><tt>pool.verboseValidate</tt> (Boolean)</dt>
738    * <dd>If present and true, operations while validating
739    * connections before checkout will be logged to Syslog.
740    * </dd>
741    *
742    * <dt><tt>pool.maxConnectionIdleTime</tt> (Integer)</dt>
743    * <dd>If this property is present, and the <tt>pool.maidThreadCheckInterval</tt>
744    * property is also present, then a thread will be created that
745    * looks for connections that have been idle for more than
746    * <tt>pool.maxConnectionIdleTime</tt> seconds. When this thread
747    * finds them, it closed the connection and logs a warning using
748    * the <tt>Syslog</tt> service with a stack trace of when the connection
749    * was checked out of the pool. This is primarily here as a debugging
750    * aid for finding places where connections are not getting close, and
751    * <i><b>should not</b></i> be used in a production environment.
752    * </dd>
753    *
754    * <dt><tt>pool.maidThreadCheckInterval</tt> (Integer)</dt>
755    * <dd>This is the number of seconds between attempts by the
756    * maid thread (if present) to find idle connections.
757    * </dd>
758    *
759    * <dt><tt>pool.validateOnCheckout</tt> (Boolean)</dt>
760    * <dd>Determine if we should only hand out validated connections
761    * or not. If set to true, each connection is tested with the
762    * <TT>pool.validityCheckStatement</TT> prior to being returned.
763    * Default is false.
764    * </dd>
765    *
766    * <dt><tt>pool.checkoutRefreshWaitTime</tt> (Integer)</dt>
767    * <dd>The number of milliseconds to wait between attempts
768    * to refresh a connection when checking it out from the pool.
769    * The default is 500ms. Setting this to 0 will cause no
770    * delay. This setting only comes into play if
771    * <tt>pool.validateOnCheckout</tt> is set to <tt>true</tt>.
772    * </dd>
773    *
774    * <dt><tt>pool.refreshThreadCheckInterval</tt> (Integer)</dt>
775    * <dd>If present and &gt; 0, this is the number of seconds for
776    * a low-priority thread to sleep between calls to refreshConnections()
777    * on this pool. If this option is used, you <b>must</b> specify
778    * the <tt>jdbc.validityCheckStatement</tt> option also.
779    * </dd>
780    *
781    * </dl><P>
782    *
783    * The other options listed above are read by this class's superclass,
784    * <tt>{@link com.protomatter.pool.GrowingObjectPool com.protomatter.pool.GrowingObjectPool}</tt>.<P>
785    *
786    * This method is called by the constructor that takes a String and
787    * a Map. Calling this method multiple times will have no
788    * effect on the pool since all but the first call are ignored.<P>
789    *
790    * @see com.protomatter.pool.GrowingObjectPool#init
791    * @exception Exception If there is a problem initializing the pool.
792    */

793   public void init(Map args)
794   throws Exception JavaDoc
795   {
796     // don't initialize twice
797
if (this.initialized == true)
798       return;
799     this.initialized = true;
800
801     if (DEBUG.debug())
802     {
803         log.debug(this, "Initializing new JDBC connection pool:");
804         Iterator it = args.keySet().iterator();
805         while (it.hasNext())
806         {
807             Object JavaDoc key = it.next();
808             log.debug(this, " " + key + " = " + args.get(key));
809         }
810     }
811     
812     if (args.get("pool.useSyslog") != null)
813       this.useSyslog = ((Boolean JavaDoc)args.get("pool.useSyslog")).booleanValue();
814
815     // the max number of connections is a licensed property
816
int maxSize = -1;
817     if (args.get("pool.maxSize") != null)
818       maxSize = ((Integer JavaDoc)args.get("pool.maxSize")).intValue();
819
820     this.driverName = (String JavaDoc)args.get("jdbc.driver");
821     try
822     {
823       // load the driver
824
DatabaseUtil.registerDriver(driverName);
825       driver = (Driver)Class.forName(driverName).newInstance();
826     }
827     catch (Exception JavaDoc x)
828     {
829       if (useSyslog)
830         Syslog.log(this, x);
831       throw new SQLException(MessageFormat.format(
832         PoolResources.getResourceString(MessageConstants.CANNOT_LOAD_DRIVER_MESSAGE),
833         new Object JavaDoc[] { driverName, x.toString() }));
834     }
835     this.url = (String JavaDoc)args.get("jdbc.URL");
836     this.checkStatement = (String JavaDoc)args.get("jdbc.validityCheckStatement");
837     this.props = (Properties)args.get("jdbc.properties");
838
839     if (args.get("pool.maxCheckoutRefreshAttempts") != null)
840     {
841       this.maxCheckoutRefreshAttempts = ((Integer JavaDoc)args.get("pool.maxCheckoutRefreshAttempts")).intValue();
842     }
843     if (args.get("pool.checkoutRefreshWaitTime") != null)
844     {
845       this.checkoutRefreshWaitTime = ((Integer JavaDoc)args.get("pool.checkoutRefreshWaitTime")).intValue();
846     }
847
848     super.init(args);
849
850     Boolean JavaDoc verboseValidate = (Boolean JavaDoc)args.get("pool.verboseValidate");
851     if (verboseValidate != null)
852       this.verboseValidate = verboseValidate.booleanValue();
853
854     if (args.get("pool.refreshThreadCheckInterval") != null)
855     {
856       Boolean JavaDoc verboseFlag = (Boolean JavaDoc)args.get("pool.verboseRefresh");
857       boolean verbose = false;
858       if (verboseFlag != null)
859         verbose = verboseFlag.booleanValue();
860
861       if (this.checkStatement == null)
862         throw new IllegalArgumentException JavaDoc(MessageFormat.format(
863           PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_IF_PROP_MESSAGE),
864           new Object JavaDoc[] { "jdbc.validityCheckStatement", "pool.refreshThreadCheckInterval" }));
865       int sleepTime = ((Integer JavaDoc)args.get("pool.refreshThreadCheckInterval")).intValue() * 1000;
866       if (sleepTime > 0)
867       {
868         refreshThread = new RefreshThread(this, sleepTime, verbose);
869         refreshThread.start();
870       }
871     }
872
873     if (args.get("pool.maxConnectionIdleTime") != null)
874     {
875       maxConnectionIdleTime = ((Integer JavaDoc)args.get("pool.maxConnectionIdleTime")).intValue();
876     }
877
878     if (args.get("pool.syslogChannelList") != null)
879     {
880       String JavaDoc tmp = (String JavaDoc)args.get("pool.syslogChannelList");
881       StringTokenizer st = new StringTokenizer(tmp, ", ");
882       Vector list = new Vector();
883       while (st.hasMoreTokens())
884         list.add(st.nextToken());
885       setSyslogChannelList(list);
886     }
887
888     if (args.get("pool.validateOnCheckout") != null)
889     {
890       validateOnCheckout = ((Boolean JavaDoc)args.get("pool.validateOnCheckout")).booleanValue();
891     }
892
893     if (args.get("pool.maidThreadCheckInterval") != null)
894     {
895       if (this.maxConnectionIdleTime == 0)
896         throw new IllegalArgumentException JavaDoc(MessageFormat.format(
897           PoolResources.getResourceString(MessageConstants.MUST_SPECIFY_LESS_THAN_IF_PROP_MESSAGE),
898           new Object JavaDoc[] { "pool.maxConnectionIdleTime", "0", "pool.maidThreadCheckInterval" }));
899       int sleepTime = ((Integer JavaDoc)args.get("pool.maidThreadCheckInterval")).intValue() * 1000;
900       if (sleepTime > 0)
901       {
902         maidThread = new MaidThread(this, sleepTime);
903         maidThread.start();
904       }
905     }
906   }
907
908   /**
909    * Look for idle connections that are checked out and have
910    * not been used in a while.
911    */

912   void performMaidCheck()
913   {
914     synchronized (getSyncObject())
915     {
916         try
917         {
918           if (DEBUG.debug())
919           {
920             log.debug(this, MessageFormat.format(
921                PoolResources.getResourceString(MessageConstants.LOOKING_FOR_IDLE_CONNECTIONS_MESSAGE),
922                new Object JavaDoc[] { getName() }));
923           }
924           else
925           {
926             PrintWriter writer = DriverManager.getLogWriter();
927             if (writer != null)
928             {
929               writer.println("JdbcConnectionPool: " +
930                 MessageFormat.format(
931                   PoolResources.getResourceString(MessageConstants.LOOKING_FOR_IDLE_CONNECTIONS_MESSAGE),
932                   new Object JavaDoc[] { getName() }));
933             }
934           }
935
936           List checkedOutObjects = getCheckedOutObjects();
937           Iterator i = checkedOutObjects.iterator();
938           while (i.hasNext())
939           {
940             JdbcConnectionPoolConnection c = (JdbcConnectionPoolConnection)i.next();
941             long now = System.currentTimeMillis();
942             long idleTime = (int)((now - c.getLastTimeUsed())/1000);
943             if (idleTime > maxConnectionIdleTime)
944             {
945               // connection has been idle too long.
946
if (useSyslog)
947               {
948                 Syslog.warning(this, MessageFormat.format(
949                   PoolResources.getResourceString(MessageConstants.CLOSING_IDLE_CONNECTION_MESSAGE),
950                   new Object JavaDoc[] { String.valueOf(idleTime), c.toString() }));
951                 Syslog.warning(this, PoolResources.getResourceString(MessageConstants.CONNECTION_CHECKOUT_MESSAGE), c.getCheckoutStackTrace());
952               }
953               else
954               {
955                 PrintWriter pw = DriverManager.getLogWriter();
956                 if (pw != null)
957                 {
958                   pw.println("JdbcConnectionPool: " + MessageFormat.format(
959                     PoolResources.getResourceString(MessageConstants.CLOSING_IDLE_CONNECTION_MESSAGE),
960                     new Object JavaDoc[] { String.valueOf(idleTime), c.toString() }));
961                   pw.println("JdbcConnectionPool: "
962                     + PoolResources.getResourceString(MessageConstants.CONNECTION_CHECKOUT_MESSAGE));
963                   c.getCheckoutStackTrace().printStackTrace(pw);
964                 }
965               }
966
967               try
968               {
969                 c.close();
970               }
971               catch (Exception JavaDoc x)
972               {
973                 if (useSyslog)
974                 {
975                   Syslog.error(this, PoolResources.getResourceString(MessageConstants.EXCEPTION_CLOSE_CONNECTION_MESSAGE), x);
976                 }
977                 else
978                 {
979                   PrintWriter pw = DriverManager.getLogWriter();
980                   if (pw != null)
981                   {
982                     pw.println("JdbcConnectionPool: "
983                       + PoolResources.getResourceString(MessageConstants.EXCEPTION_CLOSE_CONNECTION_MESSAGE));
984                     x.printStackTrace(pw);
985                   }
986                 }
987               }
988             }
989           }
990         }
991         catch (Exception JavaDoc x)
992         {
993           if (useSyslog)
994           {
995             Syslog.error(this, MessageFormat.format(
996                PoolResources.getResourceString(MessageConstants.MAID_EXCEPTION_MESSAGE),
997                new Object JavaDoc[] { getName() }), x);
998           }
999           else
1000          {
1001            PrintWriter pw = DriverManager.getLogWriter();
1002            if (pw != null)
1003            {
1004              pw.println("JdbcConnectionPool: " + MessageFormat.format(
1005                 PoolResources.getResourceString(MessageConstants.MAID_EXCEPTION_MESSAGE),
1006                 new Object JavaDoc[] { getName() }));
1007              x.printStackTrace(pw);
1008            }
1009          }
1010        }
1011    }
1012  }
1013
1014  /**
1015   * Get the underlying JDBC driver instance.
1016   */

1017  Driver getDriver()
1018  {
1019    return this.driver;
1020  }
1021
1022  /**
1023   * Used internally for growing the pool.
1024   *
1025   * @exception SQLException If there is a problem creating a new connection.
1026   */

1027  protected ObjectPoolObject createObjectPoolObject()
1028  throws SQLException
1029  {
1030    return new JdbcConnectionPoolConnection(this, url, props);
1031  }
1032
1033  /**
1034   * Get the statement set as the validity check statement.
1035   *
1036   * @see #init
1037   */

1038  public String JavaDoc getValidityCheckStatement()
1039  {
1040    return this.checkStatement;
1041  }
1042
1043  /**
1044   * Determine if connections should be validated on checkout.
1045   */

1046  boolean getValidateOnCheckout()
1047  {
1048    return this.validateOnCheckout;
1049  }
1050
1051  /**
1052   * Determine if connection validation should be verbose.
1053   */

1054  boolean getVerboseValidate()
1055  {
1056    return this.verboseValidate;
1057  }
1058
1059  /**
1060   * Destroy this connection pool.
1061   */

1062  public void destroy()
1063  {
1064    unRegisterPool();
1065
1066    if (refreshThread != null)
1067    {
1068      if (refreshThread.isAlive())
1069      {
1070        refreshThread.stopRunning();
1071        refreshThread = null;
1072      }
1073    }
1074
1075    if (maidThread != null)
1076    {
1077      if (maidThread.isAlive())
1078      {
1079        maidThread.stopRunning();
1080        maidThread = null;
1081      }
1082    }
1083
1084    closeAllConnections();
1085  }
1086
1087  /**
1088   * A thread that sits in the background and refreshes connections.
1089   */

1090  class RefreshThread
1091  extends Thread JavaDoc
1092  implements SyslogChannelAware
1093  {
1094    private JdbcConnectionPool pool = null;
1095    private int sleepTime;
1096    private boolean verbose = false;
1097
1098    public Object JavaDoc getSyslogChannel()
1099    {
1100        if (pool != null)
1101            return pool.getSyslogChannel();
1102        return null;
1103    }
1104
1105    public void stopRunning()
1106    {
1107        this.pool = null;
1108    }
1109
1110    public RefreshThread(JdbcConnectionPool pool, int sleepTime, boolean verbose)
1111    {
1112      super("JdbcConnectionPoolRefreshThread[poolname=" + pool.getName() + "]");
1113      setPriority(Thread.MIN_PRIORITY);
1114      setDaemon(true); // VM should not wait for this thread to die.
1115
this.pool = pool;
1116      this.sleepTime = sleepTime;
1117      this.verbose = verbose;
1118    }
1119
1120    public void run()
1121    {
1122      while (pool != null)
1123      {
1124        try
1125        {
1126          sleep(sleepTime);
1127        }
1128        catch (InterruptedException JavaDoc x)
1129        {
1130          ;
1131        }
1132        try
1133        {
1134          if (verbose && pool != null)
1135          {
1136            if (useSyslog)
1137            {
1138              Syslog.info(this, MessageFormat.format(
1139                 PoolResources.getResourceString(MessageConstants.REFRESHING_CONNECTIONS_MESSAGE),
1140                 new Object JavaDoc[] { pool.getName() }));
1141            }
1142            else
1143            {
1144              PrintWriter writer = DriverManager.getLogWriter();
1145              if (writer != null)
1146              {
1147                writer.println(MessageFormat.format(
1148                  PoolResources.getResourceString(MessageConstants.REFRESHING_CONNECTIONS_MESSAGE),
1149                  new Object JavaDoc[] { pool.getName() }));
1150              }
1151            }
1152          }
1153          if (pool != null)
1154            pool.refreshConnections(verbose);
1155        }
1156        catch (Exception JavaDoc x)
1157        {
1158          //Syslog.log(this, "Exception refreshing pool \"" + pool.getName() + "\"", x, Syslog.ERROR);
1159
}
1160      }
1161    }
1162  }
1163
1164  /**
1165   * Looks for connections that have been idle for a long time
1166   * closes them. Also logs a stack trace of where the connection
1167   * was checked out. This is primarily for debugging, and should
1168   * probably not be used in production.
1169   */

1170  class MaidThread
1171  extends Thread JavaDoc
1172  implements SyslogChannelAware
1173  {
1174    private JdbcConnectionPool pool = null;
1175    private int sleepTime;
1176
1177    public MaidThread(JdbcConnectionPool pool, int sleepTime)
1178    {
1179      super("JdbcConnectionPoolMaidThread[poolname=" + pool.getName() + "]");
1180      setPriority(Thread.MIN_PRIORITY);
1181      setDaemon(true); // VM should not wait for this thread to die.
1182
this.pool = pool;
1183      this.sleepTime = sleepTime;
1184    }
1185
1186    public Object JavaDoc getSyslogChannel()
1187    {
1188        if (pool != null)
1189            return pool.getSyslogChannel();
1190        return null;
1191    }
1192
1193    public void stopRunning()
1194    {
1195      this.pool = null;
1196    }
1197
1198    public void run()
1199    {
1200      while (pool != null)
1201      {
1202        try
1203        {
1204          sleep(sleepTime);
1205        }
1206        catch (InterruptedException JavaDoc x)
1207        {
1208          ;
1209        }
1210        if (pool != null)
1211            pool.performMaidCheck();
1212      }
1213    }
1214  }
1215}
1216
Popular Tags