KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > db > DBConnectionPool


1 /* ====================================================================
2 * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3 *
4 * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 *
18 * 3. The end-user documentation included with the redistribution,
19 * if any, must include the following acknowledgment:
20 * "This product includes software developed by Jcorporate Ltd.
21 * (http://www.jcorporate.com/)."
22 * Alternately, this acknowledgment may appear in the software itself,
23 * if and wherever such third-party acknowledgments normally appear.
24 *
25 * 4. "Jcorporate" and product names such as "Expresso" must
26 * not be used to endorse or promote products derived from this
27 * software without prior written permission. For written permission,
28 * please contact info@jcorporate.com.
29 *
30 * 5. Products derived from this software may not be called "Expresso",
31 * or other Jcorporate product names; nor may "Expresso" or other
32 * Jcorporate product names appear in their name, without prior
33 * written permission of Jcorporate Ltd.
34 *
35 * 6. No product derived from this software may compete in the same
36 * market space, i.e. framework, without prior written permission
37 * of Jcorporate Ltd. For written permission, please contact
38 * partners@jcorporate.com.
39 *
40 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43 * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 * ====================================================================
53 *
54 * This software consists of voluntary contributions made by many
55 * individuals on behalf of the Jcorporate Ltd. Contributions back
56 * to the project(s) are encouraged when you make modifications.
57 * Please send them to support@jcorporate.com. For more information
58 * on Jcorporate Ltd. and its products, please see
59 * <http://www.jcorporate.com/>.
60 *
61 * Portions of this software are based upon other open source
62 * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.db;
66
67 import com.jcorporate.expresso.core.dataobjects.PersistenceManager;
68 import com.jcorporate.expresso.core.db.config.JDBCConfig;
69 import com.jcorporate.expresso.core.db.config.JNDIConfig;
70 import com.jcorporate.expresso.core.db.datasource.DSException;
71 import com.jcorporate.expresso.core.db.datasource.JndiDataSource;
72 import com.jcorporate.expresso.core.db.exception.ConnectionPoolException;
73 import com.jcorporate.expresso.core.db.exception.PoolFullException;
74 import com.jcorporate.expresso.core.misc.ConfigManager;
75 import com.jcorporate.expresso.core.misc.ConfigurationException;
76 import com.jcorporate.expresso.core.misc.DateTime;
77 import com.jcorporate.expresso.core.misc.StringUtil;
78 import com.jcorporate.expresso.kernel.RootContainerInterface;
79 import com.jcorporate.expresso.kernel.management.ExpressoRuntimeMap;
80 import com.jcorporate.expresso.kernel.util.ClassLocator;
81 import com.jcorporate.expresso.kernel.util.LocatorUtils;
82 import org.apache.log4j.Logger;
83
84 import java.util.ArrayList JavaDoc;
85 import java.util.Date JavaDoc;
86 import java.util.Enumeration JavaDoc;
87 import java.util.HashMap JavaDoc;
88 import java.util.Iterator JavaDoc;
89 import java.util.LinkedList JavaDoc;
90 import java.util.List JavaDoc;
91 import java.util.Map JavaDoc;
92
93
94 /**
95  * a generic database connection pooling
96  * object.</p>
97  * <p>Any object requiring connection to the database can request a connection
98  * from the connection pool, which will re-use connections and allocate
99  * new connections as required. </p>
100  * <p>A connection pool will automatically drop connections that have been idle
101  * for more than a certain number of seconds when the pool reaches it's
102  * maximum size & a new connection is required. </p>
103  * <p>It is the responsibility of the object that requests the connection
104  * to release it again as soon as possible.</p>
105  *
106  * @author Michael Nash
107  */

