package railo.runtime.db;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import railo.commons.db.DBUtil;
import railo.commons.lang.StringUtil;
import railo.commons.lang.types.RefInteger;
import railo.commons.lang.types.RefIntegerImpl;
import railo.runtime.PageContext;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.DatabaseException;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;
import railo.runtime.type.util.ArrayUtil;
public class DatasourceConnectionPool {
private Map<String,DCStack> dcs=new HashMap<String,DCStack>();
private Map<String,RefInteger> counter=new HashMap<String,RefInteger>();
public DatasourceConnection getDatasourceConnection(PageContext pc,DataSource datasource, String user, String pass) throws PageException {
pc=ThreadLocalPageContext.get(pc);
if(StringUtil.isEmpty(user)) {
user=datasource.getUsername();
pass=datasource.getPassword();
}
if(pass==null)pass="";
// get stack
DCStack stack=getDCStack(datasource,user,pass);
// max connection
int max=datasource.getConnectionLimit();
synchronized (stack) {
while(max!=-1 && max<=_size(datasource)) {
try {
//stack.inc();
stack.wait(10000L);
}
catch (InterruptedException e) {
throw Caster.toPageException(e);
}
}
if(pc!=null){
while(!stack.isEmpty()) {
DatasourceConnectionImpl dc=(DatasourceConnectionImpl) stack.get(pc);
if(dc!=null && isValid(dc,Boolean.TRUE)){
_inc(datasource);
return dc.using();
}
}
}
_inc(datasource);
return loadDatasourceConnection(datasource, user, pass).using();
}
}
private DatasourceConnectionImpl loadDatasourceConnection(DataSource ds, String user, String pass) throws DatabaseException {
Connection conn=null;
String connStr = ds.getDsnTranslated();
try {
conn = DBUtil.getConnection(connStr, user, pass);
conn.setAutoCommit(true);
}
catch (SQLException e) {
throw new DatabaseException(e,null);
}
//print.err("create connection");
return new DatasourceConnectionImpl(conn,ds,user,pass);
}
public void releaseDatasourceConnection(DatasourceConnection dc) {
if(dc==null) return;
DCStack stack=getDCStack(dc.getDatasource(), dc.getUsername(), dc.getPassword());
synchronized (stack) {
stack.add(dc);
int max = dc.getDatasource().getConnectionLimit();
if(max!=-1) {
_dec(dc.getDatasource());
stack.notify();
}
else _dec(dc.getDatasource());
}
}
public void clear() {
//int size=0;
// remove all timed out conns
try{
Object[] arr = dcs.entrySet().toArray();
if(ArrayUtil.isEmpty(arr)) return;
for(int i=0;i<arr.length;i++) {
DCStack conns=(DCStack) ((Map.Entry) arr[i]).getValue();
if(conns!=null)conns.clear();
//size+=conns.size();
}
}
catch(Throwable t){}
}
public void remove(DataSource datasource) {
Object[] arr = dcs.keySet().toArray();
String key,id=createId(datasource);
for(int i=0;i<arr.length;i++) {
key=(String) arr[i];
if(key.startsWith(id)) {
DCStack conns=dcs.get(key);
conns.clear();
}
}
RefInteger ri=counter.get(id);
if(ri!=null)ri.setValue(0);
else counter.put(id,new RefIntegerImpl(0));
}
public static boolean isValid(DatasourceConnection dc,Boolean autoCommit) {
try {
if(dc.getConnection().isClosed())return false;
}
catch (Throwable t) {return false;}
try {
if(dc.getDatasource().validate() && !DataSourceUtil.isValid(dc,1000))return false;
}
catch (Throwable t) {} // not all driver support this, because of that we ignore a error here, also protect from java 5
try {
if(autoCommit!=null) dc.getConnection().setAutoCommit(autoCommit.booleanValue());
}
catch (Throwable t) {return false;}
return true;
}
private DCStack getDCStack(DataSource datasource, String user, String pass) {
String id = createId(datasource,user,pass);
synchronized(id) {
DCStack stack=dcs.get(id);
if(stack==null){
dcs.put(id, stack=new DCStack());
}
return stack;
}
}
public int openConnections() {
Iterator<DCStack> it = dcs.values().iterator();
int count=0;
while(it.hasNext()){
count+=it.next().openConnections();
}
return count;
}
private void _inc(DataSource datasource) {
_getCounter(datasource).plus(1);
}
private void _dec(DataSource datasource) {
_getCounter(datasource).minus(1);
}
private int _size(DataSource datasource) {
return _getCounter(datasource).toInt();
}
private RefInteger _getCounter(DataSource datasource) {
String did = createId(datasource);
RefInteger ri=counter.get(did);
if(ri==null) {
counter.put(did,ri=new RefIntegerImpl(0));
}
return ri;
}
public static String createId(DataSource datasource, String user, String pass) {
return createId(datasource)+":"+user+":"+pass;
}
private static String createId(DataSource datasource) {
if(datasource instanceof DataSourcePro) return ((DataSourceSupport)datasource).id();
return datasource.getClazz().getName()+":"+datasource.getDsnTranslated()+":"+datasource.getClazz().getName();
}
}