KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > snaq > db > ConnectionPool


1 /*
2     DBPool - JDBC Connection Pool Manager
3     Copyright (c) Giles Winstanley
4 */

5 package snaq.db;
6
7 import snaq.util.*;
8 import java.sql.*;
9 import java.util.*;
10
11 /**
12  * Implementation of a database connection pool.
13  * @see snaq.db.CacheConnection
14  * @see snaq.db.CachedCallableStatement
15  * @see snaq.db.CachedPreparedStatement
16  * @author Giles Winstanley
17  */

18 public class ConnectionPool extends ObjectPool implements Comparable JavaDoc
19 {
20     private String JavaDoc url, user, pass;
21     private Properties props;
22     private ConnectionValidator validator = new DefaultValidator();
23     private PasswordDecoder decoder;
24     private boolean cacheSS, cachePS, cacheCS;
25     private List listeners = new ArrayList();
26
27
28     /**
29      * Creates new connection pool.
30      * @param name pool name
31      * @param poolSize maximum number of pooled objects, or 0 for no limit
32      * @param maxSize maximum number of possible objects, or 0 for no limit
33      * @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
34      * @param url JDBC connection URL
35      * @param username database username
36      * @param password password for the database username supplied
37      */

38     public ConnectionPool(String JavaDoc name, int poolSize, int maxSize, long expiryTime, String JavaDoc url, String JavaDoc username, String JavaDoc password)
39     {
40         super(name, poolSize, maxSize, expiryTime);
41         this.url = url;
42         this.user = username;
43         this.pass = password;
44         this.props = null;
45         setCaching(true);
46         addObjectPoolListener(new EventRelay());
47     }
48
49     /**
50      * Creates new connection pool.
51      * @param name pool name
52      * @param poolSize maximum number of pooled objects, or 0 for no limit
53      * @param maxSize maximum number of possible objects, or 0 for no limit
54      * @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
55      * @param url JDBC connection URL
56      * @param props connection properties
57      */

58     public ConnectionPool(String JavaDoc name, int poolSize, int maxSize, long expiryTime, String JavaDoc url, Properties props)
59     {
60         this(name, poolSize, maxSize, expiryTime, url, null, null);
61         this.props = props;
62         this.pass = props.getProperty("password");
63         addObjectPoolListener(new EventRelay());
64     }
65
66
67     /**
68      * Creates a new database connection.
69      */

70     protected Reusable create() throws SQLException
71     {
72         Connection con = null;
73         CacheConnection ccon = null;
74         try
75         {
76             if (props != null)
77             {
78                 if (decoder != null)
79                     props.setProperty("password", new String JavaDoc(decoder.decode(pass)));
80                 log("Getting connection (properties): " + url);
81                 con = DriverManager.getConnection(url, props);
82             }
83             else if (user != null)
84             {
85                 try
86                 {
87                     if (decoder != null)
88                     {
89                         log("Getting connection (user/enc.pass): " + url);
90                         con = DriverManager.getConnection(url, user, new String JavaDoc(decoder.decode(pass)));
91                     }
92                     else
93                     {
94                         log("Getting connection (user/pass): " + url);
95                         con = DriverManager.getConnection(url, user, pass);
96                     }
97                 }
98                 catch (SQLException sqle)
99                 {
100                     log("Failed to connect with standard authentication...trying with just JDBC URL");
101                     log("Getting connection (URL only): " + url);
102                     con = DriverManager.getConnection(url);
103                 }
104             }
105             else
106                 con = DriverManager.getConnection(url);
107
108             // Add caching wrapper to connection
109
ccon = new CacheConnection(this, con);
110             ccon.setCacheStatements(cacheSS);
111             ccon.setCachePreparedStatements(cachePS);
112             ccon.setCacheCallableStatements(cacheCS);
113             log("Created a new connection");
114
115             // Check for warnings
116
SQLWarning warn = con.getWarnings();
117             while (warn != null)
118             {
119                 log("Warning - " + warn.getMessage());
120                 warn = warn.getNextWarning();
121             }
122         }
123         catch (SQLException sqle)
124         {
125             log(sqle, "Can't create a new connection for " + url);
126             // Clean up open connection.
127
try { con.close(); }
128             catch (SQLException sqle2) {}
129             // Rethrow exception.
130
throw sqle;
131         }
132         return ccon;
133     }
134
135     /**
136      * Validates a connection.
137      */

138     protected boolean isValid(final Reusable o)
139     {
140         if (o == null)
141             return false;
142         if (validator == null)
143             return true;
144
145         try
146         {
147             boolean valid = validator.isValid((Connection)o);
148             if (!valid)
149                 fireValidationErrorEvent();
150             return valid;
151         }
152         catch (Exception JavaDoc e) { log(e, "Exception during validation"); return false; }
153     }
154
155     /**
156      * Sets the validator class for connections.
157      */

158     public void setValidator(ConnectionValidator cv) { validator = cv; }
159
160     /**
161      * Returns the current validator class.
162      */

163     public ConnectionValidator getValidator() { return validator; }
164
165     /**
166      * Sets the password decoder class.
167      */

168     public void setPasswordDecoder(PasswordDecoder pd) { decoder = pd; }
169
170     /**
171      * Returns the current password decoder class.
172      */

173     public PasswordDecoder getPasswordDecoder() { return decoder; }
174
175     /**
176      * Closes the given connection.
177      */

178     protected void destroy(final Reusable o)
179     {
180         if (o == null)
181             return;
182         try
183         {
184             ((CacheConnection)o).release();
185             log("Destroyed connection");
186         }
187         catch (SQLException e)
188         {
189             log(e, "Can't destroy connection");
190         }
191     }
192
193     /**
194      * Gets a connection from the pool.
195      */

196     public Connection getConnection() throws SQLException
197     {
198         try
199         {
200             Reusable o = super.checkOut();
201             if (o != null)
202             {
203                 CacheConnection cc = (CacheConnection)o;
204                 cc.setOpen();
205                 return cc;
206             }
207             return null;
208         }
209         catch (Exception JavaDoc e)
210         {
211             log(e, "Error getting connection");
212             if (e instanceof SQLException)
213                 throw (SQLException)e;
214             else
215             {
216                 Throwable JavaDoc t = e.getCause();
217                 while (t != null)
218                 {
219                     log(e, "Error getting connection");
220                     t = t.getCause();
221                 }
222                 throw new SQLException(e.getMessage());
223             }
224         }
225     }
226
227     /**
228      * Gets a connection from the pool.
229      */

230     public Connection getConnection(long timeout) throws SQLException
231     {
232         try
233         {
234             Object JavaDoc o = super.checkOut(timeout);
235             if (o != null)
236             {
237                 CacheConnection cc = (CacheConnection)o;
238                 cc.setOpen();
239                 return cc;
240             }
241             return null;
242         }
243         catch (Exception JavaDoc e)
244         {
245             if (e instanceof SQLException)
246                 throw (SQLException)e;
247             else
248             {
249                 log(e, "Error getting connection");
250                 throw new SQLException(e.getMessage());
251             }
252         }
253     }
254
255     /**
256      * Returns a connection to the pool (for internal use only).
257      * Connections obtained from the pool should be returned by calling the
258      * close() method on the connection.
259      */

260     protected void freeConnection(Connection c) throws SQLException
261     {
262         if (c == null || !CacheConnection.class.isInstance(c))
263             log("Attempt to return invalid item");
264         else
265         {
266             CacheConnection cc = (CacheConnection)c;
267             super.checkIn((Reusable)c);
268         }
269     }
270
271     /**
272      * Determines whether to perform statement caching.
273      * This applies to all types of statements (normal, prepared, callable).
274      */

275     public void setCaching(boolean b)
276     {
277         cacheSS = cachePS = cacheCS = b;
278     }
279
280     /**
281      * Determines whether to perform statement caching.
282      * @param ss whether to cache Statement objects
283      * @param ps whether to cache PreparedStatement objects
284      * @param cs whether to cache CallableStatement objects
285      */

286     public void setCaching(boolean ss, boolean ps, boolean cs)
287     {
288         cacheSS = ss;
289         cachePS = ps;
290         cacheCS = cs;
291     }
292
293     /** Returns a descriptive string for this pool instance. */
294     public String JavaDoc toString() { return getName(); }
295     /** Compares this instances to other instances by name. */
296     public int compareTo(Object JavaDoc o) { return this.toString().compareTo(((ConnectionPool)o).toString()); }
297
298     //**************************
299
// Event-handling methods
300
//**************************
301

302     /**
303      * Adds an ConnectionPoolListener to the event notification list.
304      */

305     public final void addConnectionPoolListener(ConnectionPoolListener x)
306     {
307         listeners.add(x);
308     }
309
310     /**
311      * Removes an ConnectionPoolListener from the event notification list.
312      */

313     public final void removeConnectionPoolListener(ConnectionPoolListener x)
314     {
315         listeners.remove(x);
316     }
317
318     private final void fireValidationErrorEvent()
319     {
320         if (listeners.isEmpty())
321             return;
322         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.VALIDATION_ERROR);
323         for (Iterator iter = listeners.iterator(); iter.hasNext();)
324             ((ConnectionPoolListener)iter.next()).validationError(poolEvent);
325     }
326
327     private final void firePoolCheckOutEvent()
328     {
329         if (listeners.isEmpty())
330             return;
331         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.CHECKOUT);
332         for (Iterator iter = listeners.iterator(); iter.hasNext();)
333             ((ConnectionPoolListener)iter.next()).poolCheckOut(poolEvent);
334     }
335
336     private final void firePoolCheckInEvent()
337     {
338         if (listeners.isEmpty())
339             return;
340         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.CHECKIN);
341         for (Iterator iter = listeners.iterator(); iter.hasNext();)
342             ((ConnectionPoolListener)iter.next()).poolCheckIn(poolEvent);
343     }
344
345     private final void fireMaxPoolLimitReachedEvent()
346     {
347         if (listeners.isEmpty())
348             return;
349         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_POOL_LIMIT_REACHED);
350         for (Iterator iter = listeners.iterator(); iter.hasNext();)
351             ((ConnectionPoolListener)iter.next()).maxPoolLimitReached(poolEvent);
352     }
353
354     private final void fireMaxPoolLimitExceededEvent()
355     {
356         if (listeners.isEmpty())
357             return;
358         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_POOL_LIMIT_EXCEEDED);
359         for (Iterator iter = listeners.iterator(); iter.hasNext();)
360             ((ConnectionPoolListener)iter.next()).maxPoolLimitExceeded(poolEvent);
361     }
362
363     private final void fireMaxSizeLimitReachedEvent()
364     {
365         if (listeners.isEmpty())
366             return;
367         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_SIZE_LIMIT_REACHED);
368         for (Iterator iter = listeners.iterator(); iter.hasNext();)
369             ((ConnectionPoolListener)iter.next()).maxSizeLimitReached(poolEvent);
370     }
371
372     private final void fireMaxSizeLimitErrorEvent()
373     {
374         if (listeners.isEmpty())
375             return;
376         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_SIZE_LIMIT_ERROR);
377         for (Iterator iter = listeners.iterator(); iter.hasNext();)
378             ((ConnectionPoolListener)iter.next()).maxSizeLimitError(poolEvent);
379     }
380
381     private final void fireParametersChangedEvent()
382     {
383         if (listeners == null || listeners.isEmpty())
384             return;
385         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.PARAMETERS_CHANGED);
386         for (Iterator iter = listeners.iterator(); iter.hasNext();)
387             ((ConnectionPoolListener)iter.next()).poolParametersChanged(poolEvent);
388     }
389
390     private final void firePoolReleasedEvent()
391     {
392         if (listeners.isEmpty())
393             return;
394         ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.POOL_RELEASED);
395         for (Iterator iter = listeners.iterator(); iter.hasNext();)
396             ((ConnectionPoolListener)iter.next()).poolReleased(poolEvent);
397     }
398
399
400     /**
401      * Class to relay ObjectPoolEvents as ConnectionPoolEvents for convenience.
402      */

