Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

RandomWaitPoolConnectionManager.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2004 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet.
00022  * Contributor(s): ______________________.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.connection;
00026 
00027 import java.io.Serializable;
00028 import java.sql.Connection;
00029 import java.util.EmptyStackException;
00030 
00031 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException;
00032 import org.objectweb.cjdbc.common.i18n.Translate;
00033 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00034 
00035 /**
00036  * This connection manager waits when the pool is empty. Requests are stacked
00037  * using the Java wait/notify mechanism. Therefore the FIFO order is not
00038  * guaranteed and the first request to get the freed connection is the thread
00039  * that gets elected by the scheduler.
00040  * 
00041  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00042  * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
00043  * @version 1.0
00044  */
00045 public class RandomWaitPoolConnectionManager
00046     extends
00047       AbstractPoolConnectionManager implements Serializable
00048 {
00049   /** Time to wait for a connection in milliseconds (0 means wait forever). */
00050   private int timeout;
00051 
00052   /**
00053    * Creates a new <code>RandomWaitPoolConnectionManager</code> instance.
00054    * 
00055    * @param backendUrl URL of the <code>DatabaseBackend</code> owning this
00056    *          connection manager
00057    * @param backendName name of the <code>DatabaseBackend</code> owning this
00058    *          connection manager
00059    * @param login backend connection login to be used by this connection manager
00060    * @param password backend connection password to be used by this connection
00061    *          manager
00062    * @param driverPath path for driver
00063    * @param driverClassName class name for driver
00064    * @param poolSize size of the connection pool
00065    * @param timeout time to wait for a connection in seconds (0 means wait
00066    *          forever)
00067    */
00068   public RandomWaitPoolConnectionManager(String backendUrl, String backendName,
00069       String login, String password, String driverPath, String driverClassName,
00070       int poolSize, int timeout)
00071   {
00072     super(backendUrl, backendName, login, password, driverPath,
00073         driverClassName, poolSize);
00074     this.timeout = timeout * 1000;
00075   }
00076   
00077   /**
00078    * @see java.lang.Object#clone()
00079    */
00080   protected Object clone() throws CloneNotSupportedException
00081   {
00082     return new RandomWaitPoolConnectionManager(backendUrl, backendName, rLogin,
00083         rPassword, driverPath, driverClassName, poolSize,timeout);
00084   }
00085 
00086   /**
00087    * Gets the timeout.
00088    * 
00089    * @return a <code>int</code> value.
00090    */
00091   public int getTimeout()
00092   {
00093     return timeout;
00094   }
00095 
00096   /**
00097    * Gets a connection from the pool.
00098    * <p>
00099    * If the pool is empty, this methods blocks until a connection is freed or
00100    * the timeout expires.
00101    * 
00102    * @return a connection from the pool or <code>null</code> if the timeout
00103    *         has expired.
00104    * @throws UnreachableBackendException if the backend must be disabled
00105    * @see org.objectweb.cjdbc.controller.connection.AbstractConnectionManager#getConnection()
00106    */
00107   public Connection getConnection() throws UnreachableBackendException
00108   {
00109     if (!initialized)
00110     {
00111       logger
00112           .error("Requesting a connection from a non-initialized connection manager");
00113       return null;
00114     }
00115 
00116     long lTimeout = timeout;
00117     synchronized (freeConnections)
00118     {
00119       // We have to do a while loop() because there is a potential race here.
00120       // When freeConnections is notified in releaseConnection, a new thread
00121       // can
00122       // take the lock on freeConnections before we wake up/reacquire the lock
00123       // on freeConnections. Therefore, we could wake up and have no connection
00124       // to take! We ensure that everything is correct with a while statement
00125       // and recomputing the timeout between 2 wakeup.
00126       while (freeConnections.isEmpty())
00127       {
00128         // Wait
00129         try
00130         {
00131           if (lTimeout > 0)
00132           {
00133             long start = System.currentTimeMillis();
00134             // Convert seconds to milliseconds for wait call
00135             freeConnections.wait(timeout);
00136             long end = System.currentTimeMillis();
00137             lTimeout -= end - start;
00138             if (lTimeout <= 0)
00139             {
00140               if (activeConnections.size() == 0)
00141               { // No connection active and backend unreachable, the backend
00142                 // is probably dead
00143                 logger.error("Backend " + backendName
00144                     + " is no more accessible.");
00145                 throw new UnreachableBackendException();
00146               }
00147               if (logger.isWarnEnabled())
00148                 logger.warn("Timeout expired for connection on backend '"
00149                     + backendName
00150                     + "', consider increasing pool size (current size is "
00151                     + poolSize + ") or timeout (current timeout is "
00152                     + (timeout / 1000) + " seconds)");
00153               return null;
00154             }
00155           }
00156           else
00157             freeConnections.wait();
00158         }
00159         catch (InterruptedException e)
00160         {
00161           logger
00162               .error("Wait on freeConnections interrupted in RandomWaitPoolConnectionManager: "
00163                   + e);
00164           return null;
00165         }
00166       }
00167       // Get the connection
00168       try
00169       {
00170         Connection c = (Connection) freeConnections.pop();
00171         activeConnections.add(c);
00172         return c;
00173       }
00174       catch (EmptyStackException e)
00175       {
00176         int missing = poolSize
00177             - (activeConnections.size() + freeConnections.size());
00178         if (missing > 0)
00179         { // Re-allocate missing connections
00180           logger.info("Trying to reallocate " + missing
00181               + " missing connections.");
00182           Connection connectionToBeReturned = null;
00183           while (missing > 0)
00184           {
00185             Connection c = getConnectionFromDriver();
00186             if (c == null)
00187             {
00188               if (missing == poolSize)
00189               {
00190                 String msg = Translate.get("loadbalancer.backend.unreacheable",
00191                     backendName);
00192                 logger.error(msg);
00193                 throw new UnreachableBackendException(msg);
00194               }
00195               logger.warn("Unable to re-allocate " + missing
00196                   + " missing connections.");
00197               break;
00198             }
00199             else
00200             {
00201               if (connectionToBeReturned == null)
00202                 connectionToBeReturned = c;
00203               else
00204                 freeConnections.add(c);
00205             }
00206             missing--;
00207           }
00208           return connectionToBeReturned;
00209         }
00210         if (logger.isErrorEnabled())
00211           logger.error("Failed to get a connection on backend '" + backendName
00212               + "' whereas an idle connection was expected");
00213         return null;
00214       }
00215     }
00216   }
00217 
00218   /**
00219    * @see org.objectweb.cjdbc.controller.connection.AbstractPoolConnectionManager#releaseConnection(Connection)
00220    */
00221   public void releaseConnection(Connection c)
00222   {
00223     if (!initialized)
00224       return; // We probably have been disabled
00225 
00226     synchronized (freeConnections)
00227     {
00228       if (activeConnections.remove(c))
00229       {
00230         freeConnections.push(c);
00231         freeConnections.notify();
00232       }
00233       else
00234         logger.error("Failed to release connection " + c
00235             + " (not found in active pool)");
00236     }
00237   }
00238 
00239   /**
00240    * @see org.objectweb.cjdbc.controller.connection.AbstractPoolConnectionManager#deleteConnection(Connection)
00241    */
00242   public void deleteConnection(Connection c)
00243   {
00244     if (!initialized)
00245       return; // We probably have been disabled
00246 
00247     synchronized (freeConnections)
00248     {
00249       if (activeConnections.remove(c))
00250       {
00251         Connection newConnection = getConnectionFromDriver();
00252         if (newConnection == null)
00253         {
00254           if (logger.isDebugEnabled())
00255             logger.error("Bad connection " + c
00256                 + " has been removed but cannot be replaced.");
00257         }
00258         else
00259         {
00260           freeConnections.push(newConnection);
00261           freeConnections.notify();
00262           if (logger.isDebugEnabled())
00263             logger.debug("Bad connection " + c
00264                 + " has been replaced by a new connection.");
00265         }
00266       }
00267       else
00268         logger.error("Failed to release connection " + c
00269             + " (not found in active pool)");
00270     }
00271   }
00272 
00273   /**
00274    * @see org.objectweb.cjdbc.controller.connection.AbstractConnectionManager#getXmlImpl()
00275    */
00276   public String getXmlImpl()
00277   {
00278     StringBuffer info = new StringBuffer();
00279     info.append("<" + DatabasesXmlTags.ELT_RandomWaitPoolConnectionManager
00280         + " " + DatabasesXmlTags.ATT_poolSize + "=\"" + poolSize + "\" "
00281         + DatabasesXmlTags.ATT_timeout + "=\"" + timeout / 1000 + "\"/>");
00282     return info.toString();
00283   }
00284 
00285 }

Generated on Mon Apr 11 22:01:34 2005 for C-JDBC by  doxygen 1.3.9.1