KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > smartlib > pool > core > ConnectionPool


1 /*
2  * @(#) ConnectionPool 1.0 02/08/01
3  */

4
5
6 package org.smartlib.pool.core;
7
8 import org.apache.log4j.Logger;
9
10 import java.util.*;
11 import java.sql.*;
12
13
14 /**
15  * This class implements the Pool interface and thus is responsible for
16  * managing a single pool of connections.
17  */

18
19 public class ConnectionPool implements Pool , PoolMonitor {
20
21     private PoolConfig config;
22     private final String JavaDoc name;
23     private final Debugger debug;
24     private int currentPoolSize = 0;
25     private int usedConnections = 0;
26     private Vector connectionList;
27     private Vector connectionListenerList;
28     private Thread JavaDoc pollerThread;
29     private ConnectionProvider connProvider = null;
30     private String JavaDoc validatorQuery = null;
31     private volatile boolean shutDown = false;
32     private Hashtable connectionHash = new Hashtable();
33
34     Logger logger = Logger.getLogger(ConnectionPool.class);
35
36     /**
37      * This method draws a raw connection from the database.
38      */

39     private Connection loadConnection() throws ConnectionPoolException {
40
41
42             try {
43                 Class.forName(config.getDriver());
44             }
45             catch(ClassNotFoundException JavaDoc classNotFound) {
46                 throw new ConnectionPoolException("Could not load Driver",
47                     classNotFound);
48             }
49             Connection con = null;
50             try {
51                 con = DriverManager.getConnection(config.getConnectionStringByName(name).getConnectString(),
52                     config.getUserName(), config.getPassword());
53             }
54             catch (Exception JavaDoc e ) {
55                 throw new ConnectionPoolException("Could not obtain Connection",
56                         e);
57             }
58             currentPoolSize++;
59             return con;
60
61     }
62
63     /**
64      * @return Vector of connections in use.
65      */

66     public Vector getConnectionsInUse() {
67
68         return connectionList;
69
70     }
71
72     /**
73      * @return Vector of registered ConnectionLeakListeners.
74      */

75     public Vector getConnectionLeakListeners() {
76
77         return connectionListenerList;
78
79     }
80
81     /**
82      * @return Number of free connections in the pool.
83      */

84     public int getNoOfFreeConnections() {
85
86         return (currentPoolSize - usedConnections);
87
88     }
89
90     /**
91      * This method returns an instance of ConfigMonitor.
92      * @return ConfigMonitor for monitoring the configuration of the pool at
93      * runtime.
94      */

95     public ConfigMonitor getConfigMonitor() {
96
97         return (ConfigMonitor)config;
98
99     }
100
101     public String JavaDoc getName() {
102         return name;
103     }
104
105     /**
106      * This method draws the initial raw connections.
107      */

108     private void initialiseConnections() throws ConnectionPoolException {
109
110             try {
111                 int minConnections = config.getMinConnections();
112                 for (int i = 0 ; i<minConnections ; i++) {
113                     connectionHash.put(loadConnection(), Boolean.TRUE);
114                 }
115             }
116             catch (Exception JavaDoc e) {
117                 throw new ConnectionPoolException("Could not load initial connection" , e);
118             }
119
120
121     }
122
123     /*
124      * This method is called when the existing connection is wrapped to another
125      * pool.
126      */

127     private void returnConnectionToOtherPool(Connection conn)
128             throws Exception JavaDoc {
129
130         connProvider.returnConnection(conn);
131
132     }
133
134
135     /**
136      * Constructor initialises the pool as per the configuration specified by
137      * <code>config</code>
138      */

139     ConnectionPool(PoolConfig config, String JavaDoc name) throws ConnectionPoolException {
140
141         this.name = name;
142         this.config = config;
143         try {
144             if (config.getConnectionLoaderClass() != null)
145                 connProvider = (ConnectionProvider)getClass().getClassLoader()
146                     .loadClass(config.getConnectionLoaderClassByName(name).getConnectionLoaderClass()).newInstance();
147         }
148         catch (Exception JavaDoc exp) {
149             throw new ConnectionPoolException("Error loading Connection Loader Class",exp);
150         }
151         if (connProvider == null)
152             initialiseConnections();
153
154         debug = new Debugger(name, true);
155         connectionList = new Vector(config.getMinConnections() , config.getIncrement());
156         connectionListenerList = new Vector();
157         try {
158             if ( config.getDefaultListener() != null ) {
159                 String JavaDoc defaultListener = config.getDefaultListener();
160                 addConnectionLeakListener((ConnectionLeakListener)getClass().getClassLoader().loadClass(defaultListener).newInstance());
161             }
162         }
163         catch (Exception JavaDoc e ) {
164             throw new ConnectionPoolException("Could not load class "
165                 + config.getDefaultListener() , e);
166         }
167         if (config.isDetectLeaks()) {
168             pollerThread = new Thread JavaDoc(new ConnectionLeakPollThread(
169                         connectionList , connectionListenerList ,
170                         name, config.getPollThreadTime() ,
171                         config.getLeakTimeOut(), this));
172             pollerThread.start();
173         }
174
175         validatorQuery = config.getValidatorQuery();
176
177     }
178
179     /**
180      * This method returns the current size of the pool.
181      * @return Current size of the pool.
182      */

183     public int getCurrentPoolSize() {
184
185         return currentPoolSize;
186
187     }
188
189     /**
190      * This method returns a Connection from the connection pool.
191      * The owner of this pool is marked as N/A indicating unknown/anonymous.
192      *
193      * <b>Note: This method blocks if the pool size has reached it's
194      * maximum size and no free connections are available
195      * until a free connection is available.</b> The time period for which this
196      * method blocks depends on the connection-wait-time-out specified in
197      * the configuration file.
198      *
199      *
200      * @return Connection from the pool.
201      * @exception ConnectionPoolException if there is any problem
202      * getting connection.
203      */

204     public Connection getConnection() throws ConnectionPoolException {
205
206         if (config.isAllowAnonymousConnections())
207             return getConnection("N/A");
208         else
209             throw new ConnectionPoolException ("You are not allowed to take anonumous connections, please provide an owner name");
210
211     }
212
213     // Checks if the Connection is valid
214
private boolean checkIfValid(Connection conn) {
215
216         try {
217             debug.print(" Checking Connection for '" + validatorQuery + "'");
218             if (validatorQuery != null && !validatorQuery.trim().equals("")) {
219                 Statement stmt = conn.createStatement();
220                 boolean bool = stmt.execute(validatorQuery);
221                 stmt.close();
222                 return bool;
223             }
224             else {
225                 return true;
226             }
227         }
228         catch (SQLException exp) {
229             if (logger.isDebugEnabled()) {
230                 logger.debug("Exception occurred while trying to test connection validity", exp);
231             }
232             return false;
233         }
234
235     }
236
237     /*
238      * This method is called when the existing connection is wrapped to another
239      * pool.
240      */

241     private Connection getConnectionFromOtherPool(String JavaDoc owner)
242             throws ConnectionPoolException {
243
244         try {
245             synchronized (this) {
246                 if (config.getMaxConnections() == usedConnections) {
247                     try {
248                         debug.print("Hey the value is "
249                                 + config.getConnectionWaitTimeOut());
250                         wait(config.getConnectionWaitTimeOut());
251                         if (config.getMaxConnections() == usedConnections) {
252                             throw new TimeOutException("Timed-out while "
253                                 + "waiting for free connection");
254                         }
255                     }
256                     catch (InterruptedException JavaDoc ie) {
257                     }
258                 }
259
260                 Connection conn = connProvider.getConnection();
261                 usedConnections++;
262                 currentPoolSize++;
263                 // Checking if the connection is still live and active
264
// if not get replace the old one with new one
265
if (checkIfValid(conn)) {
266                     SmartConnection smt = new SmartConnection(conn,
267                             this,owner, config.isAutoClose());
268                     connectionList.add(smt);
269                     return smt;
270                 }
271                 else {
272                     boolean valid = false;
273                     int i=1;
274                     while (!valid) {
275                         conn = connProvider.getConnection();
276                         valid = checkIfValid(conn);
277                         i++;
278                         if (i == 3 && !valid)
279                             throw new ConnectionPoolException("Three consecutive cnnections failes the Validator Query org.smartlib.pool.test");
280                     }
281                     SmartConnection smt = new SmartConnection(conn,
282                             this,owner, config.isAutoClose());
283                     connectionList.add(smt);
284                     return smt;
285                 }
286             }
287         }
288         catch (ConnectionPoolException cpe) {
289             throw cpe;
290         }
291         catch (Exception JavaDoc exp) {
292             throw new ConnectionPoolException("Error while getting connections from the Connection Loader Class", exp);
293         }
294
295     }
296
297
298
299     /**
300      * This method returns a Connection from the pool.
301      * The owner of this connection is identified by <code>owner</code> .
302      *
303      * <b>Note: This method blocks if the pool size has reached it's
304      * maximum size and no free connections are available
305      * until a free connection is available</b>. The time period for which this
306      * method blocks depends on the connection-wait-time-out specified in
307      * the configuration file.
308      *
309      *
310      * @param owner String identifying the owner.
311      * @return Connection from the pool
312      *
313      * @exception ConnectionPoolException if there is any problem
314      * getting connection.
315      */

316     public Connection getConnection(String JavaDoc owner)
317             throws ConnectionPoolException {
318
319
320         // Check for external pooling
321
if (connProvider != null )
322             return getConnectionFromOtherPool(owner);
323
324
325         Enumeration cons = connectionHash.keys();
326         //debug.print("Getting Connection " + usedConnections);
327
synchronized (connectionHash) {
328             // Checking if max-conn is less than used connection , if not wait
329
if (config.getMaxConnections() == usedConnections) {
330                 try {
331                     //debug.print("Waiting for Connection " + usedConnections);
332
debug.print("Hey the value is " + config.getConnectionWaitTimeOut());
333                     connectionHash.wait(config.getConnectionWaitTimeOut());
334                     if (config.getMaxConnections() == usedConnections) {
335                         throw new TimeOutException("Timed-out while "
336                             + "waiting for free connection");
337                     }
338
339                 }
340                 catch (InterruptedException JavaDoc ie) {
341                 }
342             }
343
344             // Reached here indicates that free conn is available or
345
// currentpool size is less and thus new conn can be added to pool
346
while (cons.hasMoreElements()) {
347                 // Checking if any unused connection is available
348
Connection con = (Connection)cons.nextElement();
349                 Boolean JavaDoc b = (Boolean JavaDoc)connectionHash.get(con);
350                 if ( b == Boolean.TRUE ) {
351                     //Unused Connection is available
352
connectionHash.put(con , Boolean.FALSE);
353                     usedConnections++;
354                     debug.print("Hey After Incrementing conn " +usedConnections);
355                     //debug.print("Connection Obtained " + usedConnections);
356
// Checking if the connection is still live and active
357
// if not get replace the old one with new one
358
if (checkIfValid(con)) {
359                         SmartConnection smt = new SmartConnection(con,
360                                 this,owner, config.isAutoClose());
361                         connectionList.add(smt);
362                         return smt;
363                     }
364                     else {
365                         boolean valid = false;
366                         int failCounter = 1;
367                         while (!valid) {
368                             connectionHash.remove(con);
369                             con = loadConnection();
370                             connectionHash.put(con , Boolean.FALSE);
371                             failCounter++;
372                             valid = checkIfValid(con);
373                             if (failCounter == 3 && !valid)
374                                 throw new ConnectionPoolException("Three consecutive connections failed the Validator Query org.smartlib.pool.test");
375                         }
376                         SmartConnection smt = new SmartConnection(con,
377                                 this,owner, config.isAutoClose());
378                         connectionList.add(smt);
379                         return smt;
380                     }
381                 }
382             }
383
384             // No Connection available hence increase the pool size
385
int increment = config.getIncrement();
386             Connection c = null;
387             SmartConnection smt = null;
388             for (int i = 0 ; i < increment
389                     && i + currentPoolSize <= config.getMaxConnections(); i++){
390                 c = loadConnection();
391                 boolean valid = checkIfValid(c);
392                 int failCounter = 1;
393                 while (!valid) {
394                     c= loadConnection();
395                     failCounter++;
396                     valid = checkIfValid(c);
397                     if (failCounter == 3 && !valid)
398                         throw new ConnectionPoolException("Three consecutive connections failed the Validator Query org.smartlib.pool.test");
399                 }
400                 if (i==0) {
401                     smt = new SmartConnection(c , this ,
402                             owner , config.isAutoClose());
403                     connectionHash.put(c , Boolean.FALSE);
404                 }
405                 else
406                     connectionHash.put(c , Boolean.TRUE);
407             }
408
409             //debug.print("Connection Incremented " + usedConnections);
410
//debug.print("Pool Size" + currentPoolSize);
411
usedConnections++;
412             connectionList.add(smt);
413             return smt;
414
415         }
416
417     }
418
419     /**
420      * This method releases the connection back to the pool.
421      * @param ret connection to be released
422      */

423     public void returnConnection (Connection ret) {
424
425         if (connProvider != null ) {
426             try {
427                 synchronized (this) {
428                     Connection conn = ((SmartConnection)ret).returnConnection();
429                     returnConnectionToOtherPool(conn);
430                     usedConnections--;
431                     currentPoolSize--;
432                     debug.print("Removed value is " + connectionList.removeElement(ret));
433                     notifyAll();
434                 }
435             }
436             catch (Exception JavaDoc exp) {
437                 // Error while returning
438
debug.print("Error " + exp);
439             }
440             return;
441         }
442
443         Object JavaDoc tempRef = ret;
444         SmartConnection smt = (SmartConnection)ret;
445         ret = smt.returnConnection();
446         Connection con;
447         Enumeration cons = connectionHash.keys();
448         synchronized(connectionHash) {
449             while (cons.hasMoreElements()) {
450                 con = (Connection)cons.nextElement();
451                 if (con == ret) {
452                     connectionHash.put(con, Boolean.TRUE);
453                     break;
454                 }
455             }
456             debug.print("Connection Released " + usedConnections);
457             usedConnections--;
458             debug.print("Connection contains list " + connectionList.contains(tempRef));
459             debug.print("Removed value is " + connectionList.removeElement(tempRef));
460
461             connectionHash.notifyAll();
462         }
463
464     }
465
466     /**
467      * This method adds a connection leak listener. The methods of
468      * <code>cle</code> will be called when a leak is detected as per the
469      * pool configuration.
470      *
471      * @param cle Class implementing ConnectionLeakListener interface.
472      * @exception ConnectionPoolException if there is any problem
473      * adding ConnectionLeakListener.
474      */

475     public void addConnectionLeakListener(ConnectionLeakListener cle )
476             throws ConnectionPoolException {
477
478         if (cle == null)
479             throw new IllegalArgumentException JavaDoc("ConnectionLeakListener cannot be null");
480         debug.print("Added is " + cle);
481         connectionListenerList.add(cle);
482
483     }
484
485     /**
486      * This method removes a connection leak listener. <code>cle</code> will
487      * not get any further notifications.
488      *
489      * @param cle Class implementing ConnectionLeakListener interface.
490      * @exception ConnectionPoolException If there is any problem
491      * removing ConnectionLeakListener.
492      */

493     public void removeConnectionLeakListener(ConnectionLeakListener cle)
494             throws ConnectionPoolException {
495
496             if (cle == null )
497                 throw new IllegalArgumentException JavaDoc("ConnectionLeakListener cannot be null");
498             debug.print("Trying to remove " + cle);
499             boolean found = connectionListenerList.remove(cle);
500             if (!found)
501                 throw new ConnectionPoolException("No Such Listener");
502
503     }
504
505     /**
506      * This method releases excessive connections, i.e it actully closes
507      * them.
508      */

509     public void releaseConnections() {
510
511
512         if (config.getMaxConnectionsForRelease() == -1)
513             return ;
514         if (config.getMaxConnectionsForRelease()
515                 < getNoOfFreeConnections()) {
516             int i = config.getIncrement();
517             synchronized (connectionHash) {
518                 Enumeration cons = connectionHash.keys();
519                 while (cons.hasMoreElements() && i > 0) {
520                     Connection con = (Connection)cons.nextElement();
521                     Boolean JavaDoc b = (Boolean JavaDoc)connectionHash.get(con);
522                     if (b == Boolean.TRUE) {
523                         connectionHash.remove(con);
524                         try {
525                             con.close();
526                             i--;
527                             currentPoolSize = connectionHash.size();
528                             debug.print("Releasing conn" + con);
529                         }
530                         catch (SQLException e) {
531                             debug.print("Error in closing connection" + e );
532                         }
533                     }
534                 }
535             }
536         }
537
538     }
539
540     public void shutDown() {
541         if (logger.isDebugEnabled()) {
542             logger.debug("Shutting down connections for pool:" + name);
543         }
544         shutDown = true;
545         Enumeration cons = connectionHash.keys();
546         while (cons.hasMoreElements()) {
547             Connection con = (Connection)cons.nextElement();
548             try {
549                 con.close();
550             }
551             catch (Exception JavaDoc e) {
552                 logger.warn("Exception occurred during connections close", e);
553             }
554         }
555
556     }
557
558
559
560
561
562 }
563
Popular Tags