1 25 package org.nemesis.forum.util.jdbc; 26 27 import java.io.IOException ; 28 import java.sql.*; 29 import java.util.Date ; 30 import java.util.Map ; 31 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.nemesis.forum.config.ConfigLoader; 35 36 46 public class DbConnectionDefaultPool extends DbConnectionProvider { 47 48 static protected Log log = LogFactory.getLog(DbConnectionDefaultPool.class); 49 50 private static final boolean POOLED = true; 51 52 private ConnectionPool connectionPool = null; 53 56 private Object initLock = new Object (); 57 58 public DbConnectionDefaultPool() { 59 60 } 61 62 65 public Connection getConnection() { 66 if (connectionPool == null) { 67 synchronized (initLock) { 69 if (connectionPool == null) { 71 log.error( 72 "Warning: DbConnectionDefaultPool.getConnection() was " 73 + "called when the internal pool has not been initialized."); 74 return null; 75 } 76 } 77 } 78 return new ConnectionWrapper(connectionPool.getConnection(), connectionPool); 79 } 80 81 84 protected void start() { 85 synchronized (initLock) { 87 String driver = ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("driver"); 89 String server = ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("url"); 90 String username = ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("username"); 91 String password = ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("password"); 92 int minConnections = 1, maxConnections = 5; 93 double connectionTimeout = 0.1; 94 95 try { 96 97 98 minConnections = Integer.parseInt(ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("minConnections")); 99 maxConnections = Integer.parseInt(ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("maxConnections")); 100 connectionTimeout = Double.parseDouble(ConfigLoader.getInstance().getConfig().getJDBCProviderProperties().getProperty("connectionTimeout")); 101 102 } catch (Exception e) { 103 log.error("your config is malformed",e); 104 } 105 106 try { 107 connectionPool = 108 new ConnectionPool( 109 driver, 110 server, 111 username, 112 password, 113 minConnections, 114 maxConnections, 115 connectionTimeout); 116 } catch (IOException ioe) { 117 log.error("Error starting DbConnectionDefaultPool: " , ioe); 118 } 119 } 120 } 121 122 125 protected void restart() { 126 destroy(); 128 start(); 130 } 131 132 135 protected void destroy() { 136 if (connectionPool != null) { 137 try { 138 connectionPool.destroy(1); 139 } catch (Exception e) { 140 log.error("",e); 141 } 142 } 143 connectionPool = null; 145 } 146 147 169 private class ConnectionPool implements Runnable { 170 private Thread runner; 171 172 private Connection[] connPool; 173 private int[] connStatus; 174 175 private long[] connLockTime, connCreateDate; 176 private String [] connID; 177 private String dbDriver, dbServer, dbLogin, dbPassword; 178 private int currConnections, connLast, minConns, maxConns, maxConnMSec; 179 180 private boolean available = true; 182 183 private SQLWarning currSQLWarning; 184 private String pid; 185 186 197 public ConnectionPool( 198 String dbDriver, 199 String dbServer, 200 String dbLogin, 201 String dbPassword, 202 int minConns, 203 int maxConns, 204 double maxConnTime) 205 throws IOException { 206 connPool = new Connection[maxConns]; 207 connStatus = new int[maxConns]; 208 connLockTime = new long[maxConns]; 209 connCreateDate = new long[maxConns]; 210 connID = new String [maxConns]; 211 currConnections = minConns; 212 this.maxConns = maxConns; 213 this.dbDriver = dbDriver; 214 this.dbServer = dbServer; 215 this.dbLogin = dbLogin; 216 this.dbPassword = dbPassword; 217 218 maxConnMSec = (int) (maxConnTime * 86400000.0); if (maxConnMSec < 30000) { maxConnMSec = 30000; 221 } 222 223 225 log.info("Starting ConnectionPool:"); 226 log.info("dbDriver = " + dbDriver); 227 log.info("dbServer = " + dbServer); 228 log.info("dbLogin = " + dbLogin); 229 log.info("minconnections = " + minConns); 230 log.info("maxconnections = " + maxConns); 231 log.info("Total refresh interval = " + maxConnTime + " days"); 232 log.info("-----------------------------------------"); 233 234 boolean connectionsSucceeded = false; 240 int dbLoop = 20; 241 242 try { 243 for (int i = 1; i < dbLoop; i++) { 244 try { 245 for (int j = 0; j < currConnections; j++) { 246 createConn(j); 247 } 248 connectionsSucceeded = true; 249 break; 250 } catch (SQLException e) { 251 log.warn( 252 "--->Attempt (" 253 + String.valueOf(i) 254 + " of " 255 + String.valueOf(dbLoop) 256 + ") failed to create new connections set at startup: ", e); 257 log.info(" Will try again in 15 seconds..."); 258 try { 259 Thread.sleep(15000); 260 } catch (InterruptedException e1) { 261 } 262 } 263 } 264 if (!connectionsSucceeded) { log.warn("\r\nAll attempts at connecting to Database exhausted"); 266 throw new IOException (); 267 } 268 } catch (Exception e) { 269 log.error("",e); 270 throw new IOException (); 271 } 272 273 275 runner = new Thread (this); 276 runner.start(); 277 278 } 280 289 public void run() { 290 boolean forever = true; 291 Statement stmt = null; 292 String currCatalog = null; 293 294 while (forever) { 295 296 for (int i = 0; i < currConnections; i++) { 298 try { 299 currSQLWarning = connPool[i].getWarnings(); 300 if (currSQLWarning != null) { 301 log.warn("Warnings on connection " + String.valueOf(i) + " " + currSQLWarning); 302 connPool[i].clearWarnings(); 303 } 304 } catch (SQLException e) { 305 log.info("Cannot access Warnings: " + e); 306 } 307 } 308 309 for (int i = 0; i < currConnections; i++) { long age = System.currentTimeMillis() - connCreateDate[i]; 311 312 synchronized (connStatus) { 313 if (connStatus[i] > 0) { continue; 315 } 316 connStatus[i] = 2; } 318 319 try { if (age > maxConnMSec) { throw new SQLException(); 322 } 323 324 stmt = connPool[i].createStatement(); 325 connStatus[i] = 0; 329 if (connPool[i].isClosed()) { 331 throw new SQLException(); 332 } 333 } catch (SQLException e) { 335 try { 336 log.info( 337 new Date ().toString() + " ***** Recycling connection " + String.valueOf(i) + ":"); 338 339 connPool[i].close(); 340 createConn(i); 341 } catch (SQLException e1) { 342 log.error("Failed: " , e1); 343 connStatus[i] = 0; } 345 } finally { 346 try { 347 if (stmt != null) { 348 stmt.close(); 349 } 350 } catch (SQLException e1) { 351 }; 352 } 353 } 354 355 try { 356 Thread.sleep(20000); 357 } catch (InterruptedException e) { 359 return; 364 } 365 } 366 } 368 381 public Connection getConnection() { 382 383 Connection conn = null; 384 385 if (available) { 386 boolean gotOne = false; 387 388 for (int outerloop = 1; outerloop <= 10; outerloop++) { 389 390 try { 391 int loop = 0; 392 int roundRobin = connLast + 1; 393 if (roundRobin >= currConnections) 394 roundRobin = 0; 395 396 do { 397 synchronized (connStatus) { 398 if ((connStatus[roundRobin] < 1) && (!connPool[roundRobin].isClosed())) { 399 conn = connPool[roundRobin]; 400 connStatus[roundRobin] = 1; 401 connLockTime[roundRobin] = System.currentTimeMillis(); 402 connLast = roundRobin; 403 gotOne = true; 404 break; 405 } else { 406 loop++; 407 roundRobin++; 408 if (roundRobin >= currConnections) 409 roundRobin = 0; 410 } 411 } 412 } while ((gotOne == false) && (loop < currConnections)); 413 } catch (SQLException e1) { 414 } 415 416 if (gotOne) { 417 break; 418 } else { 419 synchronized (this) { if (currConnections < maxConns) { 421 try { 422 createConn(currConnections); 423 currConnections++; 424 } catch (SQLException e) { 425 log.error("Unable to create new connection: " , e); 426 } 427 } 428 } 429 430 try { 431 Thread.sleep(2000); 432 } catch (InterruptedException e) { 433 } 434 log.info( 435 "-----> Connections Exhausted! Will wait and try " 436 + "again in loop " 437 + String.valueOf(outerloop)); 438 } 439 } 441 } else { 442 log.error("Unsuccessful getConnection() request during destroy()"); 443 } 445 return conn; 446 } 447 448 451 public int idOfConnection(Connection conn) { 452 int match; 453 String tag; 454 455 try { 456 tag = conn.toString(); 457 } catch (NullPointerException e1) { 458 tag = "none"; 459 } 460 461 match = -1; 462 463 for (int i = 0; i < currConnections; i++) { 464 if (connID[i].equals(tag)) { 465 match = i; 466 break; 467 } 468 } 469 return match; 470 } 471 472 476 public String freeConnection(Connection conn) { 477 String res = ""; 478 479 int thisconn = idOfConnection(conn); 480 if (thisconn >= 0) { 481 connStatus[thisconn] = 0; 482 res = "freed " + conn.toString(); 483 } else { 486 log.warn("----> Could not free connection!!!"); 487 } 488 489 return res; 490 } 491 492 496 public long getAge(Connection conn) { int thisconn = idOfConnection(conn); 498 return System.currentTimeMillis() - connLockTime[thisconn]; 499 } 500 501 private void createConn(int i) throws SQLException { 502 Date now = new Date (); 503 try { 504 Class.forName(dbDriver); 505 connPool[i] = DriverManager.getConnection(dbServer, dbLogin, dbPassword); 506 connStatus[i] = 0; 507 connID[i] = connPool[i].toString(); 508 connLockTime[i] = 0; 509 connCreateDate[i] = now.getTime(); 510 511 log.info( 512 now.toString() + " Opening connection " + String.valueOf(i) + " " + connPool[i].toString() + ":"); 513 } catch (ClassNotFoundException e2) { 514 log.error("",e2); 515 throw new SQLException(e2.getMessage()); 516 } 517 } 518 519 523 524 544 public void destroy(int millis) throws SQLException { 545 546 549 available = false; 551 552 runner.interrupt(); 554 555 try { 557 runner.join(millis); 558 } catch (InterruptedException e) { 559 } 561 566 long startTime = System.currentTimeMillis(); 567 568 int useCount; 571 while ((useCount = getUseCount()) > 0 && System.currentTimeMillis() - startTime <= millis) { 572 try { 573 Thread.sleep(500); 574 } catch (InterruptedException e) { 575 } } 577 578 for (int i = 0; i < currConnections; i++) { 580 try { 581 connPool[i].close(); 582 } catch (SQLException e1) { 583 log.warn("Cannot close connections on Destroy"); 584 } 585 } 586 587 if (useCount > 0) { 588 String msg = 590 "Unsafe shutdown: Had to close " + useCount + " active DB connections after " + millis + "ms"; 591 log.warn(msg); 592 593 throw new SQLException(msg); 596 } 597 598 599 } 601 609 public void destroy() { 610 try { 611 destroy(10000); 612 } catch (SQLException e) { 613 } 614 } 615 616 619 public int getUseCount() { 626 int useCount = 0; 627 synchronized (connStatus) { 628 for (int i = 0; i < currConnections; i++) { 629 if (connStatus[i] > 0) { useCount++; 631 } 632 } 633 } 634 return useCount; 635 } 637 640 public int getSize() { 641 return currConnections; 642 } 644 } 646 651 public class ConnectionWrapper implements Connection { 652 653 private Connection connection; 654 private ConnectionPool connectionPool; 655 656 public ConnectionWrapper(Connection connection, ConnectionPool connectionPool) { 657 this.connection = connection; 658 this.connectionPool = connectionPool; 659 } 660 661 665 public void close() throws SQLException { 666 connectionPool.freeConnection(this.connection); 667 connection = null; 670 connectionPool = null; 671 } 672 673 public String toString() { 674 if (connection != null) { 675 return connection.toString(); 676 } else { 677 return "connection wrapper"; 678 } 679 } 680 681 public Statement createStatement() throws SQLException { 682 return connection.createStatement(); 683 } 684 685 public PreparedStatement prepareStatement(String sql) throws SQLException { 686 return connection.prepareStatement(sql); 687 } 688 689 public CallableStatement prepareCall(String sql) throws SQLException { 690 return connection.prepareCall(sql); 691 } 692 693 public String nativeSQL(String sql) throws SQLException { 694 return connection.nativeSQL(sql); 695 } 696 697 public void setAutoCommit(boolean autoCommit) throws SQLException { 698 connection.setAutoCommit(autoCommit); 699 } 700 701 public boolean getAutoCommit() throws SQLException { 702 return connection.getAutoCommit(); 703 } 704 705 public void commit() throws SQLException { 706 connection.commit(); 707 } 708 709 public void rollback() throws SQLException { 710 connection.rollback(); 711 } 712 713 public boolean isClosed() throws SQLException { 714 return connection.isClosed(); 715 } 716 717 public DatabaseMetaData getMetaData() throws SQLException { 718 return connection.getMetaData(); 719 } 720 721 public void setReadOnly(boolean readOnly) throws SQLException { 722 connection.setReadOnly(readOnly); 723 } 724 725 public boolean isReadOnly() throws SQLException { 726 return connection.isReadOnly(); 727 } 728 729 public void setCatalog(String catalog) throws SQLException { 730 connection.setCatalog(catalog); 731 } 732 733 public String getCatalog() throws SQLException { 734 return connection.getCatalog(); 735 } 736 737 public void setTransactionIsolation(int level) throws SQLException { 738 connection.setTransactionIsolation(level); 739 } 740 741 public int getTransactionIsolation() throws SQLException { 742 return connection.getTransactionIsolation(); 743 } 744 745 public SQLWarning getWarnings() throws SQLException { 746 return connection.getWarnings(); 747 } 748 749 public void clearWarnings() throws SQLException { 750 connection.clearWarnings(); 751 } 752 753 public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { 754 return connection.createStatement(resultSetType, resultSetConcurrency); 755 } 756 757 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) 758 throws SQLException { 759 return connection.prepareStatement(sql, resultSetType, resultSetConcurrency); 760 } 761 762 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) 763 throws SQLException { 764 return connection.prepareCall(sql, resultSetType, resultSetConcurrency); 765 } 766 767 public Map getTypeMap() throws SQLException { 768 return connection.getTypeMap(); 769 } 770 771 public void setTypeMap(Map map) throws SQLException { 772 connection.setTypeMap(map); 773 } 774 776 780 public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) 781 throws SQLException { 782 return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); 783 } 784 785 788 public int getHoldability() throws SQLException { 789 return connection.getHoldability(); 790 } 791 792 795 public CallableStatement prepareCall( 796 String sql, 797 int resultSetType, 798 int resultSetConcurrency, 799 int resultSetHoldability) 800 throws SQLException { 801 return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); 802 } 803 804 807 public PreparedStatement prepareStatement( 808 String sql, 809 int resultSetType, 810 int resultSetConcurrency, 811 int resultSetHoldability) 812 throws SQLException { 813 return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); 814 } 815 816 819 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { 820 return connection.prepareStatement(sql, autoGeneratedKeys); 821 } 822 823 826 public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { 827 return connection.prepareStatement(sql, columnIndexes); 828 } 829 830 833 public PreparedStatement prepareStatement(String sql, String [] columnNames) throws SQLException { 834 return connection.prepareStatement(sql, columnNames); 835 } 836 837 840 public void releaseSavepoint(Savepoint savepoint) throws SQLException { 841 connection.releaseSavepoint(savepoint); 842 } 843 844 847 public void rollback(Savepoint savepoint) throws SQLException { 848 connection.rollback(savepoint); 849 } 850 851 854 public void setHoldability(int holdability) throws SQLException { 855 connection.setHoldability(holdability); 856 } 857 858 861 public Savepoint setSavepoint() throws SQLException { 862 return connection.setSavepoint(); 863 } 864 865 868 public Savepoint setSavepoint(String name) throws SQLException { 869 return connection.setSavepoint(name); 870 } 871 872 } 873 } 874 | Popular Tags |