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.Iterator; 007 import java.util.Map; 008 009 import railo.commons.db.DBUtil; 010 import railo.commons.lang.StringUtil; 011 import railo.commons.lang.types.RefInteger; 012 import railo.commons.lang.types.RefIntegerImpl; 013 import railo.runtime.PageContext; 014 import railo.runtime.engine.ThreadLocalPageContext; 015 import railo.runtime.exp.DatabaseException; 016 import railo.runtime.exp.PageException; 017 import railo.runtime.op.Caster; 018 import railo.runtime.type.util.ArrayUtil; 019 020 public class DatasourceConnectionPool { 021 022 private Map<String,DCStack> dcs=new HashMap<String,DCStack>(); 023 private Map<String,RefInteger> counter=new HashMap<String,RefInteger>(); 024 025 public DatasourceConnection getDatasourceConnection(PageContext pc,DataSource datasource, String user, String pass) throws PageException { 026 pc=ThreadLocalPageContext.get(pc); 027 if(StringUtil.isEmpty(user)) { 028 user=datasource.getUsername(); 029 pass=datasource.getPassword(); 030 } 031 if(pass==null)pass=""; 032 033 034 // get stack 035 DCStack stack=getDCStack(datasource,user,pass); 036 037 038 // max connection 039 int max=datasource.getConnectionLimit(); 040 synchronized (stack) { 041 while(max!=-1 && max<=_size(datasource)) { 042 try { 043 //stack.inc(); 044 stack.wait(10000L); 045 046 } 047 catch (InterruptedException e) { 048 throw Caster.toPageException(e); 049 } 050 051 } 052 if(pc!=null){ 053 while(!stack.isEmpty()) { 054 DatasourceConnectionImpl dc=(DatasourceConnectionImpl) stack.get(pc); 055 if(dc!=null && isValid(dc,Boolean.TRUE)){ 056 _inc(datasource); 057 return dc.using(); 058 } 059 060 } 061 } 062 _inc(datasource); 063 return loadDatasourceConnection(datasource, user, pass).using(); 064 } 065 } 066 067 private DatasourceConnectionImpl loadDatasourceConnection(DataSource ds, String user, String pass) throws DatabaseException { 068 Connection conn=null; 069 String connStr = ds.getDsnTranslated(); 070 try { 071 conn = DBUtil.getConnection(connStr, user, pass); 072 conn.setAutoCommit(true); 073 } 074 catch (SQLException e) { 075 throw new DatabaseException("can't connect to datasource ["+ds.getName()+"]",e,null,null); 076 } 077 //print.err("create connection"); 078 return new DatasourceConnectionImpl(conn,ds,user,pass); 079 } 080 081 public void releaseDatasourceConnection(DatasourceConnection dc) { 082 if(dc==null) return; 083 084 DCStack stack=getDCStack(dc.getDatasource(), dc.getUsername(), dc.getPassword()); 085 synchronized (stack) { 086 stack.add(dc); 087 int max = dc.getDatasource().getConnectionLimit(); 088 089 if(max!=-1) { 090 _dec(dc.getDatasource()); 091 stack.notify(); 092 093 } 094 else _dec(dc.getDatasource()); 095 } 096 } 097 098 public void clear() { 099 //int size=0; 100 101 // remove all timed out conns 102 try{ 103 Object[] arr = dcs.entrySet().toArray(); 104 if(ArrayUtil.isEmpty(arr)) return; 105 for(int i=0;i<arr.length;i++) { 106 DCStack conns=(DCStack) ((Map.Entry) arr[i]).getValue(); 107 if(conns!=null)conns.clear(); 108 //size+=conns.size(); 109 } 110 } 111 catch(Throwable t){} 112 } 113 114 public void remove(DataSource datasource) { 115 Object[] arr = dcs.keySet().toArray(); 116 String key,id=createId(datasource); 117 for(int i=0;i<arr.length;i++) { 118 key=(String) arr[i]; 119 if(key.startsWith(id)) { 120 DCStack conns=dcs.get(key); 121 conns.clear(); 122 } 123 } 124 125 RefInteger ri=counter.get(id); 126 if(ri!=null)ri.setValue(0); 127 else counter.put(id,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(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=dcs.get(id); 159 160 if(stack==null){ 161 dcs.put(id, stack=new DCStack()); 162 } 163 return stack; 164 } 165 166 public int openConnections() { 167 Iterator<DCStack> it = dcs.values().iterator(); 168 int count=0; 169 while(it.hasNext()){ 170 count+=it.next().openConnections(); 171 } 172 return count; 173 } 174 175 private void _inc(DataSource datasource) { 176 _getCounter(datasource).plus(1); 177 } 178 private void _dec(DataSource datasource) { 179 _getCounter(datasource).minus(1); 180 } 181 private int _size(DataSource datasource) { 182 return _getCounter(datasource).toInt(); 183 } 184 185 private RefInteger _getCounter(DataSource datasource) { 186 String did = createId(datasource); 187 RefInteger ri=counter.get(did); 188 if(ri==null) { 189 counter.put(did,ri=new RefIntegerImpl(0)); 190 } 191 return ri; 192 } 193 194 public static String createId(DataSource datasource, String user, String pass) { 195 return createId(datasource)+":"+user+":"+pass; 196 } 197 private static String createId(DataSource datasource) { 198 if(datasource instanceof DataSourcePro) return ((DataSourceSupport)datasource).id(); 199 return datasource.getClazz().getName()+":"+datasource.getDsnTranslated()+":"+datasource.getClazz().getName(); 200 } 201 }