001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.db; 020 021import java.sql.Connection; 022import java.sql.SQLException; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.Map; 026import java.util.concurrent.ConcurrentHashMap; 027 028import lucee.commons.db.DBUtil; 029import lucee.commons.io.IOUtil; 030import lucee.commons.lang.ExceptionUtil; 031import lucee.commons.lang.StringUtil; 032import lucee.commons.lang.types.RefInteger; 033import lucee.commons.lang.types.RefIntegerImpl; 034import lucee.commons.lang.types.RefIntegerSync; 035import lucee.runtime.config.Config; 036import lucee.runtime.exp.DatabaseException; 037import lucee.runtime.exp.PageException; 038import lucee.runtime.op.Caster; 039import lucee.runtime.type.util.ArrayUtil; 040 041public class DatasourceConnectionPool { 042 043 private ConcurrentHashMap<String,DCStack> dcs=new ConcurrentHashMap<String,DCStack>(); 044 private Map<String,RefInteger> counter=new ConcurrentHashMap<String,RefInteger>(); 045 046 public DatasourceConnection getDatasourceConnection(DataSource datasource, String user, String pass) throws PageException { 047 // pc=ThreadLocalPageContext.get(pc); 048 if(StringUtil.isEmpty(user)) { 049 user=datasource.getUsername(); 050 pass=datasource.getPassword(); 051 } 052 if(pass==null)pass=""; 053 054 // get stack 055 DCStack stack=getDCStack(datasource,user,pass); 056 057 058 // max connection 059 int max=datasource.getConnectionLimit(); 060 061 // get an existing connection 062 DatasourceConnectionImpl rtn=null; 063 do { 064 // we have a bad connection 065 if(rtn!=null) { 066 IOUtil.closeEL(rtn.getConnection()); 067 } 068 synchronized (stack) { 069 while(max!=-1 && max<=_size(datasource)) { 070 try { 071 //stack.inc(); 072 stack.wait(10000L); 073 074 } 075 catch (InterruptedException e) { 076 throw Caster.toPageException(e); 077 } 078 } 079 080 while(!stack.isEmpty()) { 081 DatasourceConnectionImpl dc=(DatasourceConnectionImpl) stack.get(); 082 if(dc!=null){ 083 rtn=dc; 084 break; 085 } 086 } 087 } 088 }while(rtn!=null && !isValid(rtn,Boolean.TRUE)); 089 090 // create a new connection 091 if(rtn==null) 092 rtn=loadDatasourceConnection(datasource, user, pass); 093 094 synchronized (stack) { 095 _inc(datasource); 096 } 097 return rtn.using(); 098 } 099 100 private DatasourceConnectionImpl loadDatasourceConnection(DataSource ds, String user, String pass) throws DatabaseException { 101 Connection conn=null; 102 String connStr = ds.getDsnTranslated(); 103 try { 104 conn = DBUtil.getConnection(connStr, user, pass); 105 conn.setAutoCommit(true); 106 } 107 catch (SQLException e) { 108 throw new DatabaseException(e,null); 109 } 110 //print.err("create connection"); 111 return new DatasourceConnectionImpl(conn,ds,user,pass); 112 } 113 114 public void releaseDatasourceConnection(Config config,DatasourceConnection dc, boolean async) { 115 releaseDatasourceConnection(dc,false); 116 //if(async)((SpoolerEngineImpl)config.getSpoolerEngine()).add((DatasourceConnectionImpl)dc); 117 //else releaseDatasourceConnection(dc); 118 } 119 public void releaseDatasourceConnection(Config config,DatasourceConnection dc, boolean async, boolean closeIt) { 120 releaseDatasourceConnection(dc,closeIt); 121 //if(async)((SpoolerEngineImpl)config.getSpoolerEngine()).add((DatasourceConnectionImpl)dc); 122 //else releaseDatasourceConnection(dc); 123 } 124 125 public void releaseDatasourceConnection(DatasourceConnection dc) { 126 releaseDatasourceConnection(dc, false); 127 } 128 129 public void releaseDatasourceConnection(DatasourceConnection dc, boolean closeIt) { 130 if(dc==null) return; 131 DCStack stack=getDCStack(dc.getDatasource(), dc.getUsername(), dc.getPassword()); 132 synchronized (stack) { 133 if(closeIt) IOUtil.closeEL(dc.getConnection()); 134 else stack.add(dc); 135 int max = dc.getDatasource().getConnectionLimit(); 136 137 if(max!=-1) { 138 _dec(dc.getDatasource()); 139 stack.notify(); 140 141 } 142 else _dec(dc.getDatasource()); 143 } 144 } 145 146 public void clear() { 147 //int size=0; 148 149 // remove all timed out conns 150 try{ 151 Object[] arr = dcs.entrySet().toArray(); 152 if(ArrayUtil.isEmpty(arr)) return; 153 for(int i=0;i<arr.length;i++) { 154 DCStack conns=(DCStack) ((Map.Entry) arr[i]).getValue(); 155 if(conns!=null)conns.clear(); 156 //size+=conns.size(); 157 } 158 } 159 catch(Throwable t){ 160 ExceptionUtil.rethrowIfNecessary(t); 161 } 162 } 163 164 public void remove(DataSource datasource) { 165 Object[] arr = dcs.keySet().toArray(); 166 String key,id=createId(datasource); 167 for(int i=0;i<arr.length;i++) { 168 key=(String) arr[i]; 169 if(key.startsWith(id)) { 170 DCStack conns=dcs.get(key); 171 conns.clear(); 172 } 173 } 174 175 RefInteger ri=counter.get(id); 176 if(ri!=null)ri.setValue(0); 177 else counter.put(id,new RefIntegerSync(0)); 178 179 } 180 181 182 183 public static boolean isValid(DatasourceConnection dc,Boolean autoCommit) { 184 try { 185 if(dc.getConnection().isClosed())return false; 186 } 187 catch (Throwable t) { 188 ExceptionUtil.rethrowIfNecessary(t); 189 return false; 190 } 191 192 try { 193 if(dc.getDatasource().validate() && !DataSourceUtil.isValid(dc,10))return false; 194 } 195 catch (Throwable t) { 196 ExceptionUtil.rethrowIfNecessary(t); 197 } // not all driver support this, because of that we ignore a error here, also protect from java 5 198 199 200 try { 201 if(autoCommit!=null) dc.getConnection().setAutoCommit(autoCommit.booleanValue()); 202 } 203 catch (Throwable t) { 204 ExceptionUtil.rethrowIfNecessary(t); 205 return false; 206 } 207 208 209 return true; 210 } 211 212 213 private DCStack getDCStack(DataSource datasource, String user, String pass) { 214 String id = createId(datasource,user,pass); 215 synchronized(id) { 216 DCStack stack=dcs.get(id); 217 218 if(stack==null){ 219 dcs.put(id, stack=new DCStack()); 220 } 221 return stack; 222 } 223 } 224 225 public int openConnections() { 226 Iterator<DCStack> it = dcs.values().iterator(); 227 int count=0; 228 while(it.hasNext()){ 229 count+=it.next().openConnections(); 230 } 231 return count; 232 } 233 234 private void _inc(DataSource datasource) { 235 _getCounter(datasource).plus(1); 236 } 237 private void _dec(DataSource datasource) { 238 _getCounter(datasource).minus(1); 239 } 240 private int _size(DataSource datasource) { 241 return _getCounter(datasource).toInt(); 242 } 243 244 private RefInteger _getCounter(DataSource datasource) { 245 String did = createId(datasource); 246 RefInteger ri=counter.get(did); 247 if(ri==null) { 248 counter.put(did,ri=new RefIntegerSync(0)); 249 } 250 return ri; 251 } 252 253 public static String createId(DataSource datasource, String user, String pass) { 254 return createId(datasource)+":"+user+":"+pass; 255 } 256 private static String createId(DataSource datasource) { 257 if(datasource instanceof DataSourcePro) return ((DataSourceSupport)datasource).id(); 258 return datasource.getClazz().getName()+":"+datasource.getDsnTranslated()+":"+datasource.getClazz().getName(); 259 } 260}