403     private final class EventRelay extends ObjectPoolEventAdapter
404     {
405         public void poolCheckOut(ObjectPoolEvent evt) { firePoolCheckOutEvent(); }
406         public void poolCheckIn(ObjectPoolEvent evt) { firePoolCheckInEvent(); }
407         public void maxPoolLimitReached(ObjectPoolEvent evt) { fireMaxPoolLimitReachedEvent(); }
408         public void maxPoolLimitExceeded(ObjectPoolEvent evt) { fireMaxPoolLimitExceededEvent(); }
409         public void maxSizeLimitReached(ObjectPoolEvent evt) { fireMaxSizeLimitReachedEvent(); }
410         public void maxSizeLimitError(ObjectPoolEvent evt) { fireMaxSizeLimitErrorEvent(); }
411         public void poolParametersChanged(ObjectPoolEvent evt) { fireParametersChangedEvent(); }
412         public void poolReleased(ObjectPoolEvent evt) { firePoolReleasedEvent(); listeners.clear(); }
413     }
414
415
416     /**
417      * Default implementation of ConnectionValidator.
418      * This class simply checks a Connection with the <tt>isClosed()</tt> method.
419      */

420     static class DefaultValidator implements ConnectionValidator
421     {
422         /**
423          * Validates a connection.
424          */

425         public boolean isValid(Connection con)
426         {
427             try { return !con.isClosed(); }
428             catch (SQLException e) { return false; }
429         }
430     }
431 }
Popular Tags