KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > proxool > ProxoolFacade


1 /*
2  * This software is released under a licence similar to the Apache Software Licence.
3  * See org.logicalcobwebs.proxool.package.html for details.
4  * The latest version is available at http://proxool.sourceforge.net
5  */

6 package org.logicalcobwebs.proxool;
7
8 import org.apache.commons.logging.Log;
9 import org.apache.commons.logging.LogFactory;
10 import org.logicalcobwebs.proxool.admin.Admin;
11 import org.logicalcobwebs.proxool.admin.SnapshotIF;
12 import org.logicalcobwebs.proxool.admin.StatisticsIF;
13 import org.logicalcobwebs.proxool.admin.StatisticsListenerIF;
14
15 import java.lang.reflect.Method JavaDoc;
16 import java.sql.Connection JavaDoc;
17 import java.sql.Statement JavaDoc;
18 import java.util.Collection JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Properties JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.ArrayList JavaDoc;
25
26 /**
27  * <p>This provides some nice-to-have features that can't be provided by the
28  * {@link java.sql.Driver} {@link ProxoolDriver implementation} of java.sql.Driver. Like starting up
29  * a pool before you need a connection. And getting statistical information.</p>
30  *
31  * <p>You need to use this class wisely. It is obviously specfic to proxool so it will
32  * stop you switching to another driver. Consider isolating the code that calls this
33  * class so that you can easily remove it if you have to.</p>
34  *
35  * @version $Revision: 1.85 $, $Date: 2006/11/02 10:00:34 $
36  * @author billhorsman
37  * @author $Author: billhorsman $ (current maintainer)
38  */

