001 package railo.runtime.db; 002 003 import java.sql.Connection; 004 import java.sql.SQLException; 005 import java.util.HashMap; 006 import java.util.Map; 007 008 import railo.commons.db.DBUtil; 009 import railo.commons.lang.StringUtil; 010 import railo.commons.lang.types.RefInteger; 011 import railo.commons.lang.types.RefIntegerImpl; 012 import railo.runtime.PageContext; 013 import railo.runtime.engine.ThreadLocalPageContext; 014 import railo.runtime.exp.DatabaseException; 015 import railo.runtime.exp.PageException; 016 import railo.runtime.op.Caster; 017 import railo.runtime.type.util.ArrayUtil; 018 019 public class DatasourceConnectionPool { 020 021 private Map<String,DCStack> dcs=new HashMap<String,DCStack>(); 022 private Map<String,RefInteger> counter=new HashMap<String,RefInteger>(); 023 024 public DatasourceConnection getDatasourceConnection(PageContext pc,DataSource datasource, String user, String pass) throws PageException { 025 pc=ThreadLocalPageContext.get(pc); 026 if(StringUtil.isEmpty(user)) { 027 user=datasource.getUsername(); 028 pass=datasource.getPassword(); 029 } 030 if(pass==null)pass=""; 031 032 033 // get stack 034 DCStack stack=getDCStack(datasource,user,pass); 035 036 037 // max connection 038 int max=datasource.getConnectionLimit(); 039 synchronized (stack) { 040 while(max!=-1 && max<=_size(datasource)) { 041 try { 042 //stack.inc(); 043 stack.wait(10000L); 044 045 } 046 catch (InterruptedException e) { 047 throw Caster.toPageException(e); 048 } 049 050 } 051 if(pc!=null){ 052 while(!stack.isEmpty()) { 053 DatasourceConnectionImpl dc=(DatasourceConnectionImpl) stack.get(pc); 054 if(dc!=null && isValid(dc,Boolean.TRUE)){ 055 _inc(datasource); 056 return dc.using(); 057 } 058 059 } 060 } 061 _inc(datasource); 062 return loadDatasourceConnection(datasource, user, pass).using(); 063 } 064 } 065 066 private DatasourceConnectionImpl loadDatasourceConnection(DataSource ds, String user, String pass) throws DatabaseException { 067 Connection conn=null; 068 String dsn = ds.getDsnTranslated(); 069 try { 070 conn = DBUtil.getConnection(dsn, user, pass); 071 conn.setAutoCommit(true); 072 } 073 catch (SQLException e) { 074 throw new DatabaseException("can't connect to datasource ["+ds.getName()+"]",e,null,null); 075 } 076 //print.err("create connection"); 077 return new DatasourceConnectionImpl(conn,ds,user,pass); 078 } 079 080 public void releaseDatasourceConnection(DatasourceConnection dc) { 081 if(dc==null) return; 082 083 DCStack stack=getDCStack(dc.getDatasource(), dc.getUsername(), dc.getPassword()); 084 synchronized (stack) { 085 stack.add((DatasourceConnectionPro)dc); 086 int max = dc.getDatasource().getConnectionLimit(); 087 088 if(max!=-1) { 089 _dec(dc.getDatasource()); 090 stack.notify(); 091 092 } 093 else _dec(dc.getDatasource()); 094 } 095 } 096 097 public void clear() { 098 int size=0; 099 100 // remove all timed out conns 101 try{ 102 Object[] arr = dcs.entrySet().toArray(); 103 if(ArrayUtil.isEmpty(arr)) return; 104 for(int i=0;i<arr.length;i++) { 105 DCStack conns=(DCStack) ((Map.Entry) arr[i]).getValue(); 106 if(conns!=null)conns.clear(); 107 size+=conns.size(); 108 } 109 } 110 catch(Throwable t){} 111 } 112 113 public void remove(String datasource) { 114 Object[] arr = dcs.keySet().toArray(); 115 String key; 116 for(int i=0;i<arr.length;i++) { 117 key=(String) arr[i]; 118 if(key.startsWith(datasource.toLowerCase())) { 119 DCStack conns=(DCStack) dcs.get(key); 120 conns.clear(); 121 } 122 } 123 124 String did = createId(datasource); 125 RefInteger ri=(RefInteger) counter.get(did); 126 if(ri!=null)ri.setValue(0); 127 else counter.put(did,new RefIntegerImpl(0)); 128 129 } 130 131 132 133 public static boolean isValid(DatasourceConnection dc,Boolean autoCommit) { 134 try { 135 if(dc.getConnection().isClosed())return false; 136 } 137 catch (Throwable t) {return false;} 138 139 try { 140 if(((DataSourceImpl)dc.getDatasource()).validate() && !DataSourceUtil.isValid(dc,1000))return false; 141 } 142 catch (Throwable t) {} // not all driver support this, because of that we ignore a error here, also protect from java 5 143 144 145 try { 146 if(autoCommit!=null) dc.getConnection().setAutoCommit(autoCommit.booleanValue()); 147 } 148 catch (Throwable t) {return false;} 149 150 151 return true; 152 } 153 154 155 private DCStack getDCStack(DataSource datasource, String user, String pass) { 156 String id = createId(datasource,user,pass); 157 158 DCStack stack=(DCStack)dcs.get(id); 159 160 if(stack==null){ 161 dcs.put(id, stack=new DCStack()); 162 } 163 return stack; 164 } 165 166 private void _inc(DataSource datasource) { 167 _getCounter(datasource.getName()).plus(1); 168 } 169 private void _dec(DataSource datasource) { 170 _getCounter(datasource.getName()).minus(1); 171 } 172 private int _size(DataSource datasource) { 173 return _getCounter(datasource.getName()).toInt(); 174 } 175 176 private RefInteger _getCounter(String datasource) { 177 String did = createId(datasource); 178 RefInteger ri=(RefInteger) counter.get(did); 179 if(ri==null) { 180 counter.put(did,ri=new RefIntegerImpl(0)); 181 } 182 return ri; 183 } 184 185 public static String createId(DataSource datasource, String user, String pass) { 186 return datasource.getName().toLowerCase()+user+pass; 187 } 188 private static String createId(String datasource) { 189 return datasource.toLowerCase(); 190 } 191 }