1 32 33 package com.knowgate.jdc; 34 35 import java.util.HashMap ; 36 import java.util.Iterator ; 37 import java.util.LinkedList ; 38 import java.util.ListIterator ; 39 import java.util.Set ; 40 import java.util.Vector ; 41 import java.util.Enumeration ; 42 import java.util.Date ; 43 import java.util.ConcurrentModificationException ; 44 45 import java.sql.DriverManager ; 46 import java.sql.Connection ; 47 import java.sql.CallableStatement ; 48 import java.sql.PreparedStatement ; 49 import java.sql.SQLException ; 50 import java.sql.Timestamp ; 51 import java.sql.Types .*; 52 53 import com.knowgate.debug.DebugFile; 54 55 60 61 final class ConnectionReaper extends Thread { 62 63 66 private JDCConnectionPool pool; 67 68 71 private boolean keepruning; 72 73 76 private long delay=300000l; 77 78 82 ConnectionReaper(JDCConnectionPool forpool) { 83 pool = forpool; 84 keepruning = true; 85 try { 86 checkAccess(); 87 setDaemon(true); 88 setPriority(MIN_PRIORITY); 89 } catch (SecurityException ignore) { } 90 } 91 92 97 public long getDelay() { 98 return delay; 99 } 100 101 108 public void setDelay(long lDelay) throws IllegalArgumentException { 109 if (lDelay<1000l) 110 throw new IllegalArgumentException ("ConnectionReaper delay cannot be smaller than 1000 miliseconds"); 111 delay=lDelay; 112 } 113 114 public void halt() { 115 keepruning=false; 116 } 117 118 121 public void run() { 122 while (keepruning) { 123 try { 124 sleep(delay); 125 } catch( InterruptedException e) { } 126 pool.reapConnections(); 127 } } } 131 133 138 139 public final class JDCConnectionPool { 140 141 private Object binding; 142 private Vector connections; 143 private int openconns; 144 private HashMap callers; 145 private String url, user, password; 146 private ConnectionReaper reaper; 147 private LinkedList errorlog; 148 149 153 private long timeout=600000l; 154 155 158 private int poolsize = 32; 159 160 163 private int hardlimit = 100; 164 165 167 177 178 public JDCConnectionPool(String url, String user, String password) { 179 180 binding = null; 181 182 if (null==url) 183 throw new IllegalArgumentException ("JDCConnectionPool : url cannot be null"); 184 185 if (url.length()==0) 186 throw new IllegalArgumentException ("JDCConnectionPool : url value not set"); 187 188 this.url = url; 189 this.user = user; 190 this.password = password; 191 this.openconns = 0; 192 193 DriverManager.setLoginTimeout(20); 195 connections = new Vector (poolsize<=hardlimit ? poolsize : hardlimit); 196 reaper = new ConnectionReaper(this); 197 reaper.start(); 198 199 if (DebugFile.trace) callers = new HashMap (1023); 200 201 errorlog = new LinkedList (); 202 } 203 204 206 219 220 public JDCConnectionPool(Object bind, String url, String user, String password, 221 int maxpoolsize, int maxconnections, 222 int logintimeout, long connectiontimeout) { 223 224 binding = bind; 225 226 if (null==url) 227 throw new IllegalArgumentException ("JDCConnectionPool : url cannot be null"); 228 229 if (url.length()==0) 230 throw new IllegalArgumentException ("JDCConnectionPool : url value not set"); 231 232 if (maxpoolsize<1) 233 throw new IllegalArgumentException ("maxpoolsize must be greater than or equal to 1"); 234 235 if (maxconnections<1) 236 throw new IllegalArgumentException ("maxpoolsize must be greater than or equal to 1"); 237 238 if (maxpoolsize>maxconnections) 239 throw new IllegalArgumentException ("maxpoolsize must be less than or equal to maxconnections"); 240 241 this.url = url; 242 this.user = user; 243 this.password = password; 244 this.openconns = 0; 245 this.poolsize = maxpoolsize; 246 this.hardlimit = maxconnections; 247 this.timeout = connectiontimeout; 248 249 DriverManager.setLoginTimeout(logintimeout); 250 251 connections = new Vector (poolsize<=hardlimit ? poolsize : hardlimit); 252 reaper = new ConnectionReaper(this); 253 reaper.start(); 254 255 if (DebugFile.trace) callers = new HashMap (1023); 256 257 errorlog = new LinkedList (); 258 } 259 260 272 273 public JDCConnectionPool(Object bind, String url, String user, String password, 274 int maxpoolsize, int maxconnections, 275 int logintimeout) { 276 this(bind,url,user,password,maxpoolsize,maxconnections,logintimeout,60000l); 277 } 278 279 281 291 public JDCConnectionPool(Object bind, String url, String user, String password, 292 int maxpoolsize, int maxconnections) { 293 this(bind,url,user,password,maxpoolsize,maxconnections,20); 294 } 295 296 298 308 309 public JDCConnectionPool(String url, String user, String password, 310 int maxpoolsize, int maxconnections, 311 int logintimeout) { 312 313 binding = null; 314 315 if (null==url) 316 throw new IllegalArgumentException ("JDCConnectionPool : url cannot be null"); 317 318 if (url.length()==0) 319 throw new IllegalArgumentException ("JDCConnectionPool : url value not set"); 320 321 if (maxpoolsize<1) 322 throw new IllegalArgumentException ("maxpoolsize must be greater than or equal to 1"); 323 324 if (maxconnections<1) 325 throw new IllegalArgumentException ("maxpoolsize must be greater than or equal to 1"); 326 327 if (maxpoolsize>maxconnections) 328 throw new IllegalArgumentException ("maxpoolsize must be less than or equal to maxconnections"); 329 330 this.url = url; 331 this.user = user; 332 this.password = password; 333 this.openconns = 0; 334 this.poolsize = maxpoolsize; 335 this.hardlimit = maxconnections; 336 337 DriverManager.setLoginTimeout(logintimeout); 338 339 connections = new Vector (poolsize); 340 reaper = new ConnectionReaper(this); 341 reaper.start(); 342 343 if (DebugFile.trace) callers = new HashMap (1023); 344 345 errorlog = new LinkedList (); 346 } 347 348 350 359 360 362 public JDCConnectionPool(String url, String user, String password, int maxpoolsize, int maxconnections) { 363 this(url,user,password,maxpoolsize,maxconnections,20); 364 } 365 366 368 private synchronized void modifyMap (String sCaller, int iAction) 369 throws NullPointerException { 370 371 if (null==callers) callers = new HashMap (1023); 372 373 if (callers.containsKey(sCaller)) { 374 Integer iRefCount = new Integer (((Integer ) callers.get(sCaller)).intValue()+iAction); 375 callers.remove(sCaller); 376 callers.put(sCaller, iRefCount); 377 DebugFile.writeln(" " + sCaller + " reference count is " + iRefCount.toString()); 378 } 379 else { 380 if (1==iAction) { 381 callers.put(sCaller, new Integer (1)); 382 DebugFile.writeln(" " + sCaller + " reference count is 1"); 383 } 384 else { 385 DebugFile.writeln(" ERROR: JDCConnectionPool get/close connection mismatch for " + sCaller); 386 } 387 } 388 } 390 392 395 public void close() { 396 if (DebugFile.trace) { 397 DebugFile.writeln("Begin ConnectionPool.close()"); 398 DebugFile.incIdent(); 399 } 400 401 reaper.halt(); 402 403 closeConnections(); 404 405 if (DebugFile.trace) { 406 DebugFile.decIdent(); 407 DebugFile.writeln("End ConnectionPool.close()"); 408 } 409 } 411 413 419 public int getPoolSize() { 420 return poolsize; 421 } 422 423 425 434 435 public void setPoolSize(int iPoolSize) { 436 437 if (iPoolSize>hardlimit) 438 throw new IllegalArgumentException ("prefered pool size must be less than or equal to max pool size "); 439 440 reapConnections(); 441 poolsize = iPoolSize; 442 } 443 444 446 453 public void setMaxPoolSize(int iMaxConnections) { 454 455 if (iMaxConnections==0) { 456 reapConnections(); 457 poolsize = hardlimit = 0; 458 } else { 459 if (iMaxConnections<poolsize) 460 throw new IllegalArgumentException ("max pool size must be greater than or equal to prefered pool size "); 461 462 reapConnections(); 463 hardlimit = iMaxConnections; 464 } 465 } 466 467 469 473 public int getMaxPoolSize() { 474 return hardlimit; 475 } 476 477 486 public long getTimeout() { 487 return timeout; 488 } 489 490 492 499 500 public void setTimeout(long miliseconds) 501 throws IllegalArgumentException { 502 503 if (miliseconds<1000l) 504 throw new IllegalArgumentException ("Connection timeout must be at least 1000 miliseconds"); 505 506 timeout = miliseconds; 507 } 508 509 514 public long getReaperDaemonDelay() { 515 return reaper.getDelay(); 516 } 517 518 524 public void setReaperDaemonDelay(long lDelayMs) throws IllegalArgumentException { 525 reaper.setDelay(lDelayMs); 526 } 527 528 530 534 535 private synchronized void removeConnection(JDCConnection conn) { 536 boolean bClosed; 537 String sCaller = ""; 538 539 try { 540 if (DebugFile.trace) logConnection (conn, "removeConnection", "RDBC", null); 541 542 sCaller = conn.getName(); 543 if (!conn.isClosed()) conn.getConnection().close(); 544 conn.expireLease(); 545 if (DebugFile.trace && (null!=sCaller)) modifyMap(sCaller,-1); 546 bClosed = true; 547 } 548 catch (SQLException e) { 549 bClosed = false; 550 551 if (errorlog.size()>100) errorlog.removeFirst(); 552 errorlog.addLast(new Date ().toString() + " " + sCaller + " Connection.close() " + e.getMessage()); 553 554 if (DebugFile.trace) DebugFile.writeln("SQLException at JDCConnectionPool.removeConnection() : " + e.getMessage()); 555 } 556 557 if (bClosed) { 558 if (DebugFile.trace) DebugFile.writeln("connections.removeElement(" + String.valueOf(openconns) + ")"); 559 connections.removeElement(conn); 560 openconns--; 561 } 562 } 564 566 569 570 synchronized void reapConnections() { 571 572 if (DebugFile.trace) { 573 DebugFile.writeln("Begin JDCConnectionPool.reapConnections()"); 574 DebugFile.incIdent(); 575 } 576 577 long stale = System.currentTimeMillis() - timeout; 578 Enumeration connlist = connections.elements(); 579 JDCConnection conn; 580 581 while((connlist != null) && (connlist.hasMoreElements())) { 582 conn = (JDCConnection) connlist.nextElement(); 583 584 if (!conn.inUse()) 587 removeConnection(conn); 588 else if (stale>conn.getLastUse()) { 589 if (DebugFile.trace) DebugFile.writeln("Connection "+conn.getName()+" was staled since "+new Date (conn.getLastUse()).toString()); 590 if (errorlog.size()>100) errorlog.removeFirst(); 591 errorlog.addLast(new Date ().toString()+" Connection "+conn.getName()+" was staled since "+new Date (conn.getLastUse()).toString()); 592 removeConnection(conn); 593 } 594 } 596 if (DebugFile.trace) { 597 DebugFile.decIdent(); 598 DebugFile.writeln("End JDCConnectionPool.reapConnections() : " + new Date ().toString()); 599 } 600 } 602 604 607 608 public void closeConnections() { 609 610 Enumeration connlist; 611 612 if (DebugFile.trace) { 613 DebugFile.writeln("Begin JDCConnectionPool.closeConnections()"); 614 DebugFile.incIdent(); 615 } 616 617 connlist = connections.elements(); 618 619 if (connlist != null) { 620 while (connlist.hasMoreElements()) { 621 removeConnection ((JDCConnection) connlist.nextElement()); 622 } } 625 if (DebugFile.trace) callers.clear(); 626 627 connections.clear(); 628 629 if (DebugFile.trace) { 630 DebugFile.decIdent(); 631 DebugFile.writeln("End JDCConnectionPool.closeConnections() : " + String.valueOf(openconns)); 632 } 633 634 openconns = 0; 635 } 637 639 643 644 public int closeStaledConnections() { 645 646 JDCConnection conn; 647 Enumeration connlist; 648 649 if (DebugFile.trace) { 650 DebugFile.writeln("Begin JDCConnectionPool.closeStaledConnections()"); 651 DebugFile.incIdent(); 652 } 653 654 int staled = 0; 655 final long stale = System.currentTimeMillis() - timeout; 656 657 connlist = connections.elements(); 658 659 if (connlist != null) { 660 while (connlist.hasMoreElements()) { 661 conn = (JDCConnection) connlist.nextElement(); 662 if (stale>conn.getLastUse()) { 663 staled++; 664 removeConnection (conn); 665 } 666 } } 669 if (DebugFile.trace) { 670 DebugFile.decIdent(); 671 DebugFile.writeln("End JDCConnectionPool.closeStaledConnections() : " + String.valueOf(staled)); 672 } 673 674 return staled; 675 } 677 679 private static void logConnection(JDCConnection conn, String sName, String cOpCode, String sParams) { 680 681 PreparedStatement oStmt = null; 682 JDCConnection oLogConn = null; 683 684 if (DebugFile.trace) { 685 com.knowgate.dataobjs.DBAudit.log(JDCConnection.IdClass, cOpCode, "", sName, "", 0, "", sParams, ""); 686 } 687 688 } 690 692 695 public synchronized JDCConnection[] getAllConnections() { 696 int iConnections = connections.size(); 697 JDCConnection[] aConnections = new JDCConnection[iConnections]; 698 699 for (int c=0; c<iConnections; c++) 700 aConnections[c] = (JDCConnection) connections.get(c); 701 702 return aConnections; 703 } 705 707 711 public Object getDatabaseBinding() { 712 return binding; 713 } 714 715 717 727 728 public synchronized JDCConnection getConnection(String sCaller) throws SQLException { 729 730 int i, s; 731 JDCConnection j; 732 Connection c; 733 734 if (DebugFile.trace) { 735 DebugFile.writeln("Begin JDCConnectionPool.getConnection(" + (sCaller!=null ? sCaller : "") + ")"); 736 DebugFile.incIdent(); 737 } 738 739 if (hardlimit==0) { 740 743 c = DriverManager.getConnection(url, user, password); 744 j = new JDCConnection(c,null); 745 } else { 746 747 j = null; 748 749 s = connections.size(); 750 for (i = 0; i < s; i++) { 751 j = (JDCConnection) connections.elementAt(i); 752 if (j.lease(sCaller)) { 753 if (DebugFile.trace) { 754 DebugFile.writeln(" JDCConnectionPool hit for (" + url + ", ...) on pooled connection #" + String.valueOf(i)); 755 if (sCaller!=null) logConnection (j, sCaller, "ODBC", "hit"); 756 } 757 break; 758 } 759 else 760 j = null; 761 } 763 if (null==j) { 764 if (openconns==hardlimit) { 765 if (DebugFile.trace) DebugFile.decIdent(); 766 throw new SQLException ("Maximum number of " + String.valueOf(hardlimit) + " concurrent connections exceeded","08004"); 767 } 768 769 if (DebugFile.trace) DebugFile.writeln(" DriverManager.getConnection(" + url + ", ...)"); 770 771 c = DriverManager.getConnection(url, user, password); 772 773 if (null!=c) { 774 j = new JDCConnection(c, this); 775 j.lease(sCaller); 776 777 if (DebugFile.trace) { 778 DebugFile.writeln(" JDCConnectionPool miss for (" + url + ", ...)"); 779 if (sCaller!=null) logConnection (j, sCaller, "ODBC", "miss"); 780 } 781 782 connections.addElement(j); 783 c = null; 784 } 785 else { 786 if (DebugFile.trace) DebugFile.writeln("JDCConnectionPool.getConnection() DriverManager.getConnection() returned null value"); 787 j = null; 788 } 789 790 if (null!=j) openconns++; 791 } 793 if (DebugFile.trace ) { 794 if (sCaller!=null) modifyMap(sCaller, 1); 795 } } 798 if (DebugFile.trace ) { 799 DebugFile.decIdent(); 800 DebugFile.writeln("End JDCConnectionPool.getConnection()"); 801 } 803 return j; 804 } 806 808 815 public JDCConnection getConnectionForPId(String sPId) throws SQLException { 816 String pid; 817 JDCConnection conn; 818 Enumeration connlist = connections.elements(); 819 if (connlist != null) { 820 while(connlist.hasMoreElements()) { 821 conn = (JDCConnection) connlist.nextElement(); 822 try { 823 pid = conn.pid(); 824 } catch (Exception ignore) { pid=null; } 825 if (sPId.equals(pid)) 826 return conn; 827 } } return null; 830 } 832 834 838 839 public synchronized void returnConnection(JDCConnection conn) { 840 if (DebugFile.trace) DebugFile.writeln("JDCConnectionPool.returnConnection([JDCConnection])"); 841 conn.expireLease(); 842 } 844 846 851 852 public synchronized void returnConnection(JDCConnection conn, String sCaller) { 853 854 if (DebugFile.trace) { 855 DebugFile.writeln("JDCConnectionPool.returnConnection([JDCConnection], "+sCaller+")"); 856 if (sCaller!=null) 857 logConnection (conn, sCaller, "CDBC", null); 858 } 859 860 if (DebugFile.trace) DebugFile.writeln("JDCConnection.expireLease()"); 861 862 conn.expireLease(); 863 864 if (DebugFile.trace && (null!=sCaller)) modifyMap(sCaller, -1); 865 } 866 867 869 872 873 public synchronized int size() { 874 return openconns; 875 } 876 877 883 public JDCActivityInfo getActivityInfo() throws SQLException { 884 JDCActivityInfo oInfo; 885 try { oInfo = new JDCActivityInfo(this); 886 } catch (Exception xcpt) { 887 throw new SQLException (xcpt.getMessage()); 888 } 889 return oInfo; 890 } 891 892 894 900 public String dumpStatistics() 901 throws ConcurrentModificationException { 902 String sDump; 903 String sPId; 904 Object sKey; 905 Object iVal; 906 int iConnOrdinal, iStaled; 907 long stale = System.currentTimeMillis() - timeout; 908 909 if (DebugFile.trace) { 910 DebugFile.writeln("Begin JDCConnectionPool.dumpStatistics()"); 911 DebugFile.incIdent(); 912 } 913 914 Enumeration connlist = connections.elements(); 915 JDCConnection conn; 916 917 sDump = "Maximum Pool Size=" + String.valueOf(poolsize) + "\n"; 918 sDump += "Maximum Connections=" + String.valueOf(hardlimit) + "\n"; 919 sDump += "Connection Timeout=" + String.valueOf(timeout) + " ms\n"; 920 sDump += "Reaper Daemon Delay=" + String.valueOf(getReaperDaemonDelay()) + " ms\n"; 921 sDump += "\n"; 922 923 iStaled = iConnOrdinal = 0; 924 925 if (connlist != null) { 926 while (connlist.hasMoreElements()) { 927 conn = (JDCConnection) connlist.nextElement(); 928 929 if (stale>conn.getLastUse()) iStaled++; 930 931 try { 932 sPId = conn.pid(); 933 } catch (Exception ignore) { sPId=null; } 934 935 sDump += "#" + String.valueOf(++iConnOrdinal) + (conn.inUse() ? " in use, " : " vacant, ") + (stale>conn.getLastUse() ? " staled," : " ready,") + (conn.validate() ? "validate=yes" : " validate=no") + ", last use=" + new Date (conn.getLastUse()).toString() + ", caller=" + conn.getName() + (sPId==null ? "" : " pid="+sPId) + "\n"; 936 } 937 } 939 sDump += "\n"; 940 941 if (DebugFile.trace) { 942 Iterator oCallersIterator = callers.keySet().iterator(); 943 944 while (oCallersIterator.hasNext()) { 945 sKey = oCallersIterator.next(); 946 iVal = callers.get(sKey); 947 sDump += sKey + " , " + iVal.toString() + " named open connections\n"; 948 } 949 sDump += "\n\n"; 950 } 952 sDump += String.valueOf(iStaled) + " staled connections\n"; 953 954 sDump += "Actual pool size " + String.valueOf(size()) + "\n\n"; 955 956 try { 957 JDCProcessInfo[] oPinfo = getActivityInfo().processesInfo(); 958 if (oPinfo!=null) { 959 sDump += "Activity information:\n"; 960 for (int p=0; p<oPinfo.length; p++) { 961 sDump += "user "+oPinfo[p].getUserName()+" running process "+oPinfo[p].getProcessId(); 962 conn = getConnectionForPId(oPinfo[p].getProcessId()); 963 if (conn!=null) { 964 sDump += " on connection "+conn.getName(); 965 } 966 if (oPinfo[p].getQueryText().length()>0) { 967 sDump += " for query "+oPinfo[p].getQueryText(); 968 } 969 if (oPinfo[p].getQueryStart()!=null) { 970 sDump += " since "+oPinfo[p].getQueryStart().toString(); 971 } 972 sDump += "\n"; 973 } } 975 } catch (Exception xcpt) { 976 sDump += xcpt.getClass().getName()+" trying to get activity information "+xcpt.getMessage()+"\n"; 977 } 978 979 sDump += "\n"; 980 981 if (errorlog.size()>0) { 982 sDump += "Fatal error log:\n"; 983 ListIterator oErrIterator = errorlog.listIterator(); 984 while (oErrIterator.hasNext()) sDump += oErrIterator.next()+"\n"; 985 } 987 if (DebugFile.trace) { 988 DebugFile.decIdent(); 989 DebugFile.writeln("End JDCConnectionPool.dumpStatistics()"); 990 } 991 992 return sDump; 993 } 995 } | Popular Tags |