| 1 23 24 package org.continuent.sequoia.controller.connection; 25 26 import java.sql.Connection ; 27 import java.sql.SQLException ; 28 import java.sql.Statement ; 29 import java.util.Iterator ; 30 import java.util.LinkedList ; 31 import java.util.NoSuchElementException ; 32 33 import org.continuent.sequoia.common.exceptions.UnreachableBackendException; 34 import org.continuent.sequoia.common.xml.DatabasesXmlTags; 35 36 53 public class VariablePoolConnectionManager 54 extends AbstractPoolConnectionManager 55 { 56 57 private int initPoolSize; 58 59 60 private int minPoolSize; 61 62 63 private int maxPoolSize; 64 65 69 private int idleTimeout; 70 71 72 private int waitTimeout; 73 74 75 private LinkedList releaseTimes; 76 77 78 private RemoveIdleConnectionsThread removeIdleConnectionsThread; 79 80 private Connection ping; 81 82 104 public VariablePoolConnectionManager(String backendUrl, String backendName, 105 String rLogin, String rPassword, String driverPath, 106 String driverClassName, int minPoolSize, int maxPoolSize, 107 int idleTimeout, int waitTimeout) 108 { 109 this(backendUrl, backendName, rLogin, rPassword, driverPath, 110 driverClassName, minPoolSize, minPoolSize, maxPoolSize, idleTimeout, 111 waitTimeout); 112 } 113 114 117 protected Object clone() throws CloneNotSupportedException  118 { 119 return new VariablePoolConnectionManager(backendUrl, backendName, rLogin, 120 rPassword, driverPath, driverClassName, minPoolSize, maxPoolSize, 121 idleTimeout, waitTimeout); 122 } 123 124 128 public AbstractConnectionManager clone(String rLogin, String rPassword) 129 { 130 return new VariablePoolConnectionManager(backendUrl, backendName, rLogin, 131 rPassword, driverPath, driverClassName, initPoolSize, minPoolSize, 132 maxPoolSize, idleTimeout, waitTimeout); 133 } 134 135 157 public VariablePoolConnectionManager(String backendUrl, String backendName, 158 String rLogin, String rPassword, String driverPath, 159 String driverClassName, int initPoolSize, int minPoolSize, 160 int maxPoolSize, int idleTimeout, int waitTimeout) 161 { 162 super(backendUrl, backendName, rLogin, rPassword, driverPath, 163 driverClassName, maxPoolSize == 0 ? (initPoolSize > minPoolSize 164 ? initPoolSize 165 : minPoolSize) : maxPoolSize); 166 this.initPoolSize = initPoolSize; 167 this.minPoolSize = minPoolSize; 168 this.maxPoolSize = maxPoolSize; 169 this.idleTimeout = idleTimeout * 1000; 170 this.waitTimeout = waitTimeout * 1000; 171 } 172 173 178 public int getMaxPoolSize() 179 { 180 return maxPoolSize; 181 } 182 183 188 public int getMinPoolSize() 189 { 190 return minPoolSize; 191 } 192 193 198 public int getIdleTimeout() 199 { 200 return idleTimeout; 201 } 202 203 208 public int getWaitTimeout() 209 { 210 return waitTimeout; 211 } 212 213 216 protected void doConnectionInitialization() throws SQLException  217 { 218 poolSize = maxPoolSize == 0 ? (initPoolSize > minPoolSize 219 ? initPoolSize 220 : minPoolSize) : maxPoolSize; 221 this.ping = getConnectionFromDriver(); 222 synchronized (this) 223 { 224 super.doConnectionInitialization(initPoolSize); 225 226 if (idleTimeout != 0) 227 { 228 removeIdleConnectionsThread = new RemoveIdleConnectionsThread( 230 this.backendName, this); 231 232 releaseTimes = new LinkedList (); 235 Iterator it = freeConnections.iterator(); 236 Long currentTime = new Long (System.currentTimeMillis()); 237 while (it.hasNext()) 238 { 239 it.next(); 240 releaseTimes.addLast(currentTime); 241 } 242 } 243 } 244 245 if (removeIdleConnectionsThread != null) 247 { 248 removeIdleConnectionsThread.start(); 249 250 synchronized (removeIdleConnectionsThread) 251 { 252 if (releaseTimes.size() > 0) 253 { 254 removeIdleConnectionsThread.notify(); 255 } 256 } 257 } 258 if (idlePersistentConnectionPingInterval > 0) 259 { 260 persistentConnectionPingerThread = new IdlePersistentConnectionsPingerThread( 261 backendName, this); 262 persistentConnectionPingerThread.start(); 263 idlePersistentConnectionPingRunning = true; 264 } 265 } 266 267 270 protected void doConnectionFinalization() throws SQLException  271 { 272 try 273 { 274 if (ping != null) 275 ping.close(); 276 ping = null; 277 } 278 catch (SQLException e) 279 { 280 } 282 synchronized (this) 283 { 284 super.doConnectionFinalization(); 285 } 286 287 if (removeIdleConnectionsThread != null) 288 { 289 synchronized (removeIdleConnectionsThread) 290 { 291 removeIdleConnectionsThread.isKilled = true; 292 removeIdleConnectionsThread.notify(); 293 } 294 try 295 { 296 removeIdleConnectionsThread.join(); 297 } 298 catch (InterruptedException e) 299 { 300 } 301 } 302 } 303 304 319 public synchronized PooledConnection getConnection() 320 throws UnreachableBackendException 321 { 322 if (!initialized) 323 { 324 logger 325 .error("Requesting a connection from a non-initialized connection manager"); 326 return null; 327 } 328 if (isShutdown) 329 { 330 return null; 331 } 332 long lTimeout = waitTimeout; 333 if (freeConnections.isEmpty()) 334 { 335 if ((maxPoolSize == 0) || (activeConnections.size() < maxPoolSize)) 336 { 337 Connection c = getConnectionFromDriver(); 338 if (c == null) 339 { 340 if (activeConnections.size() == 0) 341 { logger.error("Backend " + backendName + " is no more accessible."); 344 throw new UnreachableBackendException(); 345 } 346 351 try 352 { 353 Statement pingStatement = ping.createStatement(); 354 pingStatement.execute(connectionTestStatement); 355 pingStatement.close(); 356 } 357 catch (SQLException e) 358 { 359 isShutdown = true; 360 logger.error("Backend " + backendName + " is no more accessible."); 361 throw new UnreachableBackendException(); 362 } 363 367 368 if (logger.isWarnEnabled()) 369 logger.warn("Failed to create new connection on backend '" 370 + backendName + "', waiting for a connection to be freed."); 371 } 372 else 373 { 374 freeConnections.addLast(new PooledConnection(c)); 375 if (idleTimeout != 0) 376 { 377 releaseTimes.add(new Long (System.currentTimeMillis())); 378 } 379 poolSize++; 380 } 381 } 382 383 391 while (freeConnections.isEmpty()) 392 { 393 if (activeConnections.size() == 0 || isShutdown) 394 { logger.error("Backend " + backendName + " is no more accessible."); 397 throw new UnreachableBackendException(); 398 } 399 try 401 { 402 if (lTimeout > 0) 403 { 404 long start = System.currentTimeMillis(); 405 this.wait(waitTimeout); 407 long end = System.currentTimeMillis(); 408 lTimeout -= end - start; 409 if (lTimeout <= 0) 410 { 411 if (logger.isWarnEnabled()) 412 logger.warn("Timeout expired for connection on backend '" 413 + backendName 414 + "', consider increasing pool size (current size is " 415 + poolSize + ") or timeout (current timeout is " 416 + (waitTimeout / 1000) + " seconds)"); 417 return null; 418 } 419 } 420 else 421 { 422 this.wait(); 423 } 424 } 425 catch (InterruptedException e) 426 { 427 logger 428 .error("Wait on freeConnections interrupted in VariablePoolConnectionManager"); 429 return null; 430 } 431 } 432 } 433 434 try 436 { 437 PooledConnection c = (PooledConnection) freeConnections.removeLast(); 438 if (idleTimeout != 0) 439 releaseTimes.removeLast(); 440 activeConnections.add(c); 441 return c; 442 } 443 catch (NoSuchElementException e) 444 { 445 if (logger.isErrorEnabled()) 446 logger.error("Failed to get a connection on backend '" + backendName 447 + "' but an idle connection was expected"); 448 return null; 449 } 450 } 451 452 455 public void releaseConnection(PooledConnection c) 456 { 457 boolean notifyThread = false; 458 synchronized (this) 459 { 460 if (!initialized) 461 { 462 closeConnection(c); 463 return; } 465 466 if (activeConnections.remove(c)) 467 { 468 if (idleTimeout != 0) 469 { 470 notifyThread = freeConnections.isEmpty() 471 || (freeConnections.size() == minPoolSize); 472 releaseTimes.addLast(new Long (System.currentTimeMillis())); 473 } 474 freeConnections.addLast(c); 475 this.notify(); 476 } 477 else 478 logger.error("Failed to release connection " + c 479 + " (not found in active pool)"); 480 } 481 482 if (notifyThread) 483 synchronized (removeIdleConnectionsThread) 484 { 485 removeIdleConnectionsThread.notify(); 486 } 487 } 488 489 492 public synchronized void deleteConnection(PooledConnection c) 493 { 494 closeConnection(c); 495 496 if (!initialized) 497 return; 499 if (activeConnections.remove(c)) 500 { 501 poolSize--; 502 if (poolSize < minPoolSize) 503 { 504 Connection newConnection = getConnectionFromDriver(); 505 if (newConnection == null) 506 { 507 if (logger.isDebugEnabled()) 508 logger.error("Bad connection " + c 509 + " has been removed but cannot be replaced."); 510 } 511 else 512 { 513 poolSize++; 514 freeConnections.addLast(new PooledConnection(newConnection)); 515 if (idleTimeout != 0) 516 releaseTimes.addLast(new Long (System.currentTimeMillis())); 517 this.notify(); 518 if (logger.isDebugEnabled()) 519 logger.debug("Bad connection " + c 520 + " has been replaced by a new connection."); 521 } 522 } 523 else if (logger.isDebugEnabled()) 524 logger.debug("Bad connection " + c + " has been removed."); 525 } 526 else 527 logger.error("Failed to release connection " + c 528 + " (not found in active pool)"); 529 notifyAll(); 530 } 531 532 535 public String getXmlImpl() 536 { 537 StringBuffer info = new StringBuffer (); 538 info.append("<" + DatabasesXmlTags.ELT_VariablePoolConnectionManager + " " 539 + DatabasesXmlTags.ATT_initPoolSize + "=\"" + initPoolSize + "\" " 540 + DatabasesXmlTags.ATT_minPoolSize + "=\"" + minPoolSize + "\" " 541 + DatabasesXmlTags.ATT_maxPoolSize + "=\"" + maxPoolSize + "\" " 542 + DatabasesXmlTags.ATT_idleTimeout + "=\"" + idleTimeout / 1000 + "\" " 543 + DatabasesXmlTags.ATT_waitTimeout + "=\"" + waitTimeout / 1000 544 + "\"/>"); 545 return info.toString(); 546 } 547 548 553 protected class RemoveIdleConnectionsThread extends Thread  554 { 555 private boolean isKilled = false; 556 private VariablePoolConnectionManager thisPool; 557 558 protected RemoveIdleConnectionsThread(String pBackendName, 559 VariablePoolConnectionManager thisPool) 560 { 561 super("RemoveIdleConnectionsThread for backend:" + pBackendName); 562 this.thisPool = thisPool; 563 } 564 565 568 public void run() 569 { 570 long idleTime, releaseTime; 571 boolean isMinPoolSizeReached = false; 572 synchronized (this) 573 { 574 try 575 { 576 while (!isKilled) 577 { 578 isMinPoolSizeReached = false; 579 if (freeConnections.isEmpty()) 582 { 583 wait(); } 585 else if (freeConnections.size() <= minPoolSize) 586 { 587 isMinPoolSizeReached = true; 589 } 590 591 if (isKilled) 592 continue; 594 PooledConnection c = null; 595 synchronized (thisPool) 596 { 597 if (releaseTimes.isEmpty()) 598 continue; 600 releaseTime = ((Long ) releaseTimes.getFirst()).longValue(); 601 idleTime = System.currentTimeMillis() - releaseTime; 602 603 if (idleTime >= idleTimeout) 604 { 605 c = (PooledConnection) freeConnections.removeFirst(); 606 releaseTimes.removeFirst(); 607 } 608 } 609 610 if (c == null) 611 { wait(idleTimeout - idleTime); 613 } 614 else if (isMinPoolSizeReached) 615 { 616 try 617 { 618 c.getConnection().createStatement().execute(connectionTestStatement); 620 releaseTimes.addLast(new Long (System.currentTimeMillis())); 622 freeConnections.addLast(c); 623 } 624 catch (SQLException e) 625 { 626 try 628 { 629 c.getConnection().close(); 630 } 631 catch (SQLException e1) 632 { 633 String msg = "An error occured while closing idle connection after the timeout: " 634 + e; 635 logger.error(msg); 636 } 637 finally 638 { 639 poolSize--; 640 } 641 } 642 } 643 else 644 { try 646 { 647 c.getConnection().close(); 648 } 649 catch (SQLException e) 650 { 651 String msg = "An error occured while closing idle connection after the timeout: " 652 + e; 653 logger.error(msg); 654 } 655 finally 656 { 657 poolSize--; 658 } 659 logger.debug("Released idle connection (idle timeout reached)"); 660 continue; 661 662 } 663 } 664 } 665 catch (InterruptedException e) 666 { 667 logger 668 .error("Wait on removeIdleConnectionsThread interrupted in VariablePoolConnectionManager: " 669 + e); 670 } 671 } 672 } 673 } 674 675 } | Popular Tags |