001: /*
002: DBPool - JDBC Connection Pool Manager
003: Copyright (c) Giles Winstanley
004: */
005: package snaq.db;
006:
007: import java.io.*;
008: import java.sql.*;
009: import java.text.*;
010: import java.util.*;
011: import snaq.util.LogUtil;
012:
013: /**
014: * <p>Class to provide access and management for multiple connection pools
015: * defined in a properties file or object.
016: * Clients get access to each defined instance through one of the
017: * static <tt>getInstance()</tt> methods and can then check-out and check-in
018: * database connections from the pools defined by that manager.</p>
019: *
020: * <p>Each successful call to a <tt>getInstance()</tt> method also increments
021: * an internal counter which keeps a record of the number of clients which hold
022: * a reference to that particular pool manager. When each client has finished
023: * using a pool manager it should call the <tt>release()</tt> method to
024: * let the manager know that it is no longer needed by that client, so it can
025: * clean up it's resources as necessary. The resources will not be released
026: * until the clients counter has reached zero. <em>It is therefore necessary to
027: * allocate and release your pool manager references carefully</em>.</p>
028: *
029: * <p>Properties for a manager can be specified in three different ways.
030: * <ol>
031: * <li>Properties file located in CLASSPATH
032: * <li>Properties file referenced explicitly (with a File object)
033: * <li>Properties object
034: * </ol>
035: *
036: * <ol>
037: * <li>A CLASSPATH located properties file can simply be accessed using the
038: * method <tt>getInstance(name)</tt> where <em>name</em> is the name of the
039: * properties file specified as a string.
040: * <li>To specify a properties file which is not in the CLASSPATH use the
041: * method <tt>getInstance(File)</tt>. This same file handle must be used
042: * each time you want to obtain the instance in this way.
043: * <li>To specify the pools using a Properties object a call must be made to
044: * the <tt>createInstance(Properties)</tt> method. This method creates the
045: * ConnectionPoolManager instance and makes it available via the <tt>getInstance()</tt>
046: * method.
047: * </ol>
048: * <p><b>Note:</b> The <tt>getInstance()</tt> method can return one of two
049: * possible instances depending on the previous calls made to the pool manager.
050: * If the <tt>createInstance(Properties)</tt> method has previously been
051: * called successfully then it will return this manually created instance.
052: * Otherwise it will attempt to return an instance relating to the default
053: * properties file (dbpool.properties) within the CLASSPATH, if it exists.</p>
054: *
055: * <p>The properties given to the manager specify which JDBC drivers to use to
056: * access the relevant databases, and also defines the characteristics of each
057: * connection pool. The properties required/allowed are as follows
058: * (those marked with * are mandatory):</p>
059: * <pre>
060: * drivers* Class names of required JDBC Drivers (comma/space delimited)
061: * logfile* Filename of logfile
062: *
063: * <poolname>.url* The JDBC URL for the database
064: * <poolname>.user Database username for login
065: * <poolname>.password Database password for login
066: * <poolname>.maxpool The maximum number of pooled connections (0 if none)
067: * <poolname>.maxconn The maximum number of possible connections (0 if no limit)
068: * <poolname>.expiry The connection expiry time in seconds (0 if no expiry)
069: * <poolname>.init The initial number of connections to create (default:0)
070: * <poolname>.validator Class name of connection validator (optional)
071: * <poolname>.decoder Class name of password decoder (optional)
072: * <poolname>.cache Whether to cache Statements (optional, default:true)
073: * <poolname>.debug Whether to log debug info (optional, default:false)
074: * <poolname>.prop.<em>XXX</em> Passes property <em>XXX</em> and it's value to the JDBC driver
075: * <poolname>.async Whether to use asynchronous connection destruction (optional, default:false)
076: * <poolname>.logfile Filename of logfile for this pool (optional)
077: * <poolname>.dateformat SimpleDateFormat formatting string for log entries (optional)
078: * </pre>
079: *
080: * <p>Multiple pools can be specified provided they each use a different pool name.
081: * The <tt>validator</tt> property optionally specifies the name of a
082: * class to be used for validating the database connections. The default
083: * connection validation simply performs a test using <tt>Connection.isClosed()</tt>.
084: * This test is not 100% reliable as the Java API documentation specifies that
085: * it only returns true if the connection has been explicitly closed.
086: * If more rigorous connection validation is required you can either use the
087: * provided class <tt>snaq.db.AutoCommitValidator</tt> or write your own
088: * validation class which should implement the <tt>ConnectionValidator</tt>
089: * interface.</p>
090: *
091: * @see snaq.db.AutoCommitValidator
092: * @see snaq.db.ConnectionValidator
093: * @author Giles Winstanley
094: */
095: public final class ConnectionPoolManager extends LogUtil implements
096: Comparable {
097: private static final String PROPERTIES_INSTANCE_KEY = "PROPERTIES_INSTANCE";
098: private static final String DEFAULT_PROPERTIES_FILE = "/dbpool.properties";
099: private static Hashtable managers = new Hashtable();
100:
101: private static ArrayList drivers = new ArrayList();
102: private boolean released = false;
103: private HashMap pools = new HashMap();
104: protected int clients;
105: private Object source, key;
106:
107: private ConnectionPoolManager(Properties props, Object src) {
108: super ();
109: this .source = src;
110: init(props);
111: }
112:
113: /** Returns a descriptive string for this instance. */
114: public String toString() {
115: if (source instanceof String)
116: return "ConnectionPoolManager [CLASSPATH resource:"
117: + source + "]";
118: else if (source instanceof File)
119: return "ConnectionPoolManager [File:"
120: + ((File) source).getAbsolutePath() + "]";
121: else if (source instanceof Properties)
122: return "ConnectionPoolManager [Properties]";
123: else
124: return "ConnectionPoolManager [Unknown]";
125: }
126:
127: /** Compares this instances to other instances by name. */
128: public int compareTo(Object o) {
129: return this .toString().compareTo(
130: ((ConnectionPoolManager) o).toString());
131: }
132:
133: /**
134: * Returns an enumeration of all the available ConnectionPoolManager instances.
135: * This method is included for convenience for external monitoring.
136: * Clients wanting to obtain an instance for using connections should NOT use
137: * this method.
138: * @deprecated Replaced by <tt>getInstances()</tt>, which returns a <tt>Set</tt>
139: */
140: public static Enumeration instances() {
141: return Collections.enumeration(getInstances());
142: }
143:
144: /**
145: * Returns a Set containing all the current ConnectionPoolManager instances.
146: * This method is included for convenience for external monitoring.
147: * Clients wanting to obtain an instance for using connections should NOT use
148: * this method.
149: * @return all current instances of ConnectionPoolManager.
150: */
151: public static Set getInstances() {
152: Set x = new HashSet();
153: x.addAll(managers.values());
154: return x;
155: }
156:
157: /**
158: * Returns the singleton instance of the ConnectionPoolManager for the specified properties file.
159: * @param propsFile filename of the properties file to use (path info should not be specified; available CLASSPATH will be searched for the properties file)
160: * @return instance of ConnectionPoolManager relating to the specified properties file
161: * @throws IOException if there was an problem loading the properties
162: */
163: public static synchronized ConnectionPoolManager getInstance(
164: String propsFile) throws IOException {
165: String s = propsFile.startsWith("/") ? propsFile
166: : ("/" + propsFile);
167: Object o = managers.get(s);
168: ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager) o
169: : null;
170: if (cpm == null || cpm.isReleased()) {
171: cpm = new ConnectionPoolManager(loadProperties(s),
172: propsFile);
173: cpm.key = s;
174: managers.put(cpm.key, cpm);
175: }
176: cpm.clients++;
177: return cpm;
178: }
179:
180: /**
181: * Returns the singleton instance of the ConnectionPoolManager for the specified properties file.
182: * @param propsFile filename of the properties file to use (path info should not be specified; available CLASSPATH will be searched for the properties file)
183: * @return instance of ConnectionPoolManager relating to the specified properties file
184: * @throws IOException if there was an problem loading the properties
185: */
186: public static synchronized ConnectionPoolManager getInstance(
187: File propsFile) throws IOException {
188: Object o = managers.get(propsFile);
189: ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager) o
190: : null;
191: if (cpm == null || cpm.isReleased()) {
192: try {
193: cpm = new ConnectionPoolManager(
194: loadProperties(propsFile), propsFile);
195: cpm.key = propsFile;
196: managers.put(cpm.key, cpm);
197: } catch (IOException ioe) {
198: if (ioe instanceof FileNotFoundException)
199: System.err
200: .println("Unable to find the properties file "
201: + propsFile.getAbsolutePath());
202: else
203: System.err
204: .println("Error loading the properties file "
205: + propsFile.getAbsolutePath());
206: ioe.printStackTrace();
207: return null;
208: }
209: }
210: cpm.clients++;
211: return cpm;
212: }
213:
214: /**
215: * Returns the standard singleton instance of the ConnectionPoolManager.
216: * If an instance has been obtained with a user-specified Properties object
217: * then this instance is returned, otherwise an attempt is made to return an
218: * instance using the default properties file (dbpool.properties).
219: * @throws IOException if there was an problem loading the properties
220: */
221: public static synchronized ConnectionPoolManager getInstance()
222: throws IOException {
223: Object o = managers.get(PROPERTIES_INSTANCE_KEY);
224: ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager) o
225: : null;
226: if (cpm != null && !cpm.released)
227: cpm.clients++;
228: else
229: cpm = getInstance(DEFAULT_PROPERTIES_FILE);
230:
231: return cpm;
232: }
233:
234: /**
235: * Creates a singleton instance of the ConnectionPoolManager for the specified
236: * Properties object. To subsequently use this instance user's should call the
237: * getInstance() method. This mechanism is used to provide the maximum
238: * separation between creation and use of this instance to avoid haphazard
239: * changes to any referenced Properties onject that may occur between calls.
240: * (This method can only be used successfully if no default properties
241: * instance exists and is in use at the time of calling.)
242: * @param props Properties object to use
243: * @throws RuntimeException if default properties instance already exists and is in use
244: */
245: public static synchronized void createInstance(Properties props) {
246: // Check for presence of default properties file instance
247: Object o = managers.get(DEFAULT_PROPERTIES_FILE);
248: ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager) o
249: : null;
250: if (cpm != null && !cpm.isReleased())
251: throw new RuntimeException(
252: "Default properties file instance already exists");
253:
254: // Create new instance and store reference
255: cpm = new ConnectionPoolManager(props, props);
256: cpm.key = PROPERTIES_INSTANCE_KEY;
257: managers.put(cpm.key, cpm);
258: }
259:
260: /**
261: * Loads and returns a Properties object from file.
262: */
263: private static Properties loadProperties(File propsFile)
264: throws IOException {
265: if (!propsFile.exists())
266: throw new FileNotFoundException(propsFile.getAbsolutePath()
267: + " does not exist");
268: if (propsFile.isDirectory())
269: throw new IOException("Error accessing properties file - "
270: + propsFile.getAbsolutePath() + " is a directory");
271: InputStream is = new FileInputStream(propsFile);
272: Properties props = new Properties();
273: props.load(is);
274: is.close();
275: return props;
276: }
277:
278: /**
279: * Loads and returns a Properties object from the resource specified..
280: * The resource should be located in the current CLASSPATH to be found.
281: * @throws IOException if there was an problem loading the properties
282: */
283: private static Properties loadProperties(String propsResource)
284: throws IOException {
285: InputStream is = ConnectionPoolManager.class
286: .getResourceAsStream(propsResource);
287: Properties props = new Properties();
288: try {
289: props.load(is);
290: } catch (IOException ioe) {
291: System.err
292: .println("Unable to load the properties file. Make sure "
293: + propsResource + " is in the CLASSPATH.");
294: ioe.printStackTrace();
295: throw ioe;
296: }
297: return props;
298: }
299:
300: /**
301: * Initializes this instance with values from the given Properties object.
302: */
303: private void init(Properties props) {
304: String logFile = props.getProperty("logfile",
305: "ConnectionPoolManager.log");
306: String df = props.getProperty("dateformat",
307: "EEE MMM dd hh:mm:ss.SSS ZZZ yyyy");
308: try {
309: setDateFormat(new SimpleDateFormat(df));
310: setLog(new FileOutputStream(logFile, true));
311: } catch (IOException e) {
312: System.err.println("Can't open the log file: " + logFile);
313: }
314: loadDrivers(props);
315: createPools(props);
316: }
317:
318: /**
319: * Loads and registers all JDBC drivers. This is done by the
320: * DBConnectionManager, as opposed to the ConnectionPool,
321: * since many pools may share the same driver.
322: * @param props the connection pool properties
323: */
324: private void loadDrivers(Properties props) {
325: String driverClasses = props.getProperty("drivers");
326: StringTokenizer st = new StringTokenizer(driverClasses,
327: ",: \t\n\r\f");
328: Enumeration current = DriverManager.getDrivers();
329: while (st.hasMoreElements()) {
330: String driverClassName = st.nextToken().trim();
331: try {
332: // Check if driver already registered
333: boolean using = false;
334: while (current.hasMoreElements()) {
335: String cName = current.nextElement().getClass()
336: .getName();
337: if (cName.equals(driverClassName))
338: using = true;
339: }
340: if (!using) {
341: Driver driver = (Driver) Class.forName(
342: driverClassName).newInstance();
343: DriverManager.registerDriver(driver);
344: drivers.add(driver);
345: log("Registered JDBC driver " + driverClassName);
346: }
347: } catch (Exception e) {
348: log("Unable to register JDBC driver: "
349: + driverClassName + ", Exception: " + e);
350: }
351: }
352: }
353:
354: /**
355: * Creates instances of ConnectionPool based on the properties.
356: * @param props the connection pool properties
357: */
358: private void createPools(Properties props) {
359: Iterator iter = props.keySet().iterator();
360: while (iter.hasNext()) {
361: String name = (String) iter.next();
362: if (name.endsWith(".url")) {
363: String poolName = name.substring(0, name
364: .lastIndexOf("."));
365: String url = props.getProperty(poolName + ".url");
366: if (url == null) {
367: log("No URL specified for " + poolName);
368: continue;
369: }
370:
371: String user = props.getProperty(poolName + ".user");
372: user = (user != null) ? user.trim() : user;
373: String pass = props.getProperty(poolName + ".password");
374: pass = (pass != null) ? pass.trim() : pass;
375: String poolSize = props.getProperty(
376: poolName + ".maxpool", "0").trim();
377: String maxSize = props.getProperty(
378: poolName + ".maxconn", "0").trim();
379: String init = props
380: .getProperty(poolName + ".init", "0").trim();
381: String expiry = props.getProperty(poolName + ".expiry",
382: "0").trim();
383: String validator = props.getProperty(poolName
384: + ".validator");
385: String decoder = props.getProperty(poolName
386: + ".decoder");
387: String logFile = props.getProperty(poolName
388: + ".logfile");
389: String dateformat = props.getProperty(poolName
390: + ".dateformat");
391: validator = (validator != null) ? validator.trim()
392: : validator;
393: boolean noCache = props.getProperty(
394: poolName + ".cache", "true").trim()
395: .equalsIgnoreCase("false");
396: boolean async = props.getProperty(poolName + ".async",
397: "false").trim().equalsIgnoreCase("true");
398: boolean poolDebug = props.getProperty(
399: poolName + ".debug", "false").trim()
400: .equalsIgnoreCase("true");
401:
402: // Construct properties object for pool if extra info supplied
403: Properties poolProps = new Properties();
404: String prefix = poolName + ".prop.";
405: Iterator it = props.keySet().iterator();
406: while (it.hasNext()) {
407: String s = (String) it.next();
408: if (s.startsWith(prefix))
409: poolProps.setProperty(s.substring(prefix
410: .length()), props.getProperty(s));
411: }
412: if (!poolProps.isEmpty() && user != null
413: && !user.equals("")) {
414: poolProps.setProperty("user", user);
415: poolProps.setProperty("password", pass);
416: } else
417: poolProps = null;
418:
419: // Validate poolsize
420: int pSize, mSize, iSize, exp;
421: try {
422: pSize = Integer.valueOf(poolSize).intValue();
423: } catch (NumberFormatException nfe) {
424: log("Invalid maxpool value " + poolSize + " for "
425: + poolName);
426: pSize = 0;
427: }
428: // Validate maxsize
429: try {
430: mSize = Integer.valueOf(maxSize).intValue();
431: } catch (NumberFormatException nfe) {
432: log("Invalid maxconn value " + maxSize + " for "
433: + poolName);
434: mSize = 0;
435: }
436: // Validate init
437: try {
438: iSize = Integer.valueOf(init).intValue();
439: } catch (NumberFormatException nfe) {
440: log("Invalid initsize value " + init + " for "
441: + poolName);
442: iSize = 0;
443: }
444: // Validate expiry
445: try {
446: exp = Integer.valueOf(expiry).intValue();
447: } catch (NumberFormatException nfe) {
448: log("Invalid expiry value " + expiry + " for "
449: + poolName);
450: exp = 0;
451: }
452:
453: // Validate pool size logic
454: pSize = Math.max(pSize, 0); // (ensure pSize >= 0)
455: mSize = Math.max(mSize, 0); // (ensure mSize >= 0)
456: if (mSize > 0) // (if mSize > 0, ensure mSize >= pSize)
457: mSize = Math.max(mSize, pSize);
458: iSize = Math.min(Math.max(iSize, 0), pSize); // (ensure 0 <= iSize <= pSize)
459: exp = Math.max(exp, 0); // (ensure exp >= 0)
460:
461: // Create connection pool
462: ConnectionPool pool = null;
463: if (poolProps != null)
464: pool = new ConnectionPool(poolName, pSize, mSize,
465: (long) (exp * 1000), url, poolProps);
466: else
467: pool = new ConnectionPool(poolName, pSize, mSize,
468: (long) (exp * 1000), url, user, pass);
469:
470: // Set custom date format, if applicable.
471: try {
472: DateFormat df = new SimpleDateFormat(dateformat);
473: pool.setDateFormat(df);
474: } catch (Exception e) {
475: log("Invalid dateformat string specified: "
476: + dateformat);
477: }
478:
479: // Setup pool logging (pool-specific if specified, otherwise generic logfile)
480: if (logFile != null && !logFile.equals("")) {
481: File f = new File(logFile);
482: if (f.exists() && f.isDirectory())
483: log("Invalid logfile specified for pool "
484: + poolName
485: + " - specified file is a directory");
486: else if (!f.exists() && !f.mkdirs())
487: log("Invalid logfile specified for pool "
488: + poolName + " - cannot create file "
489: + f.getAbsolutePath());
490: try {
491: pool.setLog(new FileOutputStream(f, true));
492: } catch (FileNotFoundException fnfe) {
493: log(fnfe, "Invalid logfile specified for pool "
494: + poolName);
495: pool.setLog(getLogStream());
496: }
497: } else
498: pool.setLog(getLogStream());
499:
500: if (poolDebug)
501: log("Enabling debug info on pool " + poolName);
502: pool.setDebug(poolDebug);
503: if (noCache)
504: log("Disabling caching on pool " + poolName);
505: pool.setCaching(!noCache);
506: if (async)
507: log("Enabling asynchronous destruction on pool "
508: + poolName);
509: pool.setAsyncDestroy(async);
510:
511: // Setup connection validator for pool
512: if (validator != null && !validator.equals("")) {
513: try {
514: Object o = Class.forName(validator)
515: .newInstance();
516: if (o instanceof ConnectionValidator)
517: pool.setValidator((ConnectionValidator) o);
518: } catch (Exception ex) {
519: log("Unable to instantiate validator class for pool "
520: + poolName + ": " + validator);
521: }
522: }
523:
524: // Setup password decoder for pool
525: if (decoder != null && !decoder.equals("")) {
526: try {
527: Object o = Class.forName(decoder).newInstance();
528: if (o instanceof PasswordDecoder)
529: pool
530: .setPasswordDecoder((PasswordDecoder) o);
531: } catch (Exception ex) {
532: log("Unable to instantiate password decoder class for pool "
533: + poolName + ": " + decoder);
534: }
535: }
536:
537: // Add new pool to collection, and show summary info
538: synchronized (pools) {
539: pools.put(poolName, pool);
540: }
541: String info = "pool=" + pool.getPoolSize() + ",max="
542: + pool.getMaxSize() + ",expiry=";
543: info += pool.getExpiryTime() == 0 ? "none" : pool
544: .getExpiryTime()
545: + "ms";
546: log("Initialized pool " + poolName + " (" + info + ")");
547:
548: // Setup initial connections in pool (spawns a thread)
549: if (iSize > 0)
550: pool.init(iSize);
551: }
552: }
553: }
554:
555: /**
556: * Returns a connection pool.
557: * (This is only provided as a convenience method to allow fine-tuning in
558: * exceptional circumstances.)
559: * @param name pool name as defined in the properties file
560: * @return the pool or null
561: */
562: public ConnectionPool getPool(String name) {
563: if (released)
564: throw new RuntimeException(
565: "Pool manager no longer valid for use");
566: return (ConnectionPool) pools.get(name);
567: }
568:
569: /**
570: * Returns all the current connection pools maintained by this manager.
571: * (This is only provided as a convenience method.)
572: * @return array of ConnectionPool objects
573: */
574: public ConnectionPool[] getPools() {
575: synchronized (pools) {
576: return (ConnectionPool[]) pools.values().toArray(
577: new ConnectionPool[0]);
578: }
579: }
580:
581: /**
582: * Returns an open connection from the specified pool.
583: * If one is not available, and the max number of connections has not been
584: * reached, a new connection is created.
585: * @param name pool name as defined in the properties file
586: * @return a connection, or null if unable to obtain one
587: */
588: public Connection getConnection(String name) throws SQLException {
589: if (released)
590: throw new RuntimeException(
591: "Pool manager no longer valid for use");
592:
593: ConnectionPool pool = (ConnectionPool) pools.get(name);
594: if (pool != null)
595: return pool.getConnection();
596: return null;
597: }
598:
599: /**
600: * Returns an open connection from the specified pool.
601: * If one is not available, and the max number of connections has not been
602: * reached, a new connection is created. If the max number has been
603: * reached, waits until one is available or the specified time has elapsed.
604: * @param name pool name as defined in the properties file
605: * @param time number of milliseconds to wait
606: * @return the connection or null
607: */
608: public Connection getConnection(String name, long time)
609: throws SQLException {
610: if (released)
611: throw new RuntimeException(
612: "Pool manager no longer valid for use");
613:
614: ConnectionPool pool = (ConnectionPool) pools.get(name);
615: if (pool != null)
616: return pool.getConnection(time);
617: return null;
618: }
619:
620: /**
621: * Releases all resources for this ConnectionPoolManager, and deregisters
622: * JDBC drivers if necessary. Any connections still in use are forcibly closed.
623: */
624: public synchronized void release() {
625: // Don't release if client still active
626: if (--clients > 0)
627: return;
628: // Set released flag to prevent check-out of new items
629: released = true;
630:
631: synchronized (pools) {
632: for (Iterator it = pools.values().iterator(); it.hasNext();) {
633: ConnectionPool pool = (ConnectionPool) it.next();
634: pool.releaseForcibly();
635: }
636: }
637:
638: // Check if drivers can be deregistered (only 1 manager left)
639: if (managers.size() == 1) {
640: for (Iterator it = drivers.iterator(); it.hasNext();) {
641: Driver driver = (Driver) it.next();
642: try {
643: DriverManager.deregisterDriver(driver);
644: log("Deregistered JDBC driver "
645: + driver.getClass().getName());
646: } catch (SQLException sqle) {
647: log(sqle, "Can't deregister JDBC driver: "
648: + driver.getClass().getName());
649: }
650: }
651: }
652: // Remove this manager from those referenced
653: managers.remove(this .key);
654:
655: // Close log
656: super .close();
657: }
658:
659: /**
660: * Returns whether this instance has been released (and therefore is unusable).
661: */
662: public synchronized boolean isReleased() {
663: return this .released;
664: }
665:
666: /**
667: * Convenience method to set the validator class for all managed connection pools.
668: * @deprecated To be removed in a future release
669: */
670: public synchronized void setValidator(ConnectionValidator cv) {
671: synchronized (pools) {
672: if (pools != null) {
673: for (Iterator it = pools.values().iterator(); it
674: .hasNext();)
675: ((ConnectionPool) it.next()).setValidator(cv);
676: }
677: }
678: }
679:
680: /**
681: * Convenience method to override LogUtil method to set log for pools.
682: * @deprecated To be removed in a future release
683: */
684: public void setLog(OutputStream out) {
685: super .setLog(out);
686: // Set log for all pools
687: synchronized (pools) {
688: if (pools != null) {
689: for (Iterator it = pools.values().iterator(); it
690: .hasNext();)
691: ((ConnectionPool) it.next()).setLog(out);
692: }
693: }
694: }
695:
696: /**
697: * Convenience method to override LogUtil method to set log for pools.
698: * @deprecated To be removed in a future release
699: */
700: public void setLog(PrintStream ps) {
701: super .setLog(ps);
702: // Set log for all pools
703: synchronized (pools) {
704: if (pools != null) {
705: for (Iterator it = pools.values().iterator(); it
706: .hasNext();)
707: ((ConnectionPool) it.next()).setLog(ps);
708: }
709: }
710: }
711: }
|