1 24 25 package org.objectweb.cjdbc.controller.connection; 26 27 import java.sql.Connection ; 28 import java.sql.SQLException ; 29 import java.util.Iterator ; 30 import java.util.LinkedList ; 31 import java.util.NoSuchElementException ; 32 33 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException; 34 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags; 35 36 53 public class VariablePoolConnectionManager 54 extends AbstractPoolConnectionManager 55 { 56 57 public static final int DEFAULT_MAX_POOL_SIZE = 0; 58 59 63 public static final int DEFAULT_IDLE_TIMEOUT = 0; 64 65 69 public static final int DEFAULT_WAIT_TIMEOUT = 0; 70 71 72 private int initPoolSize; 73 74 75 private int minPoolSize; 76 77 78 private int maxPoolSize; 79 80 84 private int idleTimeout; 85 86 87 private int waitTimeout; 88 89 90 private LinkedList releaseTimes; 91 92 93 private RemoveIdleConnectionsThread removeIdleConnectionsThread; 94 95 117 public VariablePoolConnectionManager(String backendUrl, String backendName, 118 String rLogin, String rPassword, String driverPath, 119 String driverClassName, int minPoolSize, int maxPoolSize, 120 int idleTimeout, int waitTimeout) 121 { 122 this(backendUrl, backendName, rLogin, rPassword, driverPath, 123 driverClassName, minPoolSize, minPoolSize, maxPoolSize, idleTimeout, 124 waitTimeout); 125 } 126 127 130 protected Object clone() throws CloneNotSupportedException 131 { 132 return new VariablePoolConnectionManager(backendUrl, backendName, rLogin, 133 rPassword, driverPath, driverClassName, minPoolSize, maxPoolSize, 134 idleTimeout, waitTimeout); 135 } 136 137 159 public VariablePoolConnectionManager(String backendUrl, String backendName, 160 String rLogin, String rPassword, String driverPath, 161 String driverClassName, int initPoolSize, int minPoolSize, 162 int maxPoolSize, int idleTimeout, int waitTimeout) 163 { 164 super(backendUrl, backendName, rLogin, rPassword, driverPath, 165 driverClassName, maxPoolSize == 0 ? (initPoolSize > minPoolSize 166 ? initPoolSize 167 : minPoolSize) : maxPoolSize); 168 this.initPoolSize = initPoolSize; 169 this.minPoolSize = minPoolSize; 170 this.maxPoolSize = maxPoolSize; 171 this.idleTimeout = idleTimeout * 1000; 172 this.waitTimeout = waitTimeout * 1000; 173 } 174 175 180 public int getMaxPoolSize() 181 { 182 return maxPoolSize; 183 } 184 185 190 public int getMinPoolSize() 191 { 192 return minPoolSize; 193 } 194 195 200 public int getIdleTimeout() 201 { 202 return idleTimeout; 203 } 204 205 210 public int getWaitTimeout() 211 { 212 return waitTimeout; 213 } 214 215 218 public void initializeConnections() throws SQLException 219 { 220 poolSize = maxPoolSize == 0 ? (initPoolSize > minPoolSize 221 ? initPoolSize 222 : minPoolSize) : maxPoolSize; 223 synchronized (this) 224 { 225 super.initializeConnections(initPoolSize); 226 227 if (idleTimeout != 0) 228 { 229 removeIdleConnectionsThread = new RemoveIdleConnectionsThread( 231 this.backendName, this); 232 233 releaseTimes = new LinkedList (); 236 Iterator it = freeConnections.iterator(); 237 Long currentTime = new Long (System.currentTimeMillis()); 238 while (it.hasNext()) 239 { 240 it.next(); 241 releaseTimes.addLast(currentTime); 242 } 243 } 244 } 245 246 if (removeIdleConnectionsThread != null) 248 { 249 removeIdleConnectionsThread.start(); 250 251 synchronized (removeIdleConnectionsThread) 252 { 253 if (releaseTimes.size() > 0) 254 { 255 removeIdleConnectionsThread.notify(); 256 } 257 } 258 } 259 } 260 261 264 public void finalizeConnections() throws SQLException 265 { 266 synchronized (this) 267 { 268 super.finalizeConnections(); 269 } 270 271 if (removeIdleConnectionsThread != null) 272 { 273 synchronized (removeIdleConnectionsThread) 274 { 275 removeIdleConnectionsThread.isKilled = true; 276 idleTimeout = 0; 277 removeIdleConnectionsThread.notify(); 278 } 279 try 280 { 281 removeIdleConnectionsThread.join(); 282 } 283 catch (InterruptedException e) 284 { 285 } 286 } 287 } 288 289 304 public synchronized Connection getConnection() 305 throws UnreachableBackendException 306 { 307 if (!initialized) 308 { 309 logger 310 .error("Requesting a connection from a non-initialized connection manager"); 311 return null; 312 } 313 314 long lTimeout = waitTimeout; 315 if (freeConnections.isEmpty()) 316 { 317 if ((maxPoolSize == 0) || (activeConnections.size() < maxPoolSize)) 318 { 319 Connection c = getConnectionFromDriver(); 320 if (c == null) 321 { 322 if (activeConnections.size() == 0) 323 { logger.error("Backend " + backendName + " is no more accessible."); 326 throw new UnreachableBackendException(); 327 } 328 if (logger.isWarnEnabled()) 330 logger.warn("Failed to create new connection on backend '" 331 + backendName + "', waiting for a connection to be freed."); 332 } 333 else 334 { 335 freeConnections.addLast(c); 336 if (idleTimeout != 0) 337 { 338 releaseTimes.add(new Long (System.currentTimeMillis())); 339 } 340 poolSize++; 341 } 342 } 343 344 352 while (freeConnections.isEmpty()) 353 { 354 try 356 { 357 if (lTimeout > 0) 358 { 359 long start = System.currentTimeMillis(); 360 this.wait(waitTimeout); 362 long end = System.currentTimeMillis(); 363 lTimeout -= end - start; 364 if (lTimeout <= 0) 365 { 366 if (logger.isWarnEnabled()) 367 logger.warn("Timeout expired for connection on backend '" 368 + backendName 369 + "', consider increasing pool size (current size is " 370 + poolSize + ") or timeout (current timeout is " 371 + (waitTimeout / 1000) + " seconds)"); 372 return null; 373 } 374 } 375 else 376 { 377 this.wait(); 378 } 379 } 380 catch (InterruptedException e) 381 { 382 logger 383 .error("Wait on freeConnections interrupted in VariablePoolConnectionManager"); 384 return null; 385 } 386 } 387 } 388 389 try 391 { 392 Connection c = (Connection ) freeConnections.removeLast(); 393 if (idleTimeout != 0) 394 releaseTimes.removeLast(); 395 activeConnections.add(c); 396 return c; 397 } 398 catch (NoSuchElementException e) 399 { 400 if (logger.isErrorEnabled()) 401 logger.error("Failed to get a connection on backend '" + backendName 402 + "' but an idle connection was expected"); 403 return null; 404 } 405 } 406 407 410 public void releaseConnection(Connection c) 411 { 412 boolean notifyThread = false; 413 synchronized (this) 414 { 415 if (!initialized) 416 return; 418 if (activeConnections.remove(c)) 419 { 420 if (idleTimeout != 0) 421 { 422 notifyThread = freeConnections.isEmpty() 423 || (freeConnections.size() == minPoolSize); 424 releaseTimes.addLast(new Long (System.currentTimeMillis())); 425 } 426 freeConnections.addLast(c); 427 this.notify(); 428 } 429 else 430 logger.error("Failed to release connection " + c 431 + " (not found in active pool)"); 432 } 433 434 if (notifyThread) 435 synchronized (removeIdleConnectionsThread) 436 { 437 removeIdleConnectionsThread.notify(); 438 } 439 } 440 441 444 public synchronized void deleteConnection(Connection c) 445 { 446 if (!initialized) 447 return; 449 if (activeConnections.remove(c)) 450 { 451 poolSize--; 452 if (poolSize < minPoolSize) 453 { 454 Connection newConnection = getConnectionFromDriver(); 455 if (newConnection == null) 456 { 457 if (logger.isDebugEnabled()) 458 logger.error("Bad connection " + c 459 + " has been removed but cannot be replaced."); 460 } 461 else 462 { 463 poolSize++; 464 freeConnections.addLast(newConnection); 465 if (idleTimeout != 0) 466 releaseTimes.addLast(new Long (System.currentTimeMillis())); 467 this.notify(); 468 if (logger.isDebugEnabled()) 469 logger.debug("Bad connection " + c 470 + " has been replaced by a new connection."); 471 } 472 } 473 else if (logger.isDebugEnabled()) 474 logger.debug("Bad connection " + c + " has been removed."); 475 } 476 else 477 logger.error("Failed to release connection " + c 478 + " (not found in active pool)"); 479 } 480 481 484 public String getXmlImpl() 485 { 486 StringBuffer info = new StringBuffer (); 487 info.append("<" + DatabasesXmlTags.ELT_VariablePoolConnectionManager + " " 488 + DatabasesXmlTags.ATT_initPoolSize + "=\"" + initPoolSize + "\" " 489 + DatabasesXmlTags.ATT_minPoolSize + "=\"" + minPoolSize + "\" " 490 + DatabasesXmlTags.ATT_maxPoolSize + "=\"" + maxPoolSize + "\" " 491 + DatabasesXmlTags.ATT_idleTimeout + "=\"" + idleTimeout / 1000 + "\" " 492 + DatabasesXmlTags.ATT_waitTimeout + "=\"" + waitTimeout / 1000 493 + "\"/>"); 494 return info.toString(); 495 } 496 497 502 protected class RemoveIdleConnectionsThread extends Thread 503 { 504 private boolean isKilled = false; 505 private VariablePoolConnectionManager thisPool; 506 507 protected RemoveIdleConnectionsThread(String pBackendName, 508 VariablePoolConnectionManager thisPool) 509 { 510 super("RemoveIdleConnectionsThread for backend:" + pBackendName); 511 this.thisPool = thisPool; 512 } 513 514 517 public void run() 518 { 519 long idleTime, releaseTime; 520 synchronized (this) 521 { 522 try 523 { 524 while (!isKilled) 525 { 526 if (freeConnections.isEmpty() 529 || (freeConnections.size() == minPoolSize)) 530 { 531 wait(); } 533 if (isKilled) 534 continue; 536 Connection c = null; 537 synchronized (thisPool) 538 { 539 if (releaseTimes.isEmpty()) 540 continue; 542 releaseTime = ((Long ) releaseTimes.get(0)).longValue(); 543 idleTime = System.currentTimeMillis() - releaseTime; 544 545 if (idleTime >= idleTimeout) 546 c = (Connection ) freeConnections.remove(0); 547 } 548 549 if (c == null) 550 { wait(idleTimeout - idleTime); 552 } 553 else 554 { try 556 { 557 c.close(); 558 } 559 catch (SQLException e) 560 { 561 String msg = "An error occured while closing idle connection after the timeout: " 562 + e; 563 logger.error(msg); 564 } 565 finally 566 { 567 releaseTimes.remove(0); 568 poolSize--; 569 } 570 logger.debug("Released idle connection (idle timeout reached)"); 571 continue; 572 573 } 574 } 575 } 576 catch (InterruptedException e) 577 { 578 logger 579 .error("Wait on removeIdleConnectionsThread interrupted in VariablePoolConnectionManager: " 580 + e); 581 } 582 } 583 } 584 } 585 586 } | Popular Tags |