KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > connection > AbstractConnectionManager


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
6  * Contact: sequoia@continuent.org
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * Initial developer(s): Emmanuel Cecchet.
21  * Contributor(s): Mathieu Peltier.
22  */

23
24 package org.continuent.sequoia.controller.connection;
25
26 import java.sql.Connection JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import java.util.LinkedList JavaDoc;
30
31 import org.continuent.sequoia.common.exceptions.UnreachableBackendException;
32 import org.continuent.sequoia.common.i18n.Translate;
33 import org.continuent.sequoia.common.log.Trace;
34 import org.continuent.sequoia.common.xml.DatabasesXmlTags;
35 import org.continuent.sequoia.common.xml.XmlComponent;
36 import org.continuent.sequoia.controller.core.ControllerConstants;
37 import org.continuent.sequoia.controller.requests.AbstractRequest;
38
39 /**
40  * A <code>ConnectionManager</code> object is responsible to talk directly
41  * with a database backend.
42  *
43  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
44  * @author <a HREF="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
45  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
46  * @author <a HREF="mailto:Stephane.Giron@continuent.com">Stephane Giron </a>
47  * @version 1.0
48  */

49 public abstract class AbstractConnectionManager
50     implements
51       XmlComponent,
52       Cloneable JavaDoc
53 {
54   //
55
// How the code is organized ?
56
//
57
// 1. Member variables
58
// 2. Constructors/Destructors
59
// 3. Connection handling
60
// 4. Getter/Setter (possibly in alphabetical order)
61
//
62

63   /** Logger instance. */
64   static Trace logger = Trace
65                                                                                           .getLogger("org.continuent.sequoia.controller.connection");
66
67   /** URL of the <code>DatabaseBackend</code> owning this connection manager. */
68   protected String JavaDoc backendUrl;
69
70   /**
71    * Name of the <code>DatabaseBackend</code> owning this connection manager.
72    */

73   protected String JavaDoc backendName;
74
75   /** Backend connection login to be used by this connection manager. */
76   protected String JavaDoc rLogin;
77
78   /** Backend connection password to be used by this connection manager. */
79   protected String JavaDoc rPassword;
80
81   /** The class name of the driver */
82   protected String JavaDoc driverClassName;
83
84   /**
85    * The path to the driver if null the default directory is used
86    */

87   protected String JavaDoc driverPath;
88
89   /** <code>true</code> if the connection pool has been initialized. */
90   protected boolean initialized;
91
92   /**
93    * <code>true</code> if the connection manager has been shutdown and should
94    * no longer hand out Connections.
95    */

96   protected boolean isShutdown;
97
98   /** Hastable of connections associated to a transaction (tid->Connection) */
99   private transient Hashtable JavaDoc connectionForTransaction;
100
101   /**
102    * Hashtable&lt;Long, PooledConnection&gt; which associates a persistent
103    * connection id (encapsulated in a Long) with a PooledConnection.
104    */

105   protected transient Hashtable JavaDoc persistentConnections;
106
107   /** Virtual Login used this connection manager */
108   private String JavaDoc vLogin;
109
110   protected String JavaDoc connectionTestStatement;
111
112   /**
113    * Stores the time on which persistent connections have been last used.
114    */

115   protected LinkedList JavaDoc persistentConnectionsIdleTime;
116
117   /**
118    * Stack of idle persistent connections
119    */

120   protected LinkedList JavaDoc idlePersistentConnections;
121
122   /**
123    * Allow to check idle persistent connections against the backend.
124    */

125   protected IdlePersistentConnectionsPingerThread persistentConnectionPingerThread;
126
127   /**
128    * Time after which an idle persistent connection will be checked.
129    */

130   protected int idlePersistentConnectionPingInterval;
131
132   /**
133    * Indicates if the idle persistent connections checker thread is running.
134    */

135   protected boolean idlePersistentConnectionPingRunning = false;
136
137   /*
138    * Constructor(s)
139    */

140
141   /**
142    * Creates a new <code>AbstractConnectionManager</code> instance: assigns
143    * login/password and instanciates transaction id/connection mapping.
144    *
145    * @param backendUrl URL of the <code>DatabaseBackend</code> owning this
146    * connection manager
147    * @param backendName name of the <code>DatabaseBackend</code> owning this
148    * connection manager
149    * @param rLogin backend connection login to be used by this connection
150    * manager
151    * @param rPassword backend connection password to be used by this connection
152    * manager
153    * @param driverPath path for driver
154    * @param driverClassName class name for driver
155    */

156   protected AbstractConnectionManager(String JavaDoc backendUrl, String JavaDoc backendName,
157       String JavaDoc rLogin, String JavaDoc rPassword, String JavaDoc driverPath, String JavaDoc driverClassName)
158   {
159     if (backendUrl == null)
160       throw new IllegalArgumentException JavaDoc(
161           "Illegal null database backend URL in AbstractConnectionManager constructor");
162
163     if (backendName == null)
164       throw new IllegalArgumentException JavaDoc(
165           "Illegal null database backend name in AbstractConnectionManager constructor");
166
167     if (rLogin == null)
168       throw new IllegalArgumentException JavaDoc(
169           "Illegal null database backend login in AbstractConnectionManager constructor");
170
171     if (rPassword == null)
172       throw new IllegalArgumentException JavaDoc(
173           "Illegal null database backend password in AbstractConnectionManager constructor");
174
175     if (driverPath != null)
176     {
177       if (driverClassName == null)
178       {
179         throw new IllegalArgumentException JavaDoc(
180             "Illegal null database backend driverClassName in AbstractConnectionManager constructor");
181       }
182     }
183     this.backendUrl = backendUrl;
184     this.backendName = backendName;
185     this.rLogin = rLogin;
186     this.rPassword = rPassword;
187     this.driverPath = driverPath;
188     this.driverClassName = driverClassName;
189     connectionForTransaction = new Hashtable JavaDoc();
190     persistentConnections = new Hashtable JavaDoc();
191
192     // Get the IDLE_PERSISTENT_CONNECTION_PING_INTERVAL and convert it in ms
193
idlePersistentConnectionPingInterval = ControllerConstants.IDLE_PERSISTENT_CONNECTION_PING_INTERVAL * 1000;
194     if (idlePersistentConnectionPingInterval > 0)
195     {
196       this.idlePersistentConnections = new LinkedList JavaDoc();
197       this.persistentConnectionsIdleTime = new LinkedList JavaDoc();
198     }
199
200   }
201
202   /**
203    * Ensures that the connections are closed when the object is garbage
204    * collected.
205    *
206    * @exception Throwable if an error occurs.
207    */

208   protected void finalize() throws Throwable JavaDoc
209   {
210     if (isInitialized())
211       finalizeConnections();
212
213     if (idlePersistentConnectionPingRunning)
214     {
215       stopPersistentConnectionPingerThread();
216       synchronized (this)
217       {
218         idlePersistentConnections.clear();
219         persistentConnectionsIdleTime.clear();
220       }
221     }
222
223     super.finalize();
224   }
225
226   /**
227    * @see java.lang.Object#clone()
228    */

229   protected abstract Object JavaDoc clone() throws CloneNotSupportedException JavaDoc;
230
231   /**
232    * Creates a new connection manager with the same parameters but a different
233    * backend user and password.
234    *
235    * @param rLogin real login
236    * @param rPassword real password
237    * @return an AbstractConnectionManager for the new user
238    */

239   public abstract AbstractConnectionManager clone(String JavaDoc rLogin,
240       String JavaDoc rPassword);
241
242   /**
243    * Copy this connection manager and replace the name of the backend and its
244    * url Every other parameter is the same
245    *
246    * @param url the url to the backend associated to this ConnectionManager
247    * @param name the name of the backend
248    * @return <code>AbstractConnectionManager</code>
249    * @throws Exception if clone fails
250    */

251   public AbstractConnectionManager copy(String JavaDoc url, String JavaDoc name)
252       throws Exception JavaDoc
253   {
254     AbstractConnectionManager connectionManager = (AbstractConnectionManager) this
255         .clone();
256     connectionManager.backendName = name;
257     connectionManager.backendUrl = url;
258     return connectionManager;
259   }
260
261   /*
262    * Connection handling
263    */

264
265   /**
266    * Delete a connection that is no more valid.
267    *
268    * @param connection the connection to delete.
269    */

270   public abstract void deleteConnection(PooledConnection connection);
271
272   /**
273    * Close an unwanted connection. The connection may be in a bad state so we
274    * ignore all errors.
275    *
276    * @param connection to be closed
277    */

278   protected void closeConnection(PooledConnection connection)
279   {
280     try
281     {
282       Connection JavaDoc conn = connection.getConnection();
283       if (!conn.isClosed())
284       {
285         conn.close();
286       }
287     }
288     catch (SQLException JavaDoc e)
289     {
290
291     }
292   }
293
294   /**
295    * Delete a bad connection that was used for a transaction. The corresponding
296    * connection is deleted by calling
297    * {@link #deleteConnection(PooledConnection)}.
298    *
299    * @param transactionId the transaction id.
300    * @see #releaseConnection(PooledConnection)
301    */

302   public void deleteConnection(long transactionId)
303   {
304     // Underlying Hashtable is synchronized
305
PooledConnection c = (PooledConnection) connectionForTransaction
306         .remove(new Long JavaDoc(transactionId));
307
308     if (c == null)
309       logger.error(Translate.get("connection.transaction.unknown",
310           transactionId));
311     else
312       deleteConnection(c);
313   }
314
315   /**
316    * Deletes a connection associated to the persistentConnectionId.<br />
317    * Do nothing if there is no connection associated to the
318    * persistentConnectionId
319    *
320    * @param persistentConnectionId the id of the persistent connection to delete
321    */

322   public void deletePersistentConnection(long persistentConnectionId)
323   {
324     Long JavaDoc connectionId = new Long JavaDoc(persistentConnectionId);
325     if (idlePersistentConnectionPingRunning
326         && idlePersistentConnections.contains(persistentConnections
327             .get(connectionId)))
328     {
329       PooledConnection c = (PooledConnection) persistentConnections
330           .get(connectionId);
331       synchronized (this)
332       {
333         persistentConnectionsIdleTime.remove(idlePersistentConnections
334             .indexOf(c));
335         idlePersistentConnections.remove(c);
336       }
337     }
338     persistentConnections.remove(connectionId);
339   }
340
341   /**
342    * Releases all the connections to the database.
343    *
344    * @exception SQLException if an error occurs.
345    */

346   public final void finalizeConnections() throws SQLException JavaDoc
347   {
348     connectionForTransaction.clear();
349     doConnectionFinalization();
350   }
351
352   /**
353    * Must be implemented by class extending AbstractConnectionManager to
354    * finalize their connections.
355    *
356    * @throws SQLException if an error occurs
357    */

358   protected abstract void doConnectionFinalization() throws SQLException JavaDoc;
359
360   /**
361    * Force all connections to be renewed when they are used next. This just sets
362    * a flag and connections will be lazily replaced as needed.
363    */

364   public abstract void flagAllConnectionsForRenewal();
365
366   /**
367    * Get a connection from DriverManager.
368    *
369    * @return a new connection or null if Driver.getConnection() failed.
370    * @see DriverManager#getConnection(String, String, String, String, String)
371    */

372   public Connection JavaDoc getConnectionFromDriver()
373   {
374     try
375     {
376       return DriverManager.getConnection(backendUrl, rLogin, rPassword,
377           driverPath, driverClassName);
378     }
379     catch (SQLException JavaDoc ignore)
380     {
381       if (logger.isDebugEnabled())
382       {
383         logger.debug("failed to get connection for driver ", ignore);
384       }
385       return null;
386     }
387   }
388
389   /**
390    * Gets a connection from the pool (implementation specific).
391    *
392    * @return a <code>Connection</code> or <code>null</code> if no connection
393    * is available or if the connection has not been initialized.
394    * @throws UnreachableBackendException if the backend must be disabled
395    */

396   protected abstract PooledConnection getConnection()
397       throws UnreachableBackendException;
398
399   /**
400    * Releases a connection.
401    *
402    * @param connection the connection to release.
403    */

404   protected abstract void releaseConnection(PooledConnection connection);
405
406   /**
407    * Gets a connection from the pool in autocommit mode. If connections needed
408    * to be renewed, they are renewed here.
409    *
410    * @return a <code>PooledConnection</code> or <code>null</code> if no
411    * connection is available or if the connection has not been
412    * initialized.
413    * @throws UnreachableBackendException if the backend must be disabled
414    */

415   private PooledConnection getRenewedConnectionInAutoCommit()
416       throws UnreachableBackendException
417   {
418     PooledConnection c;
419     while (true)
420     { // Loop until all connections that must be renewed have been renewed
421
c = getConnection();
422       if (c != null)
423       {
424         if (c.mustBeRenewed())
425           deleteConnection(c);
426         else
427           break;
428       }
429       else
430         break;
431     }
432
433     return c;
434   }
435
436   /**
437    * Gets a new connection for a transaction. This function calls
438    * {@link #getConnection()}to get the connection and store the mapping
439    * between the connection and the transaction id.
440    * <p>
441    * Note that this function does not start the transaction, it is up to the
442    * caller to call setAutoCommit(false) on the returned connection.
443    *
444    * @param transactionId the transaction id.
445    * @return a <code>Connection</code> or <code>null</code> if no connection
446    * is available .
447    * @throws UnreachableBackendException if the backend must be disabled
448    * @see #getConnection()
449    */

450   public PooledConnection getConnectionForTransaction(long transactionId)
451       throws UnreachableBackendException
452   {
453     PooledConnection c = getRenewedConnectionInAutoCommit();
454     registerConnectionForTransaction(c, transactionId);
455     return c;
456   }
457
458   /**
459    * Initializes the connection(s) to the database. The caller must ensure that
460    * the driver has already been loaded else an exception will be thrown.
461    *
462    * @exception SQLException if an error occurs.
463    */

464   public final void initializeConnections() throws SQLException JavaDoc
465   {
466     isShutdown = false;
467     connectionForTransaction.clear();
468     doConnectionInitialization();
469   }
470
471   /**
472    * Mark the ConnectionManager as shutdown. getConnection() should return null
473    * from now on and any threads waiting in getConnection() should wake up. This
474    * is used when a database is shutting down due to an error. It is not needed
475    * in the case of a clean shutdown, because everybody will stop requesting
476    * connections at the correct time.
477    */

478   public synchronized void shutdown()
479   {
480     isShutdown = true;
481     notifyAll();
482   }
483
484   /**
485    * Must be implemented by class extending AbstractConnectionManager to
486    * initalize their connections.
487    *
488    * @throws SQLException if an error occurs
489    */

490   protected abstract void doConnectionInitialization() throws SQLException JavaDoc;
491
492   /**
493    * Enlist a connection for a transaction (maintains the transaction id <->
494    * connection mapping).
495    *
496    * @param c a pooled connection
497    * @param transactionId the transaction id to register this connection for
498    */

499   public void registerConnectionForTransaction(PooledConnection c,
500       long transactionId)
501   {
502     if (c != null)
503     {
504       // Underlying Hashtable is synchronized
505
Long JavaDoc lTid = new Long JavaDoc(transactionId);
506       if (connectionForTransaction.put(lTid, c) != null)
507       {
508         logger
509             .error("A new connection for transaction "
510                 + lTid
511                 + " has been opened but there was a remaining connection for this transaction that has not been closed.");
512       }
513     }
514   }
515
516   /**
517    * Retrieves a connection used for a transaction. This connection must have
518    * been allocated by calling {@link #getConnectionForTransaction(long)}.
519    *
520    * @param transactionId the transaction id.
521    * @return a <code>Connection</code> or <code>null</code> if no connection
522    * has been found for this transaction id.
523    * @see #getConnectionForTransaction(long)
524    */

525   public PooledConnection retrieveConnectionForTransaction(long transactionId)
526   {
527     Long JavaDoc id = new Long JavaDoc(transactionId);
528     // Underlying Hashtable is synchronized
529
return (PooledConnection) connectionForTransaction.get(id);
530   }
531
532   /**
533    * Releases a connection used for a transaction. The corresponding connection
534    * is released by calling {@link #releaseConnection(PooledConnection)}.
535    *
536    * @param transactionId the transaction id.
537    * @see #releaseConnection(PooledConnection)
538    */

539   public void releaseConnectionForTransaction(long transactionId)
540   {
541     // Underlying Hashtable is synchronized
542
PooledConnection c = (PooledConnection) connectionForTransaction
543         .remove(new Long JavaDoc(transactionId));
544
545     if (c == null)
546       logger.error(Translate.get("connection.transaction.unknown",
547           transactionId));
548     else
549     {
550       if (!c.isDefaultTransactionIsolation())
551       { // Reset transaction isolation (SEQUOIA-532)
552
try
553         {
554           c.restoreDefaultTransactionIsolation();
555         }
556         catch (Throwable JavaDoc e)
557         {
558           logger.error("Error while resetting transaction ", e);
559         }
560       }
561
562       // Only release the connection if it is not persistent
563
if (!persistentConnections.containsValue(c))
564         releaseConnection(c);
565     }
566   }
567
568   /**
569    * Gets a connection from the pool in autocommit mode. If a persistent id is
570    * defined for the request, the appropriate connection is retrieved.
571    *
572    * @param request the request asking for a connection (we will check its
573    * persistent connection id)
574    * @return a <code>PooledConnection</code> or <code>null</code> if no
575    * connection is available or if the connection has not been
576    * initialized.
577    * @throws UnreachableBackendException if the backend must be disabled
578    */

579   public PooledConnection retrieveConnectionInAutoCommit(AbstractRequest request)
580       throws UnreachableBackendException
581   {
582     if ((request != null) && request.isPersistentConnection())
583     {
584       Long JavaDoc id = new Long JavaDoc(request.getPersistentConnectionId());
585       // Underlying Hashtable is synchronized
586
PooledConnection c = (PooledConnection) persistentConnections.get(id);
587       if (c == null)
588       {
589         c = getRenewedConnectionInAutoCommit();
590         if (c != null)
591           persistentConnections.put(id, c);
592       }
593       if (idlePersistentConnectionPingRunning
594           && idlePersistentConnections.contains(c))
595       {
596         synchronized (this)
597         {
598           persistentConnectionsIdleTime.remove(idlePersistentConnections
599               .indexOf(c));
600           idlePersistentConnections.remove(c);
601         }
602       }
603
604       return c;
605     }
606     else
607       return getRenewedConnectionInAutoCommit();
608   }
609
610   /**
611    * Releases a connection if it is not persistent. The corresponding connection
612    * is released by calling {@link #releaseConnection(PooledConnection)}.
613    *
614    * @param request the request asking for a connection (we will check its
615    * persistent connection id)
616    * @param c the connection to release.
617    * @see #releaseConnection(PooledConnection)
618    */

619   public void releaseConnectionInAutoCommit(AbstractRequest request,
620       PooledConnection c)
621   {
622     if ((request != null) && request.isPersistentConnection())
623     {
624       if (idlePersistentConnectionPingRunning)
625         synchronized (this)
626         {
627           persistentConnectionsIdleTime.addLast(new Long JavaDoc(System
628               .currentTimeMillis()));
629           idlePersistentConnections.addLast(c);
630           if (persistentConnectionsIdleTime.size() > 0)
631             notifyPersistentConnectionPingerThread();
632         }
633       return;
634     }
635
636     try
637     {
638       if (!c.getConnection().getAutoCommit())
639       {
640         c.getConnection().setAutoCommit(true);
641         if (logger.isDebugEnabled())
642         {
643           Exception JavaDoc e = new Exception JavaDoc("Connection returned with autocommit off");
644           logger.debug(e);
645         }
646       }
647     }
648     catch (SQLException JavaDoc e)
649     {
650       // ignore this for now somebody will deal with it later
651
}
652     releaseConnection(c);
653   }
654
655   protected void notifyPersistentConnectionPingerThread()
656   {
657     synchronized (persistentConnectionPingerThread)
658     {
659       persistentConnectionPingerThread.notify();
660     }
661   }
662
663   /**
664    * Releases a connection used for a persistent connection. The corresponding
665    * connection is released by calling
666    * {@link #releaseConnection(PooledConnection)}.
667    *
668    * @param persistentConnectionId the persistent connection id.
669    * @see #releaseConnection(PooledConnection)
670    */

671   public void releasePersistentConnectionInAutoCommit(
672       long persistentConnectionId)
673   {
674     // Underlying Hashtable is synchronized
675
PooledConnection c = (PooledConnection) persistentConnections
676         .remove(new Long JavaDoc(persistentConnectionId));
677
678     if (c == null)
679       logger.error(Translate.get("connection.persistent.id.unknown",
680           persistentConnectionId));
681     else
682     {
683       if (idlePersistentConnectionPingRunning
684           && idlePersistentConnections.contains(c))
685       {
686         synchronized (this)
687         {
688           persistentConnectionsIdleTime.remove(idlePersistentConnections
689               .indexOf(c));
690           idlePersistentConnections.remove(c);
691         }
692       }
693       releaseConnection(c);
694     }
695   }
696
697   /**
698    * Tests if the connections have been initialized.
699    *
700    * @return <code>true</code> if the connections have been initialized.
701    */

702   public boolean isInitialized()
703   {
704     return initialized;
705   }
706
707   /*
708    * Getter/setter methods
709    */

710
711   /**
712    * Get the current number of connections open for this connection manager.
713    *
714    * @return the current number of open connections
715    */

716   public abstract int getCurrentNumberOfConnections();
717
718   /**
719    * Returns the driverClassName value.
720    *
721    * @return Returns the driverClassName.
722    */

723   public String JavaDoc getDriverClassName()
724   {
725     return driverClassName;
726   }
727
728   /**
729    * Returns the driverPath value.
730    *
731    * @return Returns the driverPath.
732    */

733   public String JavaDoc getDriverPath()
734   {
735     return driverPath;
736   }
737
738   /**
739    * Returns the login used by this connection manager.
740    *
741    * @return a <code>String</code> value.
742    */

743   public String JavaDoc getLogin()
744   {
745     return rLogin;
746   }
747
748   /**
749    * Returns the password used by this connection manager.
750    *
751    * @return a <code>String</code> value.
752    */

753   public String JavaDoc getPassword()
754   {
755     return rPassword;
756   }
757
758   /*
759    * Debug/monitoring information
760    */

761
762   /**
763    * @return Returns the vLogin.
764    */

765   public String JavaDoc getVLogin()
766   {
767     return vLogin;
768   }
769
770   /**
771    * @param login The vLogin to set.
772    */

773   public void setVLogin(String JavaDoc login)
774   {
775     vLogin = login;
776   }
777
778   /**
779    * Set the SQL string used to test whether or not the server is available.
780    *
781    * @param connectionTestStatement connection test statement to use for this
782    * pool
783    */

784   public void setConnectionTestStatement(String JavaDoc connectionTestStatement)
785   {
786     this.connectionTestStatement = connectionTestStatement;
787   }
788
789   /**
790    * Gets xml formatted information on this connection manager
791    *
792    * @return xml formatted string that conforms to sequoia.dtd
793    */

794   protected abstract String JavaDoc getXmlImpl();
795
796   /**
797    * @see org.continuent.sequoia.common.xml.XmlComponent#getXml()
798    */

799   public String JavaDoc getXml()
800   {
801     StringBuffer JavaDoc info = new StringBuffer JavaDoc();
802     info.append("<" + DatabasesXmlTags.ELT_ConnectionManager + " "
803         + DatabasesXmlTags.ATT_vLogin + "=\"" + vLogin + "\" " + ""
804         + DatabasesXmlTags.ATT_rLogin + "=\"" + rLogin + "\" " + ""
805         + DatabasesXmlTags.ATT_rPassword + "=\"" + rPassword + "\" " + ">");
806     info.append(this.getXmlImpl());
807     info.append("</" + DatabasesXmlTags.ELT_ConnectionManager + ">");
808     return info.toString();
809   }
810
811   protected void stopPersistentConnectionPingerThread()
812   {
813     synchronized (persistentConnectionPingerThread)
814     {
815       persistentConnectionPingerThread.isKilled = true;
816       idlePersistentConnectionPingRunning = false;
817       persistentConnectionPingerThread.notify();
818     }
819     try
820     {
821       persistentConnectionPingerThread.join();
822     }
823     catch (InterruptedException JavaDoc e)
824     {
825     }
826   }
827
828   /*
829    * this will test that persistent connections that are idled are still
830    * available
831    */

832   class IdlePersistentConnectionsPingerThread extends Thread JavaDoc
833   {
834     private boolean isKilled = false;
835     private AbstractConnectionManager thisPool;
836
837     protected IdlePersistentConnectionsPingerThread(String JavaDoc pBackendName,
838         AbstractConnectionManager thisPool)
839     {
840       super("IdlePersistentConnectionsPingerThread for backend:" + pBackendName);
841       this.thisPool = thisPool;
842     }
843
844     /**
845      * @see java.lang.Runnable#run()
846      */

847     public void run()
848     {
849       long idleTime, releaseTime;
850       synchronized (this)
851       {
852         try
853         {
854           while (!isKilled)
855           {
856             if (idlePersistentConnections.isEmpty())
857             {
858               this.wait();
859             }
860
861             if (isKilled)
862               continue; // Just exit
863

864             PooledConnection c = null;
865
866             if (persistentConnectionsIdleTime.isEmpty())
867             {
868               continue; // Sanity check
869
}
870             releaseTime = ((Long JavaDoc) persistentConnectionsIdleTime.getFirst())
871                 .longValue();
872             idleTime = System.currentTimeMillis() - releaseTime;
873
874             if (idleTime >= idlePersistentConnectionPingInterval)
875             {
876               synchronized (thisPool)
877               {
878                 c = (PooledConnection) idlePersistentConnections.removeFirst();
879                 persistentConnectionsIdleTime.removeFirst();
880               }
881             }
882
883             if (c == null)
884             { // Nothing to free, wait for next deadline
885
wait(idlePersistentConnectionPingInterval - idleTime);
886             }
887             else
888             {
889               try
890               {
891                 // check the connection is still valid
892
c.getConnection().createStatement().execute(
893                     connectionTestStatement);
894                 // and put it again in the pool
895
synchronized (thisPool)
896                 {
897                   persistentConnectionsIdleTime.addLast(new Long JavaDoc(System
898                       .currentTimeMillis()));
899                   idlePersistentConnections.addLast(c);
900                 }
901               }
902               catch (SQLException JavaDoc e)
903               {
904                 // Connection lost... we release it... and open a new connection
905
try
906                 {
907                   c.getConnection().close();
908                 }
909                 catch (SQLException JavaDoc e1)
910                 {
911                   String JavaDoc msg = "An error occured while closing idle connection after the timeout: "
912                       + e;
913                   logger.error(msg);
914                 }
915               }
916             }
917           }
918         }
919         catch (InterruptedException JavaDoc e)
920         {
921           logger
922               .error("Wait on IdlePersistentConnectionsPingerThread interrupted in ConnectionManager: "
923                   + e);
924         }
925       }
926     }
927   }
928 }
Popular Tags