src/org/objectweb/cjdbc/controller/connection/VariablePoolConnectionManager.java

説明を見る。
00001 00025 package org.objectweb.cjdbc.controller.connection; 00026 00027 import java.io.Serializable; 00028 import java.sql.Connection; 00029 import java.sql.SQLException; 00030 import java.util.EmptyStackException; 00031 import java.util.Iterator; 00032 import java.util.Stack; 00033 00034 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException; 00035 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags; 00036 00054 public class VariablePoolConnectionManager extends 00055 AbstractPoolConnectionManager implements Serializable 00056 { 00058 public static final int DEFAULT_MAX_POOL_SIZE = 0; 00059 00064 public static final int DEFAULT_IDLE_TIMEOUT = 0; 00065 00070 public static final int DEFAULT_WAIT_TIMEOUT = 0; 00071 00073 private int initPoolSize; 00074 00076 private int minPoolSize; 00077 00079 private int maxPoolSize; 00080 00085 private int idleTimeout; 00086 00088 private int waitTimeout; 00089 00091 private Stack releaseTimes; 00092 00094 private RemoveIdleConnectionsThread removeIdleConnectionsThread; 00095 00118 public VariablePoolConnectionManager(String backendUrl, String backendName, 00119 String rLogin, String rPassword, String driverPath, 00120 String driverClassName, int minPoolSize, int maxPoolSize, 00121 int idleTimeout, int waitTimeout) 00122 { 00123 this(backendUrl, backendName, rLogin, rPassword, driverPath, 00124 driverClassName, minPoolSize, minPoolSize, maxPoolSize, idleTimeout, 00125 waitTimeout); 00126 } 00127 00131 protected Object clone() throws CloneNotSupportedException 00132 { 00133 return new VariablePoolConnectionManager(backendUrl, backendName, rLogin, 00134 rPassword, driverPath, driverClassName, minPoolSize, maxPoolSize, 00135 idleTimeout, waitTimeout); 00136 } 00137 00160 public VariablePoolConnectionManager(String backendUrl, String backendName, 00161 String rLogin, String rPassword, String driverPath, 00162 String driverClassName, int initPoolSize, int minPoolSize, 00163 int maxPoolSize, int idleTimeout, int waitTimeout) 00164 { 00165 super(backendUrl, backendName, rLogin, rPassword, driverPath, 00166 driverClassName, maxPoolSize == 0 ? (initPoolSize > minPoolSize 00167 ? initPoolSize 00168 : minPoolSize) : maxPoolSize); 00169 this.initPoolSize = initPoolSize; 00170 this.minPoolSize = minPoolSize; 00171 this.maxPoolSize = maxPoolSize; 00172 this.idleTimeout = idleTimeout * 1000; 00173 this.waitTimeout = waitTimeout * 1000; 00174 } 00175 00181 public int getMaxPoolSize() 00182 { 00183 return maxPoolSize; 00184 } 00185 00191 public int getMinPoolSize() 00192 { 00193 return minPoolSize; 00194 } 00195 00202 public int getIdleTimeout() 00203 { 00204 return idleTimeout; 00205 } 00206 00213 public int getWaitTimeout() 00214 { 00215 return waitTimeout; 00216 } 00217 00221 public synchronized void initializeConnections() throws SQLException 00222 { 00223 super.initializeConnections(initPoolSize); 00224 00225 if (idleTimeout != 0) 00226 { 00227 // Create the thread which manages the free connections 00228 removeIdleConnectionsThread = new RemoveIdleConnectionsThread(this.backendName); 00229 00230 // Intialize release time for the initial connections if an idleTimeout 00231 // is set 00232 releaseTimes = new Stack(); 00233 Iterator it = freeConnections.iterator(); 00234 Long currentTime = new Long(System.currentTimeMillis()); 00235 while (it.hasNext()) 00236 { 00237 it.next(); 00238 releaseTimes.push(currentTime); 00239 } 00240 00241 // Start the thread 00242 removeIdleConnectionsThread.start(); 00243 00244 synchronized (removeIdleConnectionsThread) 00245 { 00246 if (releaseTimes.size() > 0) 00247 { 00248 removeIdleConnectionsThread.notify(); 00249 } 00250 } 00251 } 00252 } 00253 00257 public synchronized void finalizeConnections() throws SQLException 00258 { 00259 if (removeIdleConnectionsThread != null) 00260 { 00261 synchronized (removeIdleConnectionsThread) 00262 { 00263 removeIdleConnectionsThread.isKilled = true; 00264 idleTimeout = 0; 00265 removeIdleConnectionsThread.notify(); 00266 } 00267 try 00268 { 00269 removeIdleConnectionsThread.join(); 00270 } 00271 catch (InterruptedException e) 00272 { 00273 } 00274 } 00275 super.finalizeConnections(); 00276 } 00277 00293 public Connection getConnection() throws UnreachableBackendException 00294 { 00295 if (!initialized) 00296 { 00297 logger 00298 .error("Requesting a connection from a non-initialized connection manager"); 00299 return null; 00300 } 00301 00302 long lTimeout = waitTimeout; 00303 synchronized (freeConnections) 00304 { 00305 if (freeConnections.isEmpty()) 00306 { 00307 if ((maxPoolSize == 0) || (activeConnections.size() < maxPoolSize)) 00308 { 00309 Connection c = getConnectionFromDriver(); 00310 if (c == null) 00311 { 00312 if (activeConnections.size() == 0) 00313 { // No connection active and backend unreachable, the backend 00314 // is probably dead 00315 logger 00316 .error("Backend " + backendName + " is no more accessible."); 00317 throw new UnreachableBackendException(); 00318 } 00319 // If it fails, just wait for a connection to be freed 00320 if (logger.isWarnEnabled()) 00321 logger.warn("Failed to create new connection on backend '" 00322 + backendName + "', waiting for a connection to be freed."); 00323 } 00324 else 00325 { 00326 freeConnections.add(c); 00327 if (idleTimeout != 0) 00328 { 00329 releaseTimes.add(new Long(System.currentTimeMillis())); 00330 } 00331 poolSize++; 00332 } 00333 } 00334 00335 /* 00336 * We have to do a while loop() because there is a potential race here. 00337 * When freeConnections is notified in releaseConnection, a new thread 00338 * can take the lock on freeConnections before we wake up/reacquire the 00339 * lock on freeConnections. Therefore, we could wake up and have no 00340 * connection to take! We ensure that everything is correct with a while 00341 * statement and recomputing the timeout between 2 wakeup. 00342 */ 00343 while (freeConnections.isEmpty()) 00344 { 00345 // Wait 00346 try 00347 { 00348 if (lTimeout > 0) 00349 { 00350 long start = System.currentTimeMillis(); 00351 // Convert seconds to milliseconds for wait call 00352 freeConnections.wait(waitTimeout); 00353 long end = System.currentTimeMillis(); 00354 lTimeout -= end - start; 00355 if (lTimeout <= 0) 00356 { 00357 if (logger.isWarnEnabled()) 00358 logger.warn("Timeout expired for connection on backend '" 00359 + backendName 00360 + "', consider increasing pool size (current size is " 00361 + poolSize + ") or timeout (current timeout is " 00362 + (waitTimeout / 1000) + " seconds)"); 00363 return null; 00364 } 00365 } 00366 else 00367 { 00368 freeConnections.wait(); 00369 } 00370 } 00371 catch (InterruptedException e) 00372 { 00373 logger 00374 .error("Wait on freeConnections interrupted in VariablePoolConnectionManager"); 00375 return null; 00376 } 00377 } 00378 } 00379 00380 // Get the connection 00381 try 00382 { 00383 Connection c = (Connection) freeConnections.pop(); 00384 if (idleTimeout != 0) 00385 { 00386 releaseTimes.pop(); 00387 } 00388 activeConnections.add(c); 00389 return c; 00390 } 00391 catch (EmptyStackException e) 00392 { 00393 if (logger.isErrorEnabled()) 00394 logger.error("Failed to get a connection on backend '" + backendName 00395 + "' but an idle connection was expected"); 00396 return null; 00397 } 00398 } 00399 } 00400 00404 public void releaseConnection(Connection c) 00405 { 00406 if (!initialized) 00407 return; // We probably have been disabled 00408 00409 boolean notifyThread = false; 00410 synchronized (freeConnections) 00411 { 00412 if (activeConnections.remove(c)) 00413 { 00414 if (idleTimeout != 0) 00415 { 00416 notifyThread = freeConnections.isEmpty() 00417 || (freeConnections.size() == minPoolSize); 00418 freeConnections.push(c); 00419 freeConnections.notify(); 00420 releaseTimes.push(new Long(System.currentTimeMillis())); 00421 } 00422 else 00423 { 00424 freeConnections.push(c); 00425 freeConnections.notify(); 00426 } 00427 } 00428 else 00429 logger.error("Failed to release connection " + c 00430 + " (not found in active pool)"); 00431 } 00432 00433 if (notifyThread) 00434 synchronized (removeIdleConnectionsThread) 00435 { 00436 removeIdleConnectionsThread.notify(); 00437 } 00438 } 00439 00443 public void deleteConnection(Connection c) 00444 { 00445 if (!initialized) 00446 return; // We probably have been disabled 00447 00448 synchronized (freeConnections) 00449 { 00450 if (activeConnections.remove(c)) 00451 { 00452 poolSize--; 00453 if (poolSize < minPoolSize) 00454 { 00455 Connection newConnection = getConnectionFromDriver(); 00456 if (newConnection == null) 00457 { 00458 if (logger.isDebugEnabled()) 00459 logger.error("Bad connection " + c 00460 + " has been removed but cannot be replaced."); 00461 } 00462 else 00463 { 00464 freeConnections.push(newConnection); 00465 freeConnections.notify(); 00466 if (logger.isDebugEnabled()) 00467 logger.debug("Bad connection " + c 00468 + " has been replaced by a new connection."); 00469 } 00470 } 00471 else if (logger.isDebugEnabled()) 00472 logger.debug("Bad connection " + c + " has been removed."); 00473 } 00474 else 00475 logger.error("Failed to release connection " + c 00476 + " (not found in active pool)"); 00477 } 00478 } 00479 00483 public String getXmlImpl() 00484 { 00485 StringBuffer info = new StringBuffer(); 00486 info.append("<" + DatabasesXmlTags.ELT_VariablePoolConnectionManager + " " 00487 + DatabasesXmlTags.ATT_initPoolSize + "=\"" + initPoolSize + "\" " 00488 + DatabasesXmlTags.ATT_minPoolSize + "=\"" + minPoolSize + "\" " 00489 + DatabasesXmlTags.ATT_maxPoolSize + "=\"" + maxPoolSize + "\" " 00490 + DatabasesXmlTags.ATT_idleTimeout + "=\"" + idleTimeout / 1000 + "\" " 00491 + DatabasesXmlTags.ATT_waitTimeout + "=\"" + waitTimeout / 1000 00492 + "\"/>"); 00493 return info.toString(); 00494 } 00495 00502 protected class RemoveIdleConnectionsThread extends Thread 00503 { 00504 private boolean isKilled = false; 00505 00506 protected RemoveIdleConnectionsThread(String pBackendName) 00507 { 00508 super("RemoveIdleConnectionsThread for backend:" + pBackendName); 00509 } 00510 00514 public void run() 00515 { 00516 long idleTime, releaseTime; 00517 synchronized (this) 00518 { 00519 try 00520 { 00521 while (!isKilled) 00522 { 00523 // the thread is not launched if idleTimeout equals to 0 (the 00524 // connections are never released in this case) 00525 if (freeConnections.isEmpty() 00526 || (freeConnections.size() == minPoolSize)) 00527 { 00528 wait(); 00529 } 00530 00531 Connection c = null; 00532 synchronized (freeConnections) 00533 { 00534 if (releaseTimes.isEmpty()) 00535 continue; // Sanity check 00536 00537 releaseTime = ((Long) releaseTimes.get(0)).longValue(); 00538 idleTime = System.currentTimeMillis() - releaseTime; 00539 00540 if (idleTime >= idleTimeout) 00541 c = (Connection) freeConnections.remove(0); 00542 } 00543 00544 if (c == null) 00545 { // Nothing to free, wait for next deadline 00546 wait(idleTimeout - idleTime); 00547 } 00548 else 00549 { // Free the connection out of the synchronized block 00550 try 00551 { 00552 c.close(); 00553 } 00554 catch (SQLException e) 00555 { 00556 String msg = "An error occured while closing idle connection after the timeout: " 00557 + e; 00558 logger.error(msg); 00559 } 00560 finally 00561 { 00562 releaseTimes.remove(0); 00563 poolSize--; 00564 } 00565 logger.debug("Released idle connection (idle timeout reached)"); 00566 continue; 00567 00568 } 00569 } 00570 } 00571 catch (InterruptedException e) 00572 { 00573 logger 00574 .error("Wait on removeIdleConnectionsThread interrupted in VariablePoolConnectionManager: " 00575 + e); 00576 } 00577 } 00578 } 00579 } 00580 00581 }

CJDBCversion1.0.4に対してTue Oct 12 15:16:01 2004に生成されました。 doxygen 1.3.8