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    }