108 public class DBConnectionPool
109         extends Thread JavaDoc {
110     static private int nextConnectionId = 1;
111
112     /**
113      * The Log4j log.
114      */

115     private static Logger log = Logger.getLogger(DBConnectionPool.class);
116
117     /*
118     * Does this particular instance of the pool connect to
119     * a database which supports transactions?
120 */

121     private boolean supportsTransactions = false;
122
123     /**
124      * Our hash of db pools connecting to other databases
125      */

126     static private HashMap JavaDoc otherDBPools = new HashMap JavaDoc();
127
128     /**
129      * Name of this class
130      */

131     private static final String JavaDoc THIS_CLASS = DBConnectionPool.class.getName();
132
133
134     /**
135      * How many milliseconds seconds must a connection be idle before it is released?
136      */

137     private long interval = 30 * 1000;
138
139     /**
140      * Maximum time that a currently unused connection can live before it is recycled. This
141      * is irregardless of past usage.
142      */

143     private long maxttl = interval * 10;
144
145
146     /**
147      * The maximum number of retries to attempt to find a connection before
148      * we throw an exception
149      */

150     private static final int GET_CONNECTION_RETRIES = 30;
151
152     /* parameters that we need to make the connection to the database */
153     private String JavaDoc dbDriverType = null;
154
155     /**
156      * Database Driver Class
157      */

158     private String JavaDoc dbDriver = null;
159
160     /**
161      * URL to the database
162      */

163     private String JavaDoc dbURL = null;
164
165     /**
166      * Database Connection Format as per the expresso-config.xml
167      */

168     private String JavaDoc dbConnectFormat = null;
169
170     /**
171      * Database Login Name
172      */

173     private String JavaDoc dbLogin = null;
174
175     /**
176      * Password to access the database
177      */

178     private String JavaDoc dbPassword = null;
179
180     /**
181      * Name of this database connection/config key
182      */

183     private String JavaDoc dbName = "";
184
185
186     /**
187      * we hold a count of how many times we've served up a connection. When
188      * it reaches some maximum, we check the pool for "old" connections &
189      * drop 'em
190      */

191     private int issuedConnectionsCount = 0;
192
193     /**
194      * clean pool every N connections
195      */

196     private static final int CLEAN_POOL_MAX = 50;
197
198     /**
199      * start with a max of 6 connections
200      * as soon as the first connection is made, a setup value is read to give
201      * the true maximum
202      */

203     private int maxPoolSize = 6;
204
205     /**
206      * Flag to indicate if we've been initialized (e.g. given parameters
207      * to connect to the database & made the first connection) yet
208      */

209     private boolean initialized = false;
210
211     /**
212      * A vector of all of the possible wildcard characters
213      */

214     private ArrayList JavaDoc wildCards = new ArrayList JavaDoc(2);
215
216     /**
217      * Linked list of available connections to use from the connection pool
218      */

219     protected LinkedList JavaDoc available = new LinkedList JavaDoc();
220
221     /**
222      * Map of connections that are currently is use.
223      */

224     protected Map inUse = new HashMap JavaDoc(3);
225
226     /**
227      * Object that is used as a lock for both available and isUse lists
228      * in a single object.
229      */

230     protected Object JavaDoc poolLock = new Object JavaDoc();
231
232     /**
233      * object to lock finding current timestamp
234      */

235     protected Object JavaDoc timestampLock = new Object JavaDoc();
236
237     /**
238      * The query to execute against the database that defines a minimal test
239      * query that doesn't cause the target db to do a lot a work. The purpose
240      * of this is to test if the connection is still alive.
241      */

242     private String JavaDoc testQuery = null;
243
244
245     /*
246     * SQL keyword that removes duplicate rows int a query - may be specific to this
247     * database engine implementation
248 */

249     private static String JavaDoc uniqueRowKeyword = "DISTINCT";
250
251     /**
252      * Limitation syntax database vendor specific optimisation is disabled
253      * <p/>
254      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
255      */

256     public final static int LIMITATION_DISABLED = 0;
257
258     /**
259      * Insert the limitation syntax after TABLE nomination
260      * <code>
261      * SELECT {COLUMNS}... FROM {TABLE}
262      * <font color="#660066">{limitation-syntax}</font>
263      * [ WHERE {where_clause}... ]
264      * [ ORDER BY {order_by_clause}... ]
265      * </code>
266      * <p/>
267      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
268      */

269     public final static int LIMITATION_AFTER_TABLE = 1;
270
271     /**
272      * Insert the limitation syntax after WHERE key word
273      * <code>
274      * SELECT {COLUMNS}... FROM {TABLE}
275      * [ WHERE {where_clause}... ]
276      * AND <font color="#660066">{limitation-syntax}</font>
277      * [ ORDER BY {order_by_clause}... ]
278      * </code>
279      * <p/>
280      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
281      */

282     public final static int LIMITATION_AFTER_WHERE = 2;
283
284     /**
285      * Insert the limitation syntax after ORDER BY key words
286      * <code>
287      * SELECT {COLUMNS}... FROM {TABLE}
288      * [ WHERE {where_clause}... ]
289      * [ ORDER BY {order_by_clause}... ]
290      * <font color="#660066">{limitation-syntax}</font>
291      * </code>
292      * <p/>
293      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
294      */

295     public final static int LIMITATION_AFTER_ORDER_BY = 3;
296
297     /**
298      * Insert the limitation syntax after TABLE nomination
299      * <code>
300      * SELECT <font color="#660066">{limitation-syntax}</font> {COLUMNS}... FROM {TABLE}
301      * [ WHERE {where_clause}... ]
302      * [ ORDER BY {order_by_clause}... ]
303      * </code>
304      * <p/>
305      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
306      *
307      * @since Expresso 4.0
308      */

309     public final static int LIMITATION_AFTER_SELECT = 4;
310
311     /**
312      * Rowset Limitation Optimisation Syntax Position.
313      * Specifies a where in the SQL command the limitation string should be
314      * inserted.
315      * <p/>
316      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
317      *
318      * @see #LIMITATION_DISABLED
319      * @see #LIMITATION_AFTER_TABLE
320      * @see #LIMITATION_AFTER_WHERE
321      * @see #LIMITATION_AFTER_ORDER_BY
322      * @see com.jcorporate.expresso.core.dbobj.DBObject#searchAndRetrieveList()
323      * @see com.jcorporate.expresso.core.dbobj.DBObject#getOffsetRecord()
324      * @see com.jcorporate.expresso.core.dbobj.DBObject#setOffsetRecord( int )
325      * @see com.jcorporate.expresso.core.dbobj.DBObject#setMaxRecords(int))
326      */

327     protected int limitationPosition = LIMITATION_DISABLED;
328
329     /**
330      * Rowset Limitation Optimisation Syntax String.
331      * Specifies a string to add database query to retrieve
332      * only a finite number of rows from the <code>ResultSet</code>.
333      * <p/>
334      * <p>For example for MYSQL the string should be
335      * <code>"LIMIT %offset% , %maxrecord%"</code>
336      * </p>
337      * <p/>
338      * <p>For example for ORACLE the string should be
339      * <code>"ROWNUM >= %offset% AND ROWNUM <=%endrecord%"</code>
340      * </p>
341      * <p/>
342      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
343      *
344      * @see #limitationPosition
345      * @see com.jcorporate.expresso.core.dbobj.DBObject#searchAndRetrieveList()
346      * @see com.jcorporate.expresso.core.dbobj.DBObject#setOffsetRecord( int )
347      * @see com.jcorporate.expresso.core.dbobj.DBObject#setMaxRecords( int )
348      */

349     protected String JavaDoc limitationSyntax = null;
350
351     /**
352      * Check zero update setting
353      * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
354      */

355     protected boolean checkZeroUpdate = false;
356
357
358     /**
359      * Class that performs appropriate escaping in the strings being sent
360      * to the partuclar database.
361      */

362     protected EscapeHandler escapeHandler = null;
363
364
365     /**
366      * This is a simple tag for when the last time the pool was ever used. If
367      * it has elapsed the timeout time specified in the setup table, then
368      * we execute a clean before we try to hand out a connection.
369      */

370     protected long lastUsed;
371
372     /**
373      * JNDI DataSource Context instance to a DataSOurce Pool used inside
374      * Application server or Web Container
375      */

376     private JndiDataSource jndiDS = null;
377
378     /**
379      * Default Constructor
380      */

381     public DBConnectionPool() {
382         super();
383         lastUsed = System.currentTimeMillis();
384     } /* DBConnectionPool() */
385
386
387     /**
388      * Creates a new database connection. It just rather blindly does this without
389      * regards to database maximums, so it should rarely be used. Normally
390      * it is used internally by createNewConnection().
391      *
392      * @return DBConnection object
393      * @throws DBException upon error
394      */

395     protected DBConnection buildNewConnection() throws DBException {
396         DBConnection oneConnection = null;
397         //
398
//todo FIXME: Refactor so that DBConnection simply takes a ConfigJdbc
399
//bean object and have it self configure itself.
400
//
401
if (dbDriverType.equalsIgnoreCase("datasource")) {
402             JDBCConfig myJdbc = getJDBCConfig(getDataContext());
403             oneConnection = new DBConnection(myJdbc);
404         } else {
405             oneConnection = new DBConnection(dbDriver, dbURL, dbConnectFormat);
406             oneConnection.connect(dbLogin, dbPassword);
407         }
408         oneConnection.setDataContext(getDataContext());
409         oneConnection.setId(nextConnectionId);
410         nextConnectionId++;
411         oneConnection.setDescription("New Connection");
412         return oneConnection;
413     }
414
415     /**
416      * Creates a new connection to the data source
417      *
418      * @return a newly instantiated and populated DBConnection, or null if maxconnections is exceeded,
419      * or null if "available" list has items, so that we should not be creating.
420      * @throws DBException upon error
421      */

422     protected synchronized DBConnection createNewConnection() throws DBException {
423         DBConnection oneConnection = null;
424
425         //
426
// check number of existing connections; do not allow more than maxPoolSize
427
//
428
int totalConnections = 0;
429         int avail = 0;
430         int inuse = 0;
431         boolean firstConnection = false;
432         synchronized (poolLock) {
433             avail = available.size();
434             inuse = inUse.size();
435             totalConnections = avail + inuse;
436             firstConnection = totalConnections == 0;
437             //
438
//If there's one that's become available we'd rather try to get
439
//that one instead.
440
//
441
if (avail > 0) {
442                 return null;
443             }
444
445             if (totalConnections >= this.maxPoolSize) {
446                 return null;
447             }
448
449         } // sync
450

451         if (log.isInfoEnabled()) {
452             log.info("Creating new connection Total size: " + totalConnections + "In Use Size: " +
453                     inuse + " Available: " + avail + " Name: " + this.getDataContext());
454         }
455
456         oneConnection = buildNewConnection();
457         oneConnection.setParentPool(this);
458
459         synchronized (poolLock) {
460             // another thread could have created a connection
461
// in the time between when we last peeked at number of connections, so recalc
462
int totalPools = available.size() + inUse.size();
463             if (totalPools >= this.maxPoolSize) {
464                 oneConnection.disconnect();
465                 log.warn("Got too many connections... abandoning one: inUse.size()=" +
466                         inUse.size() +
467                         " available.size()=" + available.size());
468                 return null;
469             } else {
470                 // convention is that a new connection is marked unavailable,
471
// since we do not want anyone else grabbing it out of the pool
472
// before we hand it back to the requesting client
473
oneConnection.setAvailable(false);
474
475                 inUse.put(new Integer JavaDoc(oneConnection.getId()), oneConnection);
476             }
477         }
478
479         if (log.isDebugEnabled()) {
480             log.debug("New connection " + oneConnection.getId() +
481                     " created successfully. " + "Now " + avail +
482                     " connections available in pool '" + getDataContext() +
483                     "', " + inuse + " currently connected");
484         }
485
486         /* if we've just initialized the connection pool */
487         if (firstConnection) {
488             initialized = true;
489             supportsTransactions = oneConnection.supportsTransactions();
490
491             JDBCConfig myConfig = getJDBCConfig(getDataContext());
492
493             // Try to get the "limitationPosition" setting from Expresso
494
// config properties if possible
495
String JavaDoc limPosStr = StringUtil.notNull(myConfig.getLimitationPosition());
496
497             if (limPosStr.length() > 0) {
498                 setLimitationPosition(limPosStr);
499             }
500
501             // Try to get the "limitationSyntax" setting from Expresso
502
// config properties if possible
503
String JavaDoc limSynStr = StringUtil.notNull(myConfig.getLimitationSyntax());
504
505             if (limPosStr.length() > 0) {
506                 setLimitationSyntax(limSynStr);
507             }
508
509             // Try to get the "unique row keyword" from Expresso
510
// config properties if possible
511
String JavaDoc keyword = StringUtil.notNull(myConfig.getUniqueRowKeyword());
512
513             if (keyword.length() < 1) {
514                 uniqueRowKeyword = "DISTINCT"; // Default SQL keyword
515
} else {
516                 // todo why do we not set uniqueRowKeyword in this case????? LAH 6/03
517
// uniqueRowKeyword = keyword;
518
}
519
520             // Try to get the "check zero update" from Expresso
521
// config properties if possible
522
String JavaDoc zeroUpdate = StringUtil.notNull(myConfig.getCheckZeroUpdate());
523
524             if (zeroUpdate.length() > 0) {
525                 checkZeroUpdate = myConfig.checkZeroUpdate();
526             }
527
528             // Now call setWildCards to see if any user-specific
529
// wildcards where loaded in the properties file.
530
setWildCards();
531
532         }
533         //Added so that limitation syntax is properly set for the connection
534
oneConnection.setLimitationPosition(this.limitationPosition);
535         oneConnection.setLimitationSyntax(this.limitationSyntax);
536         oneConnection.setEscapeHandler(this.escapeHandler);
537
538         return oneConnection;
539     }
540
541     /**
542      * Iterates once through the available connections and cleans them.
543      */

544     protected void cleanAvailable() throws ConnectionPoolException {
545         if (log.isDebugEnabled()) {
546             log.debug("Checking available pool for connections to remove");
547         }
548
549         long startTime = System.currentTimeMillis();
550         boolean removedConnections = false;
551         synchronized (poolLock) {
552
553             // unusual iteration allows for removal of list item during iteration
554
for (int i = 0; i < available.size();) {
555                 DBConnection dbc = (DBConnection) available.get(i);
556                 //
557
//Check how long it has been since the connection was idle. If
558
//it has been idle too long, then we need to kill it so we don't
559
//hand out stale connections in the long run.
560
//
561
if (dbc.getCreatedTime() + maxttl < startTime) {
562                     try {
563                         dbc.disconnect();
564                     } catch (Throwable JavaDoc ex) {
565                         log.warn("Error disconnecting", ex);
566                     }
567
568                     available.remove(i);
569                     removedConnections = true;
570                 } else {
571                     i++;
572                 }
573             }
574
575             if (removedConnections) {
576                 poolLock.notify();
577             }
578         }
579
580     }
581
582     /**
583      * Clean the connection pool - see if any connections have
584      * been idle more than the allowed number of seconds . If so,
585      * disconnect & de- allocate them
586      *
587      * @throws DBException If an error occurs releasing the stale connection
588      * @throws ConnectionPoolException for other errors relating internally
589      * to the connectionpool code.
590      */

591     public void clean()
592             throws ConnectionPoolException, DBException {
593
594         if (log.isDebugEnabled()) {
595             log.debug("Checking connection pool for stale connections");
596         }
597
598         long now = System.currentTimeMillis();
599         long lastTouched = 0;
600         DBConnection oneConnection = null;
601
602         ArrayList JavaDoc connectionsToRelease = null;
603
604         /* Vector holding the identifiers of the connection objects */
605         List JavaDoc connectionsToBeRemoved = new ArrayList JavaDoc();
606         /* boolean to check whether the connection objects should be removed*/
607
608         synchronized (poolLock) {
609             for (Iterator JavaDoc it = inUse.values().iterator(); it.hasNext();) {
610                 oneConnection = (DBConnection) it.next();
611                 if (log.isDebugEnabled()) {
612                     log.debug("Checking '"
613                             + oneConnection.getDescription() + "'");
614                 }
615
616                 lastTouched = oneConnection.getLastTouched();
617                 long timeOutTime = lastTouched + interval;
618
619                 if (now > timeOutTime) {
620                     /* it's been too long */
621                     if (log.isDebugEnabled()) {
622                         log.debug("Closing connection");
623                     }
624
625                     if (!oneConnection.getImmortal()) {
626                         if (log.isDebugEnabled()) {
627                             log.debug("Connection "
628                                     + oneConnection.getDescription()
629                                     + " was idle more than " + (interval / 1000)
630                                     + " seconds and was returned to the pool. "
631                                     + "Last SQL executed was '"
632                                     + oneConnection.getSQL() + "'");
633                         }
634
635                         if (connectionsToRelease == null) {
636                             connectionsToRelease = new ArrayList JavaDoc();
637                         }
638                         connectionsToRelease.add(oneConnection);
639
640                     } else {
641                         log.warn("Warning: 'Immortal' Connection "
642                                 + oneConnection.getDescription()
643                                 + " was idle more than " + (interval / 1000)
644                                 + " seconds. "
645                                 + "Last SQL executed was '"
646                                 + oneConnection.getSQL() + "'");
647                     }
648                 }
649
650                 /* The block which was here to release the connections has been moved down
651                     after the iteration */

652
653                 /* If the connection is (interval * 3) MINUTES old, then close it */
654                 timeOutTime = lastTouched + (interval * 60 * 3);
655                 if (now > timeOutTime) {
656
657                     log.warn("Connection "
658                             + oneConnection.getDescription()
659                             + " was idle more than " + (interval / 1000)
660                             + " minutes and was disconnected and removed "
661                             + "from the pool");
662
663                     /* remove the elements only after iteration
664                         This is done to avoid the ConcurrentModificationException */

665
666                     connectionsToBeRemoved.add(oneConnection);
667
668                 }
669             } /* for each connection in the pool */
670
671             /* Now iterate that vector and remove the connection objects from the hashmap
672                     This is done to avoid the ConcurrentModificationException*/

673             for (int i = 0; i < connectionsToBeRemoved.size(); i++) {
674                 DBConnection dbconn = (DBConnection) connectionsToBeRemoved.get(i);
675                 dbconn.disconnect();
676                 dbconn.setAvailable(true);
677                 inUse.remove(new Integer JavaDoc(dbconn.getId()));
678             }
679
680             /* This is the block which releases the connection and is done after the iteration*/
681             DBConnection oneToRelease = null;
682             if (connectionsToRelease != null) {
683                 for (Iterator JavaDoc rl = connectionsToRelease.iterator();
684                      rl.hasNext();) {
685
686                     oneToRelease = (DBConnection) rl.next();
687                     release(oneToRelease);
688                     oneToRelease.clear();
689                     oneToRelease = null;
690                 }
691             }
692         } /* End Lock */
693     } /* clean() */
694
695
696     /**
697      * Disconnect all of the current connections. Called when we shut down
698      */

699     public synchronized void disconnectAll()
700             throws DBException {
701         DBConnection oneConnection = null;
702
703         for (Iterator JavaDoc it = getPoolList().iterator(); it.hasNext();) {
704             oneConnection = (DBConnection) it.next();
705
706             if (!oneConnection.isClosed()) {
707                 try {
708                     oneConnection.disconnect();
709                 } catch (DBException de) {
710                     log.error("Unable to disconnect from " +
711                             "database successfully when clearing connection pool",
712                             de);
713                 }
714             }
715         } /* for each connection in the current pool */
716
717         synchronized (poolLock) {
718             available = new LinkedList JavaDoc();
719             inUse = new HashMap JavaDoc(3);
720         }
721     } /* disconnectAll() */
722
723
724     /**
725      * This allows an exclusive updated to be done to a database. (Such as
726      * drop table) should ensure that no tables are in use when this update
727      * is executed.
728      *
729      * @param theSQL the SQL code to execute.
730      */

731     public synchronized void executeExclusiveUpdate(String JavaDoc theSQL)
732             throws DBException {
733         this.disconnectAll();
734
735         try {
736             yield();
737             sleep(500); //We've got some sort of timing problem with connections closing
738

739             //and being able to execute exclusive. Maybe this will
740
//help for interbase at least.
741
} catch (java.lang.InterruptedException JavaDoc ie) {
742             if (log.isDebugEnabled()) {
743                 log.debug("Interrupted while sleeping");
744             }
745         }
746
747         DBConnection dbc = null;
748
749         try {
750             dbc = this.getConnection();
751             dbc.executeUpdate(theSQL);
752         } finally {
753             if (dbc != null) {
754                 this.release(dbc);
755             }
756         }
757     }
758
759     /**
760      * Find a connection that is already in the pool that is available.
761      * If there isn't one, return null
762      *
763      * @return The available connection if one is found, else null
764      */

765     private DBConnection findExistingConnection() {
766         DBConnection oneConnection = null;
767
768         try {
769             while (available.size() > 0) {
770                 synchronized (poolLock) {
771                     if (available.size() == 0) {
772                         return null;
773                     }
774                     oneConnection = (DBConnection) available.removeFirst();
775                     oneConnection.setAvailable(false);
776
777                     /* Check for a dead connection */
778                     if (testQuery != null) {
779                         try {
780                             oneConnection.execute(testQuery);
781                         } catch (DBException de) {
782                             if (log.isDebugEnabled()) {
783                                 log.debug("Test query '" + testQuery +
784                                         "' failed:" + de.getMessage() +
785                                         ", closing connection & trying again");
786                             }
787                             try {
788                                 oneConnection.disconnect();
789                             } catch (Exception JavaDoc e) {
790                                 log.error("Unable to close connection that failed " +
791                                         "test query", e);
792                             }
793
794                             // This was the cause of a bug because it returns a null while we could have
795
// more available connections. What we have to do is to continue iterating
796
// and decrease the number of total pools.
797
continue;
798                         }
799                     }
800
801                     if (oneConnection.isClosed()) {
802                         if (log.isDebugEnabled()) {
803                             log.debug("Dead connection removed " +
804                                     "from pool. Was previously '" +
805                                     oneConnection.getDescription() +
806                                     "'. Currently " + available.size() +
807                                     " connections available, " + inUse.size() +
808                                     " clients connected");
809                         }
810                         try {
811                             oneConnection.disconnect();
812                         } catch (Exception JavaDoc e) {
813                             //Log, but otherwise ignore the exception
814
log.warn("Error Disconnecting", e);
815                         } finally {
816                             // This was the cause of a bug because it returns a null while we could have
817
// more available connections. What we have to do is to continue iterating
818
// and decrease the number of total pools.
819
oneConnection = null;
820                         }
821                         continue;
822                     }
823
824                     //
825
//New Logic since Expresso 5.1 ea2... If the connection was created
826
//before the ttl interval, then we discard it and try to
827
//get a new one.
828
//
829
if (oneConnection.getCreatedTime()
830                             + this.maxttl < System.currentTimeMillis()) {
831                         if (log.isInfoEnabled()) {
832                             log.info("Discarding connection. Timeout since created time reached");
833                         }
834
835                         try {
836                             oneConnection.disconnect();
837                         } catch (Exception JavaDoc e) {
838                             //Log, but otherwise ignore the exception
839
log.warn("Error Disconnecting", e);
840                         } finally {
841                             // This was the cause of a bug because it returns a null while we could have
842
// more available connections. What we have to do is to continue iterating
843
// and decrease the number of total pools.
844
oneConnection = null;
845                         }
846                         continue;
847
848                     }
849
850                     /* connection wasn't dead or expired all is OK */
851                     if (log.isDebugEnabled()) {
852                         log.debug("Available connection " +
853                                 oneConnection.getId() +
854                                 " found. Was previously '" +
855                                 oneConnection.getDescription() +
856                                 "'. Currently " + available.size() +
857                                 " connections available, " + inUse.size() +
858                                 " clients connected");
859                     }
860
861                     inUse.put(new Integer JavaDoc(oneConnection.getId()),
862                             oneConnection);
863                 }
864
865                 return oneConnection;
866
867             } /* while */
868
869         } catch (DBException de) {
870             log.error("Unable to test if connection is closed", de);
871         }
872
873         return null;
874     } /* findExistingConnection() */
875
876     /**
877      * Find an available connection in the default connection pool, if any.
878      * <p>If none, make a new one if we can.</p>
879      * <p>Does not set the new connection's description</p>
880      * This method will sleep for "interval" number of seconds if no
881      * connections are available and the pool is full. It then tries
882      * again to find or allocate a connection before failing.
883      *
884      * @return DBConnection A database connection ready for use
885      * @throws DBException If there's an error talking with the Database
886      * @throws PoolFullException if we're unable to allocate a new connection because
887      * the pool is full and we still haven't gotten a connection
888      * @throws ConnectionPoolException for other connection-pool related errors.
889      * author Yves Henri AMAIZO (Modification)
890      * author Larry Hamel (Modification)
891      */

892     public DBConnection getConnection()
893             throws ConnectionPoolException, PoolFullException, DBException {
894         if (!isInitialized()) {
895             throw new ConnectionPoolException("Connection pool '" + getDataContext() +
896                     "' is not initialized. Can't get connection.");
897         }
898
899
900         //
901
//Parameter check
902
//
903
if (dbDriver == null) {
904             throw new ConnectionPoolException("Cannot make new connection - dbDriver is null");
905         }
906         if (dbURL == null) {
907             throw new ConnectionPoolException("Cannot make new connection - dbURL is null");
908         }
909         if (dbConnectFormat == null) {
910             throw new ConnectionPoolException("Cannot make new connection - dbConnectFormat is null");
911         }
912
913
914         DBConnection oneConnection = null;
915         int connectionTries = 0;
916         int numAvail, numInUse;
917
918         while (connectionTries < GET_CONNECTION_RETRIES) {
919             //
920
//Moved the periodic clean to here. Will result in more cleans, but
921
//may clearn out some of the locks we're having.
922
//
923
issuedConnectionsCount++;
924
925             /* is it time to clean the pool? */
926             if (issuedConnectionsCount >= CLEAN_POOL_MAX) {
927                 cleanAvailable();
928                 clean();
929                 issuedConnectionsCount = 0;
930                 // synchronize since lastused is a long
931
synchronized (timestampLock) {
932                     lastUsed = System.currentTimeMillis();
933                 }
934             }
935             /**
936              * If the connection pool has been all together idle for more than
937              * the timeout period, we need to attempt a clean anyway, since many
938              * connections may have already timed out.
939              */

940             if (isTimeToClean()) {
941                 cleanAvailable();
942                 clean();
943                 issuedConnectionsCount = 0;
944                 // synchronize since lastused is a long
945
synchronized (timestampLock) {
946                     lastUsed = System.currentTimeMillis();
947                 }
948             }
949
950
951             synchronized (poolLock) {
952                 //
953
//Ok, now try to find an available connection
954
//
955
oneConnection = this.findExistingConnection();
956                 if (oneConnection != null) {
957                     oneConnection.setDataContext(getDataContext());
958                     return oneConnection;
959                 }
960
961                 if (isFull()) {
962                     clean();
963                     if (isFull()) {
964                         //Sleep
965
if (log.isInfoEnabled()) {
966                             log.info("--------------------------------");
967                             log.info("WARNING: DB Connection Pool '" + getDataContext() +
968                                     "' is " + "full even after clean.");
969                         }
970
971                         if (log.isDebugEnabled()) {
972                             dumpDebugInfo();
973                         }
974
975                         long currentTime = System.currentTimeMillis();
976                         //
977
//We wait for the maximum connection timeout divided by
978
//two. This way we have
979
//
980
long waitInterval = interval / 2;
981                         long finaltime = waitInterval + currentTime;
982                         try {
983                             while (finaltime > currentTime && isFull()) {
984                                 /* Now wait for a number of seconds, retrying again */
985                                 poolLock.wait(waitInterval);
986
987                                 if (System.currentTimeMillis() >= finaltime && isFull()) {
988                                     //
989
//Give ourselves a last chance to free something up.
990
//
991
clean();
992                                     if (isFull()) {
993                                         log.error("Pool '" + getDataContext() + "' still full");
994                                         //We timed out waiting for connection:
995
throw new PoolFullException("Cannot allocate " +
996                                                 "another database connection. There are already " +
997                                                 "too many connections in use." +
998                                                 " Please report this problem to the System " +
999                                                 "Administrator");
1000                                    }
1001
1002                                } else {
1003                                    //
1004
//We got notified. time to try to find an existing connection.
1005
//
1006
oneConnection = findExistingConnection();
1007                                }
1008
1009                                if (oneConnection != null) {
1010                                    if (log.isDebugEnabled()) {
1011                                        log.debug("Found existing connection after sleep");
1012                                    }
1013
1014                                    oneConnection.setDataContext(getDataContext());
1015                                    return oneConnection;
1016                                }
1017
1018                                currentTime = System.currentTimeMillis();
1019                            }
1020                        } catch (InterruptedException JavaDoc ie) {
1021                            throw new ConnectionPoolException("Interrupted while waiting for available connection");
1022                        }
1023
1024                    }
1025                }
1026
1027                // find sizes under sync
1028
numAvail = available.size();
1029                numInUse = inUse.size();
1030            } /* End Synchronized */
1031
1032
1033            //
1034
// no existing conn if we got here,
1035
// so if we have room to create a connection, then let's create it!
1036
//
1037
if (numAvail + numInUse < this.maxPoolSize) {
1038                //Create a new connection
1039
oneConnection = createNewConnection();
1040
1041                if (oneConnection != null) {
1042                    return oneConnection;
1043                }
1044            }
1045
1046
1047            //
1048
//OK, this time around, we couldn't create a new connection or
1049
//find an existing connection. This is fine because we're running
1050
//a series of 'controlled' race conditions. So we loop around again
1051
//looking for a connection to grab. first we sleep to reduce CPU
1052
//usage for those cases where the system is
1053
//
1054
if (log.isInfoEnabled()) {
1055                log.info("Couldn't find or create a new connection for this iteration.");
1056            }
1057
1058            synchronized (this) {
1059                connectionTries++;
1060                try {
1061                    sleep(1);
1062                } catch (InterruptedException JavaDoc ex) {
1063                    log.debug("Interrupted while sleeping", ex);
1064                }
1065            }
1066
1067        } /* end while */
1068
1069
1070        throw new PoolFullException("Unable to get database connection in "
1071                + GET_CONNECTION_RETRIES + " attempts");
1072
1073    } /* getConnection() */
1074
1075    private boolean isTimeToClean() {
1076        long now = System.currentTimeMillis();
1077        boolean isTimeToClean = false;
1078
1079        // synchronize since lastused is a long
1080
synchronized (timestampLock) {
1081            isTimeToClean = now > this.lastUsed + this.interval;
1082        }
1083        return isTimeToClean;
1084    }
1085
1086
1087    /**
1088     * Helper function that dumps all the current contents of the inUse
1089     * connections.
1090     */

1091    protected void dumpDebugInfo() {
1092        //
1093
//Extensive Debugging Output if needed
1094
//
1095
if (log.isDebugEnabled()) {
1096            synchronized (poolLock) {
1097                int i = 0;
1098                log.debug("Current contents:");
1099                for (Iterator JavaDoc conns = inUse.values().iterator();
1100                     conns.hasNext();) {
1101                    DBConnection oneConn = (DBConnection) conns.next();
1102                    i++;
1103                    Date JavaDoc dt = new Date JavaDoc(oneConn.getLastTouched());
1104                    try {
1105                        log.debug("Connection " + i + ":" +
1106                                oneConn.getDescription() + ":" +
1107                                DateTime.getDateTimeForDB(dt));
1108                    } catch (DBException ex) {
1109                        log.error("Error rendering date time object", ex);
1110                    }
1111                }
1112
1113                log.debug("Waiting " + (interval / 2000) +
1114                        " seconds for a connection to free up");
1115            }
1116        }
1117
1118    }
1119
1120    /**
1121     * Helper function to determine if the pool is full.
1122     *
1123     * @return true if the connection pool is full
1124     */

1125    protected boolean isFull() {
1126        int inuse, avail;
1127        synchronized (poolLock) {
1128            inuse = inUse.size();
1129            avail = available.size();
1130        }
1131        if (log.isDebugEnabled()) {
1132            int totalConnections = avail + inuse;
1133            log.debug("Available connections: " + avail
1134                    + " In Use Connections: " + inuse + " totalConnections " + totalConnections);
1135        }
1136        return (inuse >= maxPoolSize);
1137    }
1138
1139    /**
1140     * Get a connection from the pool. If the pool does not have any free
1141     * connections (and has not reached it's max size), create a new one.
1142     * <p>This version of getConnection sets the new connections description
1143     * as well, avoiding a seperate call to setDescription </p>
1144     *
1145     * @param connectionDescrip A description of the use of the database
1146     * connection
1147     * @return DBConnection A database connection ready for use, or null
1148     * if no more connections can be allocated
1149     * @throws DBException If the pool is not initialized or the connection
1150     * cannot be established
1151     */

1152    public DBConnection getConnection(String JavaDoc connectionDescrip)
1153            throws DBException {
1154        DBConnection oneConnection = getConnection();
1155        oneConnection.setDescription(connectionDescrip);
1156
1157        if (log.isDebugEnabled()) {
1158            log.debug("Connection for '" + connectionDescrip +
1159                    "' established");
1160        }
1161
1162        return oneConnection;
1163    } /* getConnection(String) */
1164
1165
1166    /**
1167     * Return the current database name/config key
1168     *
1169     * @return The db name
1170     */

1171    public String JavaDoc getDBName() {
1172        return dbName;
1173    } /* getDBName() */
1174
1175    /**
1176     * Return the current database name/config key
1177     *
1178     * @return The db name
1179     */

1180    public String JavaDoc getDataContext() {
1181        return dbName;
1182    } /* getDBName() */
1183
1184    /**
1185     * Does this database connection support commit/rollback?
1186     *
1187     * @param connName the connection name
1188     * @return true if the db supports transactions
1189     */

1190    static public boolean supportsTransactions(String JavaDoc connName)
1191            throws DBException {
1192        DBConnectionPool myPool = getInstance(connName);
1193
1194        return myPool.supportsTransactions();
1195    }
1196
1197    /**
1198     * Does this database connection support commit/rollback?
1199     *
1200     * @return true if the db supports transactions
1201     */

1202    public boolean supportsTransactions()
1203            throws DBException {
1204        return supportsTransactions;
1205    }
1206
1207    /**
1208     * Version of getInstance that can only get an already initialized
1209     * connection pool to an alternate database
1210     *
1211     * @return DBConnectionPool The instance of a connection pool
1212     * @throws DBException If the alternate pool cannot be created,
1213     * for example if there is no connection by the given name
1214     */

1215    static synchronized public DBConnectionPool getInstance(String JavaDoc dataContext)
1216            throws DBException {
1217
1218        synchronized (DBConnectionPool.otherDBPools) {
1219            if (dataContext == null || dataContext.length() == 0) {
1220                dataContext = DBConnection.DEFAULT_DB_CONTEXT_NAME;
1221            }
1222
1223            DBConnectionPool altPool = (DBConnectionPool) otherDBPools.get(dataContext);
1224
1225            if (altPool == null) {
1226                DBConnectionPool newPool = new DBConnectionPool();
1227                newPool.setDataContext(dataContext);
1228
1229                JDBCConfig myConfig = DBConnectionPool.getJDBCConfig(dataContext);
1230
1231                try {
1232                    newPool.setParams(myConfig);
1233                } catch (DBException de) {
1234                    throw new ConnectionPoolException("Unable to initialize pool for configuration '" +
1235                            dataContext + "':" + de.getMessage(), de);
1236                }
1237
1238                otherDBPools.put(dataContext, newPool);
1239
1240                return newPool;
1241            }
1242
1243            return altPool;
1244        }
1245    } /* getInstance(String) */
1246
1247
1248    /**
1249     * Retrieve a dumb datasource implementation that is compatible with items such
1250     * as reporting tools, etc.
1251     * <p>Note that performance of the resulting DataSource may be very slow since
1252     * the datasource is a dumb connector, and does NOT consider the connection
1253     * pool (because Expresso, at this time, has no way to register for a java.sql.Connection
1254     * to be removed from the pool when the API programmer closes it)
1255     * </p>
1256     *
1257     * @param dataContext the data context to retrieve the data source for.
1258     * @return
1259     */

1260    public static synchronized javax.sql.DataSource JavaDoc getDataSource(String JavaDoc dataContext)
1261            throws java.sql.SQLException JavaDoc {
1262        try {
1263            return new SimpleDataSource(DBConnectionPool.getInstance(dataContext));
1264        } catch (DBException ex) {
1265            log.error("Error getting database connection pool", ex);
1266            throw new java.sql.SQLException JavaDoc(ex.getMessage());
1267        }
1268    }
1269
1270    /**
1271     * Return the entire pool of connections as a Vector
1272     *
1273     * @return ArrayList An array list containing the DBConnection objects
1274     * @throws DBException If the pool cannot be returned
1275     */

1276    public synchronized ArrayList JavaDoc getPoolList()
1277            throws DBException {
1278        ArrayList JavaDoc al;
1279        synchronized (poolLock) {
1280            al = new ArrayList JavaDoc(available);
1281            for (Iterator JavaDoc it = inUse.values().iterator(); it.hasNext();) {
1282                al.add(it.next());
1283            }
1284        }
1285
1286        return al;
1287    }
1288
1289    public ArrayList JavaDoc getWildCardsList() {
1290        return new ArrayList JavaDoc(wildCards);
1291    }
1292
1293    /**
1294     * Is this connection pool initialized, or does it require database
1295     * parameters?
1296     *
1297     * @return true if the DBConnectionPool is initialized
1298     */

1299    public boolean isInitialized() {
1300        return initialized;
1301    } /* isInitialized() */
1302
1303    /**
1304     * Release the given connection
1305     * It is the object requesting the connection's responsibility to call
1306     * this method to release the connection(s) it requested.
1307     *
1308     * @param connectionToRelease The DBConnection to be released back to
1309     * the pool.
1310     */

1311    public void release(DBConnection connectionToRelease) {
1312        if (connectionToRelease == null) {
1313            return;
1314        }
1315        /* We always set a connection that's being returned to the
1316        * pool to auto-commit,
1317        * so that when it's
1318        * used next time it can be assumed to be in autocommit mode
1319
1320*/

1321        try {
1322            if (!connectionToRelease.getAutoCommit()) {
1323                connectionToRelease.setAutoCommit(true);
1324            }
1325
1326            connectionToRelease.clear();
1327        } catch (DBException dbe) {
1328            if (log.isDebugEnabled()) {
1329                log.debug("Error setting auto-commit to true", dbe);
1330            }
1331            /* ignore the exception - just means that transactions are not handled */
1332            /* by this db */
1333        }
1334        if (connectionToRelease.isAvailable()) {
1335            return;
1336        }
1337        if (log.isDebugEnabled()) {
1338            log.debug("Releasing connection " + connectionToRelease.getId() +
1339                    " '" + connectionToRelease.getDescription() + "'");
1340        }
1341        synchronized (poolLock) {
1342            if (inUse.remove(new Integer JavaDoc(connectionToRelease.getId())) == null) {
1343                if (log.isDebugEnabled()) {
1344                    log.debug("Connection " + connectionToRelease.getId() +
1345                            " was not listed as " +
1346                            "in use and could not be released");
1347                }
1348
1349                return;
1350            }
1351            connectionToRelease.setAvailable(true);
1352            available.add(connectionToRelease);
1353            poolLock.notify();
1354        }
1355
1356
1357        if (log.isDebugEnabled()) {
1358            log.debug("Connection " + connectionToRelease.getId() + " '" +
1359                    connectionToRelease.getDescription() +
1360                    "' released back to pool. Now " + inUse.size() +
1361                    " connected");
1362        }
1363    } /* release(DBConnection) */
1364
1365    /**
1366     * Useful for querying the potential of the dbconnection pool's status
1367     *
1368     * @return maximum number of connections allowed.
1369     */

1370    public int getMaxConnections() {
1371        return maxPoolSize;
1372    }
1373
1374    /**
1375     * Sets the maximum number of connections for this pool
1376     *
1377     * @param newMax The new maximum number of connections
1378     */

1379    public synchronized void setMaxConnections(int newMax)
1380            throws DBException {
1381        maxPoolSize = newMax;
1382    } /* setMaxConnections(int) */
1383
1384
1385    /**
1386     * Set the current database name/config key
1387     *
1388     * @param newDBName The new dataContext to set for this connection pool
1389     */

1390    protected synchronized void setDBName(String JavaDoc newDBName) {
1391        if (StringUtil.notNull(newDBName).equals("")) {
1392            newDBName = DBConnection.DEFAULT_DB_CONTEXT_NAME;
1393        }
1394
1395        dbName = newDBName;
1396    } /* setDBName(String) */
1397
1398    /**
1399     * Set the current database name/config key
1400     *
1401     * @param newDBName The new dataContext to set for this connection pool
1402     */

1403    protected synchronized void setDataContext(String JavaDoc newDBName) {
1404        if (StringUtil.notNull(newDBName).equals("")) {
1405            newDBName = DBConnection.DEFAULT_DB_CONTEXT_NAME;
1406        }
1407
1408        dbName = newDBName;
1409    } /* setDBName(String) */
1410
1411    /**
1412     * Set the parameters required to make database connections
1413     * The servlet that gets invoked first passes this info to the connection
1414     * pool from it's arguments, where they are used to create new connections
1415     * as required.
1416     *
1417     * @param theParams The JDBC Config bean as defined by the system configuration
1418     * @throws DBException If any parameters are invalid
1419     * @see com.jcorporate.expresso.core.db.DBConnection
1420     */

1421    private synchronized void setParams(JDBCConfig theParams) throws DBException {
1422        String JavaDoc myName = (THIS_CLASS +
1423                "setParams(String, String, String, String, String)");
1424        dbDriverType = theParams.getDriverType();
1425        dbDriver = theParams.getDriver();
1426        dbURL = theParams.getUrl();
1427        dbConnectFormat = theParams.getConnectFormat();
1428        dbLogin = theParams.getLogin();
1429        dbPassword = theParams.getPassword();
1430
1431
1432        if (dbDriverType == null) {
1433            dbDriverType = ("manager");
1434        }
1435
1436        if (dbDriver == null) {
1437            throw new ConnectionPoolException(myName + ":Database driver name cannot be " +
1438                    "null");
1439        }
1440        if (dbURL == null) {
1441            throw new ConnectionPoolException(myName + ":Database URL cannot be null");
1442        }
1443        if (dbConnectFormat == null) {
1444            throw new ConnectionPoolException(myName + ":Database connection format " +
1445                    "cannot be null");
1446        }
1447        if (dbLogin == null) {
1448            throw new ConnectionPoolException(myName + ":Database login cannot be null");
1449        }
1450        if (dbPassword == null) {
1451            throw new ConnectionPoolException(myName +
1452                    ":Database password cannot be null");
1453        }
1454
1455        try {
1456            escapeHandler = (EscapeHandler) ClassLocator.loadClass(theParams
1457                    .getEscapeHandler()).newInstance();
1458        } catch (Exception JavaDoc ex) {
1459            log.warn("Error instantiating escape handler " + theParams.getEscapeHandler());
1460            //Attempt to repair the
1461
//the situation by creating default escape handler
1462
escapeHandler = new DefaultEscapeHandler();
1463        }
1464
1465        // JNDI DataSource Pool invocation for equivalent to Expresso DBConnectionPool
1466
// Each DBConnectionPool Has Equivalent to JNDI DataSOurce Pool and
1467
// and make mappings between each DataSource connection to DBConnection
1468
// Yves Henri AMAIZO 04/08/2002 02:10
1469
// @revision 1.12
1470
if (dbDriverType.equalsIgnoreCase("datasource")) {
1471            if (this.getJNDIConfig(theParams) == null) {
1472                throw new ConnectionPoolException(myName + "JNDI info configure for datasource");
1473            }
1474
1475            try {
1476                jndiDS = new JndiDataSource(this.getJNDIConfig(theParams), theParams.getUrl());
1477                jndiDS.setupContext();
1478            } catch (DSException dse) {
1479                throw new ConnectionPoolException(myName + ":Cannot initialize jndi Context Factory");
1480            }
1481        }
1482
1483        initialized = true;
1484    } /* setParams(String, String, String, String, String) */
1485
1486
1487    /**
1488     * Set a small query to be used to "test" a connection before it's handed out
1489     *
1490     * @param newTestQuery The string to execute as a test query
1491     */

1492    public synchronized void setTestQuery(String JavaDoc newTestQuery) {
1493        testQuery = newTestQuery;
1494    } /* setTestQuery(String) */
1495
1496
1497    /**
1498     * Set the number of seconds that a connection must remain idle
1499     * before it is considered "timed out" and cleared. It also sets the
1500     * maximum time-to-live for the connection to ten times the interval.
1501     * <p/>
1502     * although we are setting long values which are , no sync
1503     *
1504     * @param newInterval The new interval value in seconds
1505     */

1506    public synchronized void setTimeOutInterval(int newInterval)
1507            throws DBException {
1508
1509        if (newInterval < 1) {
1510            String JavaDoc myName = (THIS_CLASS + "setTimeOutInterval(int)");
1511            throw new ConnectionPoolException(myName + ":Interval must be greater than 0");
1512        }
1513
1514        interval = (long) newInterval * 1000;
1515        maxttl = interval * 10;
1516    } /* setTimeOutInterval(int) */
1517
1518
1519    /**
1520     * set wild cards from config, or default if no config definition exists
1521     */

1522    private synchronized void setWildCards() throws DBException {
1523        boolean propsFound = false;
1524
1525        JDBCConfig myConfig = getJDBCConfig(this.getDataContext());
1526
1527        for (Enumeration JavaDoc e = myConfig.getWildcards().elements();
1528             e.hasMoreElements();) {
1529            propsFound = true;
1530            wildCards.add(e.nextElement());
1531        }
1532        /* The user can specify database wildcards in the config files
1533        * which will override
1534        * Note: If there is even just one dbWildCard, none of the defaults
1535        * are used. This is because it is potentially harmful to have bogus
1536        * wildchard characters defined (The LIKE operator will
1537        * be used instead of the = operator in the wrong place,
1538        * and you will not get a match).
1539*/

1540        if (!propsFound) {
1541
1542            // we did not get any wildcards loaded from the props file...
1543
// load the default wildcards
1544
for (Iterator JavaDoc it = getDefaultWildCards().iterator();
1545                 it.hasNext();) {
1546                wildCards.add(it.next());
1547            }
1548        }
1549    } /* setWildCards(DBConnection) */
1550
1551
1552    /**
1553     * Close all existing connections & empty the pools
1554     */

1555    public synchronized static void reInitialize()
1556            throws DBException {
1557        synchronized (otherDBPools) {
1558
1559            for (Iterator JavaDoc it = otherDBPools.values().iterator(); it.hasNext();) {
1560                DBConnectionPool onePool = (DBConnectionPool) it.next();
1561                onePool.disconnectAll();
1562            }
1563
1564            otherDBPools = new HashMap JavaDoc();
1565            System.gc();
1566        }
1567    } /* reInitialize() */
1568
1569
1570    /**
1571     * programmatically gets the limitation optimisation insertion position
1572     * <p/>
1573     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
1574     *
1575     * @return an integer code for the limitation syntax position
1576     * @since Expresso 4.0
1577     */

1578    public synchronized int getLimitationPosition() {
1579        return limitationPosition;
1580    }
1581
1582    /**
1583     * programmatically sets the limitation optimisation insertion position
1584     *
1585     * @param pos the position
1586     * @throws DBException author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
1587     * @since Expresso 4.0
1588     */

1589    public synchronized void setLimitationPosition(int pos)
1590            throws DBException {
1591        if (pos != LIMITATION_DISABLED && pos != LIMITATION_AFTER_TABLE &&
1592                pos != LIMITATION_AFTER_WHERE &&
1593                pos != LIMITATION_AFTER_ORDER_BY &&
1594                pos != LIMITATION_AFTER_SELECT) {
1595            throw new ConnectionPoolException("illegal argument for limitation optimisation position");
1596        }
1597
1598        this.limitationPosition = pos;
1599    }
1600
1601
1602    /**
1603     * programmatically sets the limitation optimisation insertion position
1604     * as readable string.
1605     *
1606     * @param pos the position as a String
1607     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
1608     * @since Expresso 4.0
1609     */

1610    public synchronized void setLimitationPosition(String JavaDoc pos) {
1611        if (pos.equalsIgnoreCase("LIMITATION_DISABLED")) {
1612            this.limitationPosition = LIMITATION_DISABLED;
1613        } else if (pos.equalsIgnoreCase("LIMITATION_AFTER_TABLE")) {
1614            this.limitationPosition = LIMITATION_AFTER_TABLE;
1615        } else if (pos.equalsIgnoreCase("LIMITATION_AFTER_WHERE")) {
1616            this.limitationPosition = LIMITATION_AFTER_WHERE;
1617        } else if (pos.equalsIgnoreCase("LIMITATION_AFTER_ORDER_BY")) {
1618            this.limitationPosition = LIMITATION_AFTER_ORDER_BY;
1619        } else if (pos.equalsIgnoreCase("LIMITATION_AFTER_SELECT")) {
1620            this.limitationPosition = LIMITATION_AFTER_SELECT;
1621        } else {
1622            log.warn("DB Object '" + getClass().getName() +
1623                    "' illegal string argument for limitation optimisation position.");
1624        }
1625    }
1626
1627    /**
1628     * Programmatically gets the limitation syntax string
1629     * <p/>
1630     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
1631     *
1632     * @return the limitation syntax string
1633     * @since Expresso 4.0
1634     */

1635    public synchronized String JavaDoc getLimitationSyntax() {
1636        return limitationSyntax;
1637    }
1638
1639    /**
1640     * Get the current character escape handler for this class.
1641     *
1642     * @return The <code>EscapeHandler</code> for this data context
1643     * @see com.jcorporate.expresso.core.db.EscapeHandler
1644     * @see com.jcorporate.expresso.core.db.DefaultEscapeHandler
1645     */

1646    public synchronized EscapeHandler getEscapeHandler() {
1647        return this.escapeHandler;
1648    }
1649
1650    /**
1651     * Programmatically gets the limitation syntax string
1652     *
1653     * @param syntax the new syntax
1654     * <p/>
1655     * author Peter Pilgrim, Thu Jun 21 10:30:59 BST 2001
1656     * @since Expresso 4.0
1657     */

1658    public synchronized void setLimitationSyntax(String JavaDoc syntax) {
1659        this.limitationSyntax = syntax;
1660    }
1661
1662    /**
1663     * <p>Sets the SQL keyword to remove duplicate records
1664     * from a database query. Most databases use the
1665     * <code>"DISTINCT" </code> keyword
1666     * (which is the default), other databases use
1667     * <code>"UNIQUE"</code>.</p>
1668     * <p/>
1669     * <p><b>Source</b>: An old tatty copy of
1670     * "Introduction to SQL"</p>
1671     *
1672     * @param keyword the SQL keyword
1673     * author Peter Pilgrim
1674     * @see #getDistinctRowsetKeyword()
1675     * @see com.jcorporate.expresso.core.dbobj.DBObject#searchAndRetrieveList()
1676     * @see com.jcorporate.expresso.core.dbobj.DBObject#searchAndRetrieveList( String )
1677     * @since Expresso 4.0
1678     */

1679    public void setDistinctRowsetKeyword(String JavaDoc keyword) {
1680        uniqueRowKeyword = keyword;
1681    } /* setDistinctRowsetKeyword(String) */
1682
1683    /**
1684     * Gets the SQL keyword to remove duplicate records
1685     * from a database query.
1686     * <p/>
1687     * author Peter Pilgrim
1688     *
1689     * @return The distinct keyword
1690     * @see #setDistinctRowsetKeyword( String )
1691     * @see com.jcorporate.expresso.core.dbobj.DBObject#searchAndRetrieveList()
1692     * @see com.jcorporate.expresso.core.dbobj.DBObject#setFieldDistinct(java.lang.String, boolean))
1693     * @since Expresso 4.0
1694     */

1695    public String JavaDoc getDistinctRowsetKeyword() {
1696        return uniqueRowKeyword;
1697    } /* setDistinctRowsetKeyword() */
1698
1699    /**
1700     * Sets the check zero update boolean flag for this database connection pool
1701     * author Peter Pilgrim
1702     *
1703     * @param newValue true if you want to check for zero updates
1704     */

1705    public void setCheckZeroUpdate(boolean newValue) {
1706        String JavaDoc myName = (THIS_CLASS + "setCheckZeroUpdate() ");
1707        System.out.println(myName + " newValue:" + newValue);
1708        this.checkZeroUpdate = newValue;
1709    }
1710
1711    /**
1712     * Gets the check zero update boolean flag for this database connection pool
1713     * author Peter Pilgrim
1714     *
1715     * @return true if Zero updates are checked for
1716     * @since Expresso 4.0
1717     */

1718    public boolean getCheckZeroUpdate() {
1719        return this.checkZeroUpdate;
1720    }
1721
1722
1723    /**
1724     * Function that retrieves the JNDI config. If this pool is being configured
1725     * by ConfigManager, then we get the old ConfigJNDI version. If we are using
1726     * the new kernel package for configuration, then
1727     *
1728     * @param curConfig The current config that we hold.
1729     * @return a JNDI Configuration object. May be null if none is configured in
1730     * the system.
1731     * @throws ConnectionPoolException upon error getting configuration
1732     */

1733    protected JNDIConfig getJNDIConfig(JDBCConfig curConfig) throws ConnectionPoolException {
1734        if (curConfig instanceof com.jcorporate.expresso.core.misc.ConfigJdbc) {
1735            return ((com.jcorporate.expresso.core.misc.ConfigJdbc) curConfig).getMyJndi();
1736        } else {
1737            RootContainerInterface runtime = ExpressoRuntimeMap.getDefaultRuntime();
1738            LocatorUtils lc = new LocatorUtils(runtime);
1739            JNDIConfig manager = (JNDIConfig) lc
1740                    .locateComponent(this.getDataContext() + ".PersistenceManager.JNDIConfig");
1741            if (manager == null) {
1742                throw new ConnectionPoolException(
1743                        "Unable to locate PersistenceManager for data context: " + this.getDataContext());
1744            }
1745            return null;
1746        }
1747    }
1748
1749    /**
1750     * Return the JDBConfig regardless of initialization by kernel package or
1751     * DefaultInit servlet
1752     *
1753     * @param dataContext The name of the data context to retrieve
1754     * @return The filled out JDBCConfig object.
1755     * @throws ConnectionPoolException upon error getting configuration
1756     */

1757    static protected JDBCConfig getJDBCConfig(String JavaDoc dataContext) throws ConnectionPoolException {
1758        RootContainerInterface runtime = ExpressoRuntimeMap.getDefaultRuntime();
1759
1760        //Check if there is a Default runtime installed or not;
1761
if (runtime == null) {
1762            try {
1763                return ConfigManager.getJdbcRequired(dataContext);
1764            } catch (ConfigurationException ex) {
1765                throw new ConnectionPoolException("Unable to get Database Configuration Information for context "
1766                        + dataContext, ex);
1767            }
1768        } else {
1769            LocatorUtils lc = new LocatorUtils(runtime);
1770            PersistenceManager manager = (PersistenceManager) lc.locateComponent(dataContext + ".PersistenceManager");
1771            if (manager == null) {
1772                throw new ConnectionPoolException(
1773                        "Unable to locate PersistenceManager component for data context " + dataContext);
1774            }
1775
1776            return manager.getDBConfig().getCurrentConfig();
1777        }
1778    }
1779
1780    /**
1781     * Return a list of default wild card characters.
1782     * This can be used to determine if the search criteria supplied by a
1783     * user has wild-card characters in it or is an exact match.
1784     *
1785     * @return A list of the wild-card characters
1786     */

1787    public ArrayList JavaDoc getDefaultWildCards() {
1788        ArrayList JavaDoc newChars = new ArrayList JavaDoc(4);
1789        newChars.add(("%"));
1790        newChars.add(("_"));
1791        newChars.add(("["));
1792        newChars.add(("]"));
1793
1794        return newChars;
1795    }
1796} /* DBConnectionPool */
1797
Popular Tags