1 56 57 package org.objectstyle.cayenne.conn; 58 59 import java.io.PrintWriter ; 60 import java.sql.Connection ; 61 import java.sql.SQLException ; 62 import java.util.LinkedList ; 63 import java.util.List ; 64 import java.util.ListIterator ; 65 66 import javax.sql.ConnectionEvent ; 67 import javax.sql.ConnectionEventListener ; 68 import javax.sql.ConnectionPoolDataSource ; 69 import javax.sql.DataSource ; 70 import javax.sql.PooledConnection ; 71 72 import org.apache.log4j.Logger; 73 74 83 public class PoolManager implements DataSource , ConnectionEventListener { 84 private static Logger logObj = Logger.getLogger(PoolManager.class); 85 86 92 public static final int MAX_QUEUE_WAIT = 20000; 93 94 protected ConnectionPoolDataSource poolDataSource; 95 protected int minConnections; 96 protected int maxConnections; 97 protected String dataSourceUrl; 98 protected String jdbcDriver; 99 protected String password; 100 protected String userName; 101 102 protected List unusedPool; 103 protected List usedPool; 104 105 private PoolMaintenanceThread poolMaintenanceThread; 106 107 108 112 public PoolManager( 113 String jdbcDriver, 114 String dataSourceUrl, 115 int minCons, 116 int maxCons, 117 String userName, 118 String password) 119 throws SQLException { 120 121 this(jdbcDriver, dataSourceUrl, minCons, maxCons, userName, password, null); 122 } 123 124 public PoolManager( 125 String jdbcDriver, 126 String dataSourceUrl, 127 int minCons, 128 int maxCons, 129 String userName, 130 String password, 131 ConnectionEventLoggingDelegate logger) 132 throws SQLException { 133 134 if (logger != null) { 135 DataSourceInfo info = new DataSourceInfo(); 136 info.setJdbcDriver(jdbcDriver); 137 info.setDataSourceUrl(dataSourceUrl); 138 info.setMinConnections(minCons); 139 info.setMaxConnections(maxCons); 140 info.setUserName(userName); 141 info.setPassword(password); 142 logger.logPoolCreated(info); 143 } 144 145 this.jdbcDriver = jdbcDriver; 146 this.dataSourceUrl = dataSourceUrl; 147 DriverDataSource driverDS = new DriverDataSource(jdbcDriver, dataSourceUrl); 148 driverDS.setLogger(logger); 149 PoolDataSource poolDS = new PoolDataSource(driverDS); 150 init(poolDS, minCons, maxCons, userName, password); 151 } 152 153 163 public PoolManager( 164 ConnectionPoolDataSource poolDataSource, 165 int minCons, 166 int maxCons, 167 String userName, 168 String password) 169 throws SQLException { 170 init(poolDataSource, minCons, maxCons, userName, password); 171 } 172 173 174 protected void init( 175 ConnectionPoolDataSource poolDataSource, 176 int minCons, 177 int maxCons, 178 String userName, 179 String password) 180 throws SQLException { 181 182 if (maxConnections < 0) { 184 throw new SQLException ( 185 "Maximum number of connections can not be negative (" + maxCons + ")."); 186 } 187 188 if (minConnections < 0) { 189 throw new SQLException ( 190 "Minimum number of connections can not be negative (" + minCons + ")."); 191 } 192 193 if (minConnections > maxConnections) { 194 throw new SQLException ("Minimum number of connections can not be bigger then maximum."); 195 } 196 197 this.userName = userName; 199 this.password = password; 200 this.minConnections = minCons; 201 this.maxConnections = maxCons; 202 this.poolDataSource = poolDataSource; 203 204 usedPool = new LinkedList (); 206 unusedPool = new LinkedList (); 207 growPool(minConnections, userName, password); 208 209 startMaintenanceThread(); 210 } 211 212 protected synchronized void startMaintenanceThread() { 213 disposeOfMaintenanceThread(); 214 this.poolMaintenanceThread = new PoolMaintenanceThread(this); 215 this.poolMaintenanceThread.start(); 216 } 217 218 222 protected PooledConnection newPooledConnection(String userName, String password) 223 throws SQLException { 224 PooledConnection connection = 225 (userName != null) 226 ? poolDataSource.getPooledConnection(userName, password) 227 : poolDataSource.getPooledConnection(); 228 connection.addConnectionEventListener(this); 229 return connection; 230 } 231 232 233 public void dispose() throws SQLException { 234 synchronized (this) { 235 ListIterator unusedIterator = unusedPool.listIterator(); 237 while (unusedIterator.hasNext()) { 238 PooledConnection con = (PooledConnection ) unusedIterator.next(); 239 con.close(); 241 unusedIterator.remove(); 243 } 244 245 ListIterator usedIterator = usedPool.listIterator(); 247 while (usedIterator.hasNext()) { 248 PooledConnection con = (PooledConnection ) usedIterator.next(); 249 con.removeConnectionEventListener(this); 251 con.close(); 253 usedIterator.remove(); 255 } 256 } 257 258 disposeOfMaintenanceThread(); 259 } 260 261 protected void disposeOfMaintenanceThread() { 262 if (poolMaintenanceThread != null) { 263 this.poolMaintenanceThread.dispose(); 264 } 265 } 266 267 270 protected synchronized boolean canGrowPool() { 271 return getPoolSize() < maxConnections; 272 } 273 274 280 protected synchronized int growPool( 281 int addConnections, 282 String userName, 283 String password) 284 throws SQLException { 285 286 int i = 0; 287 int startPoolSize = getPoolSize(); 288 for (; i < addConnections && startPoolSize + i < maxConnections; i++) { 289 PooledConnection newConnection = newPooledConnection(userName, password); 290 unusedPool.add(newConnection); 291 } 292 293 return i; 294 } 295 296 protected synchronized void shrinkPool(int closeConnections) { 297 int idleSize = unusedPool.size(); 298 for (int i = 0; i < closeConnections && i < idleSize; i++) { 299 PooledConnection con = (PooledConnection ) unusedPool.remove(i); 300 301 try { 302 con.close(); 303 } catch (SQLException ex) { 304 logObj.info("Error closing connection. Ignoring.", ex); 305 } 306 } 307 } 308 309 314 public int getMaxConnections() { 315 return maxConnections; 316 } 317 318 public void setMaxConnections(int maxConnections) { 319 this.maxConnections = maxConnections; 320 } 321 322 324 public int getMinConnections() { 325 return minConnections; 326 } 327 328 public void setMinConnections(int minConnections) { 329 this.minConnections = minConnections; 330 } 331 332 334 public String getDataSourceUrl() { 335 return dataSourceUrl; 336 } 337 338 340 public String getJdbcDriver() { 341 return jdbcDriver; 342 } 343 344 345 public String getPassword() { 346 return password; 347 } 348 349 350 public String getUserName() { 351 return userName; 352 } 353 354 357 public synchronized int getPoolSize() { 358 return usedPool.size() + unusedPool.size(); 359 } 360 361 365 public synchronized int getCurrentlyInUse() { 366 return usedPool.size(); 367 } 368 369 374 public synchronized int getCurrentlyUnused() { 375 return unusedPool.size(); 376 } 377 378 384 public Connection getConnection() throws SQLException { 385 return getConnection(userName, password); 386 } 387 388 389 public synchronized Connection getConnection(String userName, String password) 390 throws SQLException { 391 392 PooledConnection pooledConnection = uncheckPooledConnection(userName, password); 393 394 try { 395 return uncheckConnection(pooledConnection); 396 } 397 catch (SQLException ex) { 398 logObj.info("Error getting connection", ex); 399 400 try { 401 pooledConnection.close(); 402 } 403 catch (SQLException ignored) { 404 } 405 406 logObj.info("Reconnecting..."); 407 408 pooledConnection = uncheckPooledConnection(userName, password); 410 try { 411 return uncheckConnection(pooledConnection); 412 } 413 catch (SQLException reconnectEx) { 414 try { 415 pooledConnection.close(); 416 } 417 catch (SQLException ignored) { 418 } 419 420 throw reconnectEx; 421 } 422 } 423 } 424 425 private Connection uncheckConnection(PooledConnection pooledConnection) 426 throws SQLException { 427 Connection c = pooledConnection.getConnection(); 428 429 usedPool.add(pooledConnection); 431 return c; 432 } 433 434 private PooledConnection uncheckPooledConnection(String userName, String password) 435 throws SQLException { 436 439 if (unusedPool.size() == 0) { 440 441 if (canGrowPool()) { 443 return newPooledConnection(userName, password); 444 } 445 446 448 long waitTill = 453 System.currentTimeMillis() 454 + MAX_QUEUE_WAIT; 455 456 do { 457 try { 458 wait(MAX_QUEUE_WAIT); 459 } catch (InterruptedException iex) { 460 } 462 463 } while (unusedPool.size() == 0 && waitTill > System.currentTimeMillis()); 464 465 if (unusedPool.size() == 0) { 466 throw new SQLException ( 467 "Can't obtain connection. Request timed out. Total used connections: " 468 + usedPool.size()); 469 } 470 } 471 472 return (PooledConnection ) unusedPool.remove(0); 474 } 475 476 public int getLoginTimeout() throws java.sql.SQLException { 477 return poolDataSource.getLoginTimeout(); 478 } 479 480 public void setLoginTimeout(int seconds) throws java.sql.SQLException { 481 poolDataSource.setLoginTimeout(seconds); 482 } 483 484 public PrintWriter getLogWriter() throws java.sql.SQLException { 485 return poolDataSource.getLogWriter(); 486 } 487 488 public void setLogWriter(PrintWriter out) throws java.sql.SQLException { 489 poolDataSource.setLogWriter(out); 490 } 491 492 495 public synchronized void connectionClosed(ConnectionEvent event) { 496 PooledConnection closedConn = (PooledConnection ) event.getSource(); 498 499 int usedInd = usedPool.indexOf(closedConn); 502 if (usedInd >= 0) { 503 usedPool.remove(usedInd); 504 unusedPool.add(closedConn); 505 506 notifyAll(); 508 } 509 } 513 514 519 public synchronized void connectionErrorOccurred(ConnectionEvent event) { 520 523 PooledConnection errorSrc = (PooledConnection ) event.getSource(); 524 525 528 int usedInd = usedPool.indexOf(errorSrc); 529 if (usedInd >= 0) { 530 usedPool.remove(usedInd); 531 } else { 532 int unusedInd = unusedPool.indexOf(errorSrc); 533 if (unusedInd >= 0) 534 unusedPool.remove(unusedInd); 535 } 536 537 } 541 542 static class PoolMaintenanceThread extends Thread { 543 protected boolean shouldDie; 544 protected PoolManager pool; 545 546 PoolMaintenanceThread(PoolManager pool) { 547 super.setName("PoolManagerCleanup-" + pool.hashCode()); 548 super.setDaemon(true); 549 this.pool = pool; 550 } 551 552 public void run() { 553 while (true) { 555 556 try { 557 sleep(600000); 559 } catch (InterruptedException iex) { 560 } 562 563 if (shouldDie) { 564 break; 565 } 566 567 synchronized (pool) { 568 572 int unused = pool.getCurrentlyUnused(); 573 int used = pool.getCurrentlyInUse(); 574 int total = unused + used; 575 int median = 576 pool.minConnections 577 + 1 578 + (pool.maxConnections - pool.minConnections) / 2; 579 580 if (unused > 0 && total > median) { 581 pool.shrinkPool(1); 582 logObj.debug("decreased pool size to " + (total - 1) + " connections."); 583 } 584 } 585 } 586 } 587 588 591 public void dispose() { 592 shouldDie = true; 593 } 594 } 595 } | Popular Tags |