39 public class ProxoolFacade {
40
41     private static final Log LOG = LogFactory.getLog(ProxoolFacade.class);
42
43     private static Map JavaDoc configurators = new HashMap JavaDoc();
44
45     private static CompositeProxoolListener compositeProxoolListener = new CompositeProxoolListener();
46
47     private static boolean versionLogged = false;
48
49     /**
50      * This is the thread that has been registered with {@link Runtime} as a
51      * shutdownHook. It is removed during shutdown.
52      */

53     private static Thread JavaDoc shutdownHook;
54
55     /**
56      * If you setthis to false then it us up to you to call shutdown explicitly
57      * @see #shutdown(String, int)
58      */

59     private static boolean shutdownHookEnabled = true;
60
61     /**
62      * Build a ConnectionPool based on this definition and then start it.
63      * @param url defines the delegate driver and delegate url.
64      * @param info the properties used to configure Proxool (and any for the delegate driver too) - optional
65      * @return the alias for this pool (or the full url if no alias is specified)
66      * @throws ProxoolException if anything goes wrong
67      */

68     public static synchronized String JavaDoc registerConnectionPool(String JavaDoc url, Properties JavaDoc info) throws ProxoolException {
69         return registerConnectionPool(url, info, true);
70     }
71
72     /**
73      * Build a ConnectionPool based on this definition and then start it.
74      * @param url defines the delegate driver and delegate url.
75      * @param info the properties used to configure Proxool (and any for the delegate driver too) - optional
76      * @param explicitRegister set to true if we are registering a new pool explicitly, or false
77      * if it's just because we are serving a url that we haven't come across before
78      * @return the alias for this pool (or the full url if no alias is specified)
79      * @throws ProxoolException if anything goes wrong
80      */

81     protected static synchronized String JavaDoc registerConnectionPool(String JavaDoc url, Properties JavaDoc info, boolean explicitRegister) throws ProxoolException {
82         String JavaDoc alias = getAlias(url);
83
84         if (!versionLogged) {
85             versionLogged = true;
86             LOG.info("Proxool " + Version.getVersion());
87         }
88
89         try {
90             Class.forName(ProxoolDriver.class.getName());
91         } catch (ClassNotFoundException JavaDoc e) {
92             LOG.error("Couldn't load " + ProxoolDriver.class.getName());
93         }
94
95         if (!ConnectionPoolManager.getInstance().isPoolExists(alias)) {
96             ConnectionPoolDefinition cpd = new ConnectionPoolDefinition(url, info, explicitRegister);
97             registerConnectionPool(cpd);
98         } else {
99             throw new ProxoolException("Attempt to register duplicate pool called '" + alias + "'");
100         }
101
102         return alias;
103     }
104
105     protected synchronized static void registerConnectionPool(ConnectionPoolDefinition connectionPoolDefinition) throws ProxoolException {
106         // check isPoolExists once more now we are inside synchronized block.
107
if (!ConnectionPoolManager.getInstance().isPoolExists(connectionPoolDefinition.getAlias())) {
108             Properties JavaDoc jndiProperties = extractJndiProperties(connectionPoolDefinition);
109             ConnectionPool connectionPool = ConnectionPoolManager.getInstance().createConnectionPool(connectionPoolDefinition);
110             connectionPool.start();
111             compositeProxoolListener.onRegistration(connectionPoolDefinition, connectionPoolDefinition.getCompleteInfo());
112             if (isConfiguredForJMX(connectionPoolDefinition.getCompleteInfo())) {
113                 registerForJmx(connectionPoolDefinition.getAlias(), connectionPoolDefinition.getCompleteInfo());
114             }
115             if (jndiProperties != null) {
116                 registerDataSource(connectionPoolDefinition.getAlias(), jndiProperties);
117             }
118         } else {
119             LOG.debug("Ignoring duplicate attempt to register " + connectionPoolDefinition.getAlias() + " pool");
120         }
121     }
122
123     /**
124      * With no configurator or properties (using default values)
125      * @see #registerConnectionPool(java.lang.String, java.util.Properties)
126      */

127     public static void registerConnectionPool(String JavaDoc url) throws ProxoolException {
128         registerConnectionPool(url, null);
129     }
130
131     /**
132      * Extracts the pool alias from the url:
133      *
134      * proxool.alias:driver:url -> alias
135      * proxool.alias -> alias
136      *
137      * @return the alias defined within the url
138      * @throws ProxoolException if we couldn't find the alias
139      */

140     protected static String JavaDoc getAlias(String JavaDoc url) throws ProxoolException {
141         String JavaDoc alias = null;
142         final String JavaDoc prefix = ProxoolConstants.PROXOOL + ProxoolConstants.ALIAS_DELIMITER;
143
144         // Check that the prefix is there
145
if (url.startsWith(prefix)) {
146
147             // Check for the alias
148
int endOfPrefix = url.indexOf(ProxoolConstants.URL_DELIMITER);
149
150             if (endOfPrefix > -1) {
151                 alias = url.substring(prefix.length(), endOfPrefix);
152             } else {
153                 alias = url.substring(prefix.length());
154             }
155         }
156
157         // Check we found it.
158
if (alias == null || alias.length() == 0) {
159             throw new ProxoolException("The URL '" + url + "' is not in the correct form. It should be: 'proxool.alias:driver:url'");
160         }
161
162         return alias;
163     }
164
165     /**
166      * Remove a connection pool. Kills all the connections. Resets everything.
167      * @param finalizer the name of the thread requesting shutdown (for logging)
168      * @param connectionPool the pool to remove
169      * @param delay the time to wait for connections to become inactive before killing it (milliseconds)
170      */

171     private static void removeConnectionPool(String JavaDoc finalizer, ConnectionPool connectionPool, int delay) {
172         final String JavaDoc alias = connectionPool.getDefinition().getAlias();
173         if (connectionPool != null) {
174             try {
175                 compositeProxoolListener.onShutdown(alias);
176                 connectionPool.shutdown(delay, finalizer);
177             } catch (Throwable JavaDoc t) {
178                 LOG.error("Problem trying to shutdown '" + alias + "' connection pool", t);
179             }
180         }
181         connectionPool = null;
182     }
183
184     /**
185      * Remove a connection pool. Kills all the connections. Resets everything.
186      * @param alias the pool to remove
187      * @param delay the time to wait for connections to become inactive before killing it (milliseconds)
188      * @throws ProxoolException if we couldn't find the pool
189      */

190     public static void removeConnectionPool(String JavaDoc alias, int delay) throws ProxoolException {
191         removeConnectionPool(Thread.currentThread().getName(), ConnectionPoolManager.getInstance().getConnectionPool(alias), delay);
192     }
193
194     /**
195      * Removes all connection pools. Kills all the connections. Resets everything.
196      * @param delay the time to wait for connections to become inactive before killing it (milliseconds)
197      * @deprecated use the better named {@link #shutdown(int) shutdown()} instead.
198      */

199     public static void removeAllConnectionPools(int delay) {
200         shutdown(Thread.currentThread().getName(), delay);
201     }
202
203     /**
204      * Removes all connection pools. Kills all the connections. Resets everything.
205      * Like {@link #shutdown(java.lang.String, int)} but passes the current thread name
206      * and a delay of zero.
207      */

208     public static void shutdown() {
209         shutdown(Thread.currentThread().getName(), 0);
210     }
211
212     /**
213      * Removes all connection pools. Kills all the connections. Resets everything.
214      * Like {@link #shutdown(java.lang.String, int)} but passes the current thread name.
215      * @param delay the time to wait for connections to become inactive before killing it (milliseconds)
216      */

217     public static void shutdown(int delay) {
218         shutdown(Thread.currentThread().getName(), delay);
219     }
220
221     /**
222      * Removes all connection pools. Kills all the connections. Resets everything.
223      * @param finalizer used to identify who is causing the pools to be removed (helps logging)
224      * @param delay the time to wait for connections to become inactive before killing it (milliseconds)
225      */

226     protected static void shutdown(String JavaDoc finalizer, int delay) {
227
228         ConnectionPool[] cps = ConnectionPoolManager.getInstance().getConnectionPools();
229         for (int i = 0; i < cps.length; i++) {
230             removeConnectionPool(finalizer, cps[i], delay);
231         }
232
233         // If a shutdown hook was registered then remove it
234
try {
235             if (shutdownHook != null) {
236                 ShutdownHook.remove(shutdownHook);
237             }
238         } catch (Throwable JavaDoc t) {
239             if (LOG.isDebugEnabled()) {
240                 LOG.debug("Unanticipated error during removal of ShutdownHook. Ignoring it.", t);
241             }
242         }
243
244         // Stop threads
245
PrototyperController.shutdown();
246         HouseKeeperController.shutdown();
247
248     }
249
250     /**
251      * If you call this then you'll have to call shutdown explicitly
252      * @see #shutdown(String, int)
253      */

254     public static void disableShutdownHook() {
255         ProxoolFacade.shutdownHookEnabled = false;
256     }
257
258     /**
259      * Call this if you change your mind about {@link #disableShutdownHook() disabling} it.
260      * The default behaviour is to have it enabled so unless you have disabled it then
261      * there's nothing to do.
262      */

263     public static void enableShutdownHook() {
264         ProxoolFacade.shutdownHookEnabled = true;
265     }
266
267     /**
268      * Whether the {@link ShutdownHook} should do anything.
269      * @see #disableShutdownHook()
270      * @see #enableShutdownHook()
271      * @return true if the shutdown hook should clean up
272      */

273     public static boolean isShutdownHookEnabled() {
274         return shutdownHookEnabled;
275     }
276
277     /**
278      * Like {@link #removeConnectionPool(java.lang.String, int)} but uses no delay. (Kills
279      * everything as quickly as possible).
280      * @param alias to identify the pool
281      * @throws ProxoolException if we couldn't find the pool
282      */

283     public static void removeConnectionPool(String JavaDoc alias) throws ProxoolException {
284         removeConnectionPool(alias, 0);
285     }
286
287     /**
288      * Get real-time statistical information about how a pool is performing.
289      * @param alias to identify the pool
290      * @return the statistics
291      * @throws ProxoolException if we couldn't find the pool
292      * @deprecated use {@link #getSnapshot}
293      */

294     public static ConnectionPoolStatisticsIF getConnectionPoolStatistics(String JavaDoc alias) throws ProxoolException {
295         return ConnectionPoolManager.getInstance().getConnectionPool(alias);
296     }
297
298     /**
299      * Get real-time statistical information about how a pool is performing.
300      * and extract the information piece by piece.
301      * @param alias to identify the pool
302      * @return a horrible string describing the statistics
303      * @throws ProxoolException if we couldn't find the pool
304      * @deprecated use {@link #getSnapshot}
305      */

306     public static String JavaDoc getConnectionPoolStatisticsDump(String JavaDoc alias) throws ProxoolException {
307         return ConnectionPoolManager.getInstance().getConnectionPool(alias).displayStatistics();
308     }
309
310     /**
311      * Get the definition of a pool.
312      * @param alias identifies the pool
313      * @throws ProxoolException if we couldn't find the pool
314      */

315     public static ConnectionPoolDefinitionIF getConnectionPoolDefinition(String JavaDoc alias) throws ProxoolException {
316         return ConnectionPoolManager.getInstance().getConnectionPool(alias).getDefinition();
317     }
318
319     /**
320      * Get details on each connection within the pool. This can tell you which ones are active, how long they have
321      * been active, etc.
322      * @param alias identifies the pool
323      * @return a collection of {@link ConnectionInfoIF ConnectionInfoIFs}
324      * @throws ProxoolException if we couldn't find the pool
325      * @deprecated use {@link #getSnapshot(java.lang.String, boolean) snapshot} instead.
326      */

327     public static Collection JavaDoc getConnectionInfos(String JavaDoc alias) throws ProxoolException {
328         return ConnectionPoolManager.getInstance().getConnectionPool(alias).getConnectionInfos();
329     }
330
331     /**
332      * Kill all connections in a pool. The pool continues to work however, and new connections will be
333      * made as required.
334      * @param alias the pool containing the connection
335      * @param merciful if true will only kill connections that aren't active
336      * @throws ProxoolException if we couldn't find the pool
337      * @deprecated use {@link #killAllConnections(java.lang.String, java.lang.String, boolean) alternative}
338      * to provide better auditing in log
339      */

340     public static void killAllConnections(String JavaDoc alias, boolean merciful) throws ProxoolException {
341         killAllConnections(alias, "of thread " + Thread.currentThread().getName(), merciful);
342     }
343
344     /**
345      * Kill all connections in a pool. The pool continues to work however, and new connections will be
346      * made as required.
347      * @param alias the pool containing the connection
348      * @param reason provides audit in log of why connections were killed
349      * @param merciful if true will only kill connections that aren't active
350      * @throws ProxoolException if we couldn't find the pool
351      */

352     public static void killAllConnections(String JavaDoc alias, String JavaDoc reason, boolean merciful) throws ProxoolException {
353         ConnectionPoolManager.getInstance().getConnectionPool(alias).expireAllConnections(reason, merciful);
354     }
355
356     /**
357      * Like {@link #killAllConnections} but defaults to merciful.
358      * @param alias to identify the pool
359      * @throws ProxoolException if we couldn't find the pool
360      * @deprecated use {@link #killAllConnections(java.lang.String, java.lang.String) alternative}
361      * to provide better auditing in log
362      */

363     public static void killAllConnections(String JavaDoc alias) throws ProxoolException {
364         killAllConnections(alias, "of thread " + Thread.currentThread().getName(), MERCIFUL);
365     }
366
367     /**
368      * Like {@link #killAllConnections} but defaults to merciful.
369      * @param alias to identify the pool
370      * @param reason provides audit in log of why connections were killed
371      * @throws ProxoolException if we couldn't find the pool
372      */

373     public static void killAllConnections(String JavaDoc alias, String JavaDoc reason) throws ProxoolException {
374         killAllConnections(alias, reason, MERCIFUL);
375     }
376
377     /**
378      * Kill a single connection
379      * @param alias the pool containing the connection
380      * @param id the id of the specific connection
381      * @param merciful if true will only kill connections that aren't active
382      * @return true if the connection was killed, or false if it couldn't be found.
383      * @throws ProxoolException if we couldn't find the pool
384      */

385     public static boolean killConnecton(String JavaDoc alias, long id, boolean merciful) throws ProxoolException {
386         // Let's be explicit about what we're doing here
387
boolean forceExpiry = !merciful;
388         return ConnectionPoolManager.getInstance().getConnectionPool(alias).expireConnection(id, forceExpiry);
389     }
390
391     /**
392      * Kill a single connection
393      * @param connection the connection to kill
394      * @param merciful if true will only kill connections that aren't active
395      * @return true if the connection was killed, or false if it couldn't be found.
396      * @throws ProxoolException if we didn't recognise the connection
397      */

398     public static boolean killConnecton(Connection JavaDoc connection, boolean merciful) throws ProxoolException {
399         WrappedConnection wrappedConnection = ProxyFactory.getWrappedConnection(connection);
400         if (wrappedConnection != null) {
401             long id = wrappedConnection.getId();
402             String JavaDoc alias = wrappedConnection.getAlias();
403             return killConnecton(alias, id, merciful);
404         } else {
405             throw new ProxoolException("Attempt to kill unrecognised exception " + connection);
406         }
407     }
408
409     /**
410      * Add a listener that gets called everytime a global Proxool event ocours.
411      * @param proxoolListener the listener to add.
412      */

413     public static void addProxoolListener(ProxoolListenerIF proxoolListener) {
414         compositeProxoolListener.addListener(proxoolListener);
415     }
416
417     /**
418      * Remove a registered <code>ProxoolListenerIF</code>.
419      * @param proxoolListener the listener to remove.
420      * @return whether the listener was found or removed or not.
421      */

422     public static boolean removeProxoolListener(ProxoolListenerIF proxoolListener) {
423         return compositeProxoolListener.removeListener(proxoolListener);
424     }
425
426     /**
427      * @deprecated use {@link #addStateListener(String, StateListenerIF)} instead.
428      */

429     public static void setStateListener(String JavaDoc alias, StateListenerIF stateListener) throws ProxoolException {
430         addStateListener(alias, stateListener);
431     }
432
433     /**
434      * Add a listener that monitors the change of state of the pool (quiet, busy, overloaded, or down)
435      * @param alias identifies the pool
436      * @param stateListener the new listener
437      * @throws ProxoolException if we couldn't find the pool
438      */

439     public static void addStateListener(String JavaDoc alias, StateListenerIF stateListener) throws ProxoolException {
440         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
441         cp.addStateListener(stateListener);
442     }
443
444     /**
445      * Remove a listener that monitors the change of state of the pool (quiet, busy, overloaded, or down)
446      * @param alias identifies the pool
447      * @param stateListener the listener to be removed.
448      * @throws ProxoolException if we couldn't find the pool
449      * @return wether the listnener was found and removed or not.
450      */

451     public boolean removeStateListener(String JavaDoc alias, StateListenerIF stateListener) throws ProxoolException {
452         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
453         return cp.removeStateListener(stateListener);
454     }
455
456     /**
457      * @deprecated use {@link #addConnectionListener(String, ConnectionListenerIF)} instead.
458      */

459     public static void setConnectionListener(String JavaDoc alias, ConnectionListenerIF connectionListener) throws ProxoolException {
460         addConnectionListener(alias, connectionListener);
461     }
462
463     /**
464      * Add a listener that monitors each time a connection is made or destroyed.
465      * @param alias identifies the pool
466      * @param connectionListener the new listener
467      * @throws ProxoolException if we couldn't find the pool
468      */

469     public static void addConnectionListener(String JavaDoc alias, ConnectionListenerIF connectionListener) throws ProxoolException {
470         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
471         cp.addConnectionListener(connectionListener);
472     }
473
474     /**
475      * Remove a listener that monitors each time a connection is made or destroyed.
476      * @param alias identifies the pool
477      * @param connectionListener the listener to be removed
478      * @throws ProxoolException if we couldn't find the pool
479      * @return wether the listnener was found and removed or not.
480      */

481     public static boolean removeConnectionListener(String JavaDoc alias, ConnectionListenerIF connectionListener) throws ProxoolException {
482         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
483         return cp.removeConnectionListener(connectionListener);
484     }
485
486     /**
487      * @deprecated use {@link #addConfigurationListener(String, ConfigurationListenerIF)} instead.
488      */

489     public static void setConfigurationListener(String JavaDoc alias, ConfigurationListenerIF configurationListener) throws ProxoolException {
490         addConfigurationListener(alias, configurationListener);
491     }
492
493     /**
494      * Adds a listener that gets called everytime the configuration changes.
495      * @param alias identifies the pool
496      * @param configurationListener the new listener
497      * @throws ProxoolException if we couldn't find the pool
498      */

499     public static void addConfigurationListener(String JavaDoc alias, ConfigurationListenerIF configurationListener) throws ProxoolException {
500         if (ConnectionPoolManager.getInstance().isPoolExists(alias)) {
501             CompositeConfigurationListener compositeConfigurationListener = (CompositeConfigurationListener)
502                     configurators.get(alias);
503             if (compositeConfigurationListener == null) {
504                 compositeConfigurationListener = new CompositeConfigurationListener();
505                 configurators.put(alias, compositeConfigurationListener);
506             }
507             compositeConfigurationListener.addListener(configurationListener);
508         } else {
509             throw new ProxoolException(ConnectionPoolManager.getInstance().getKnownPools(alias));
510         }
511     }
512
513     /**
514      * Broadcast a configuration change
515      * @param alias identifies the pool
516      * @param connectionPoolDefinition the definition
517      * @param completeInfo all properties
518      * @param changedInfo only changed properties (since the last
519      * time this method was called)
520      */

521     protected static void definitionUpdated(String JavaDoc alias, ConnectionPoolDefinitionIF connectionPoolDefinition,
522                                             Properties JavaDoc completeInfo, Properties JavaDoc changedInfo) {
523         CompositeConfigurationListener ccl = (CompositeConfigurationListener) configurators.get(alias);
524         if (ccl != null) {
525             ccl.definitionUpdated(connectionPoolDefinition, completeInfo, changedInfo);
526         }
527     }
528
529     /**
530      * Remove a listener that gets called everytime the configuration changes.
531      * @param alias identifies the pool.
532      * @param configurationListener the listener to be removed.
533      * @throws ProxoolException if we couldn't find the pool
534      * @return wether the listnener was found and removed or not.
535      *
536      */

537     public static boolean removeConfigurationListener(String JavaDoc alias, ConfigurationListenerIF configurationListener) throws ProxoolException {
538         boolean removed = false;
539         if (ConnectionPoolManager.getInstance().isPoolExists(alias)) {
540             CompositeConfigurationListener compositeConfigurationListener = (CompositeConfigurationListener)
541                     configurators.get(alias);
542             if (compositeConfigurationListener != null) {
543                 removed = compositeConfigurationListener.removeListener(configurationListener);
544             }
545         } else {
546             throw new ProxoolException(ConnectionPoolManager.getInstance().getKnownPools(alias));
547         }
548         return removed;
549     }
550
551     /**
552      * @see #killAllConnections(java.lang.String)
553      */

554     private static final boolean MERCIFUL = true;
555
556     /**
557      * Redefine the behaviour of the pool. All existing properties (for Proxool
558      * and the delegate driver are reset to their default) and reapplied
559      * based on the parameters sent here.
560      *
561      * @param url the url that defines the pool (or the abbreviated ""proxool.alias")
562      * @param info the new properties
563      * @see #updateConnectionPool
564      */

565     public static void redefineConnectionPool(String JavaDoc url, Properties JavaDoc info) throws ProxoolException {
566         String JavaDoc alias = getAlias(url);
567         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
568         try {
569             // Clone the old one
570
ConnectionPoolDefinition cpd = (ConnectionPoolDefinition) cp.getDefinition().clone();
571             cpd.redefine(url, info);
572             cp.setDefinition(cpd);
573         } catch (CloneNotSupportedException JavaDoc e) {
574             throw new ProxoolException("Funny, why couldn't we clone a definition?", e);
575         }
576     }
577
578
579     /**
580      * Update the behaviour of the pool. Only properties that are defined here are overwritten. That is, properties
581      * that were defined before but are not mentioned here are retained.
582      *
583      * @param url the url that defines the pool (or the abbreviated ""proxool.alias")
584      * @param info the new properties
585      * @see #redefineConnectionPool
586      */

587     public static void updateConnectionPool(String JavaDoc url, Properties JavaDoc info) throws ProxoolException {
588         String JavaDoc alias = getAlias(url);
589         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
590         try {
591             // Clone the old one
592
ConnectionPoolDefinition cpd = (ConnectionPoolDefinition) cp.getDefinition().clone();
593             cpd.update(url, info);
594             cp.setDefinition(cpd);
595         } catch (CloneNotSupportedException JavaDoc e) {
596             throw new ProxoolException("Funny, why couldn't we clone a definition?", e);
597         }
598     }
599
600     protected void finalize() throws Throwable JavaDoc {
601         super.finalize();
602         LOG.debug("Finalising");
603     }
604
605     /**
606      * Returns the driver provided statement that Proxool wraps up before it gives it to you.
607      * @return delegate statement
608      * @deprecated Just cast the statement that you are given into the driver specific one.
609      */

610     public static Statement JavaDoc getDelegateStatement(Statement JavaDoc statement) throws ProxoolException {
611         try {
612             return ProxyFactory.getDelegateStatement(statement);
613         } catch (IllegalArgumentException JavaDoc e) {
614             throw new ProxoolException("Statement argument is not one provided by Proxool (it's a " + statement.getClass() + ")");
615         }
616     }
617
618     /**
619      * Returns the driver provided connection that Proxool wraps up before it gives it to you.
620      * @return delegate connection
621      * @deprecated Just cast the connection that you are given into the driver specific one.
622      */

623     public static Connection JavaDoc getDelegateConnection(Connection JavaDoc connection) throws ProxoolException {
624         try {
625             return ProxyFactory.getDelegateConnection(connection);
626         } catch (IllegalArgumentException JavaDoc e) {
627             throw new ProxoolException("Connection argument is not one provided by Proxool (it's a " + connection.getClass() + ")");
628         }
629     }
630
631     /**
632      * Get the connection ID for a connection
633      * @param connection the connection that was served
634      * @return the ID
635      * @throws ProxoolException if the connection wasn't recognised.
636      */

637     public static long getId(Connection JavaDoc connection) throws ProxoolException {
638         try {
639             return ProxyFactory.getWrappedConnection(connection).getId();
640         } catch (NullPointerException JavaDoc e) {
641             throw new ProxoolException("Connection argument is not one provided by Proxool (it's a " + connection.getClass() + ")");
642         } catch (IllegalArgumentException JavaDoc e) {
643             throw new ProxoolException("Connection argument is not one provided by Proxool (it's a " + connection.getClass() + ")");
644         }
645     }
646
647     /**
648      * Get the alias for the connection pool that served a connection
649      * @param connection the connection that was served
650      * @return the alias
651      * @throws ProxoolException if the connection wasn't recognised.
652      */

653     public static String JavaDoc getAlias(Connection JavaDoc connection) throws ProxoolException {
654         try {
655             return ProxyFactory.getWrappedConnection(connection).getAlias();
656         } catch (NullPointerException JavaDoc e) {
657             throw new ProxoolException("Connection argument is not one provided by Proxool (it's a " + connection.getClass() + ")");
658         } catch (IllegalArgumentException JavaDoc e) {
659             throw new ProxoolException("Connection argument is not one provided by Proxool (it's a " + connection.getClass() + ")");
660         }
661     }
662
663     /**
664      * Get a list of all the registered pools
665      * @return an array of aliases
666      * @since Proxool 0.7
667      */

668     public static String JavaDoc[] getAliases() {
669         return ConnectionPoolManager.getInstance().getConnectionPoolNames();
670     }
671
672     /**
673      * Get a particular set of performance statistics for this pool
674      * @param alias identifies the pool
675      * @param token identifies which set, as defined in the configuration (see {@link ConnectionPoolDefinitionIF#getStatistics definition})
676      * @return a sample containing the statistics
677      * @throws ProxoolException if we couldn't find the pool
678      */

679     public static StatisticsIF getStatistics(String JavaDoc alias, String JavaDoc token) throws ProxoolException {
680         return ConnectionPoolManager.getInstance().getConnectionPool(alias).getAdmin().getStatistics(token);
681     }
682
683     /**
684      * Get all the lastest performance statistics for this pool
685      * @param alias identifies the pool
686      * @return a sample containing the statistics, or a zero length array if there none
687      * @throws ProxoolException if we couldn't find the pool
688      */

689     public static StatisticsIF[] getStatistics(String JavaDoc alias) throws ProxoolException {
690         final Admin monitor = ConnectionPoolManager.getInstance().getConnectionPool(alias).getAdmin();
691         if (monitor != null) {
692             return monitor.getStatistics();
693         } else {
694             return new StatisticsIF[0];
695         }
696     }
697
698     /**
699      * Add a listener that receives statistics as they are produced
700      * @param statisticsListener the new listener
701      * @throws ProxoolException if the pool couldn't be found
702      */

703     public static void addStatisticsListener(String JavaDoc alias, StatisticsListenerIF statisticsListener) throws ProxoolException {
704         // TODO investigate what happens if we add a statistics monitor after we register a listener
705
final Admin monitor = ConnectionPoolManager.getInstance().getConnectionPool(alias).getAdmin();
706         if (monitor != null) {
707             monitor.addStatisticsListener(statisticsListener);
708         } else {
709             throw new ProxoolException("Statistics are switched off, your can't add a listener");
710         }
711     }
712
713     /**
714      * Gives a snapshot of what the pool is doing
715      * @param alias identifies the pool
716      * @param detail if true then include detail of each connection. Note it you ask for
717      * detail then the pool must necessarily be locked for the duration it takes to gather
718      * the information (which isn't very long). You probably shouldn't do it that often (like
719      * not every second or something). Being locked means that connections cannot be
720      * served or returned (it doesn't mean that they can't be active).
721      * @return the current status of the pool
722      * @throws ProxoolException if we couldn't find the pool
723      */

724     public static SnapshotIF getSnapshot(String JavaDoc alias, boolean detail) throws ProxoolException {
725         SnapshotIF snapshot = null;
726         ConnectionPool cp = ConnectionPoolManager.getInstance().getConnectionPool(alias);
727
728         if (detail) {
729             try {
730                 // Only try for 10 seconds!
731
long start = System.currentTimeMillis();
732                 if (cp.attemptConnectionStatusReadLock(10000)) {
733                     snapshot = Admin.getSnapshot(cp, cp.getDefinition(), cp.getConnectionInfos());
734                 } else {
735                     LOG.warn("Give up waiting for detailed snapshot after " + (System.currentTimeMillis() - start) + " milliseconds. Serving standard snapshot instead.");
736                 }
737             } finally {
738                 cp.releaseConnectionStatusReadLock();
739             }
740         }
741         if (snapshot == null) {
742             snapshot = Admin.getSnapshot(cp, cp.getDefinition(), null);
743         }
744
745         return snapshot;
746     }
747
748     /**
749      * Calls {@link #getSnapshot(java.lang.String, boolean) getSnapshot}
750      * using false for the detail parameter.
751      * @see #getSnapshot(java.lang.String, boolean)
752      */

753     public static SnapshotIF getSnapshot(String JavaDoc alias) throws ProxoolException {
754         return getSnapshot(alias, false);
755     }
756
757     // all jmx operations are done through reflection
758
// to avoid making the facade dependant on the JMX classes
759
private static boolean registerForJmx(String JavaDoc alias, Properties JavaDoc properties) {
760         boolean success = false;
761         try {
762             Class JavaDoc jmxHelperClass = Class.forName("org.logicalcobwebs.proxool.admin.jmx.ProxoolJMXHelper");
763             Method JavaDoc registerMethod = jmxHelperClass.getDeclaredMethod("registerPool", new Class JavaDoc[]{String JavaDoc.class, Properties JavaDoc.class});
764             registerMethod.invoke(null, new Object JavaDoc[]{alias, properties});
765             success = true;
766         } catch (Exception JavaDoc e) {
767             LOG.error("JMX registration of " + alias + " pool failed.", e);
768         }
769         return success;
770     }
771
772     // all JNDI operations