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    }