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.PreparedStatement;
023import java.sql.SQLException;
024import java.sql.Statement;
025
026import lucee.commons.lang.ExceptionUtil;
027import lucee.commons.lang.StringUtil;
028import lucee.runtime.config.Config;
029import lucee.runtime.config.ConfigImpl;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.op.Caster;
032import lucee.runtime.spooler.Task;
033
034/**
035 * wrap for datasorce and connection from it
036 */
037public final class DatasourceConnectionImpl implements DatasourceConnection,Task {
038    
039    //private static final int MAX_PS = 100;
040        private Connection connection;
041    private DataSource datasource;
042    private long time;
043    private final long start;
044        private String username;
045        private String password;
046        private int transactionIsolationLevel=-1;
047        private int requestId=-1;
048        private Boolean supportsGetGeneratedKeys;
049
050    /**
051     * @param connection
052     * @param datasource
053     * @param pass  
054     * @param user 
055     */
056    public DatasourceConnectionImpl(Connection connection, DataSource datasource, String username, String password) {
057        this.connection = connection;
058        this.datasource = datasource;
059        this.time=this.start=System.currentTimeMillis();
060        this.username = username;
061        this.password = password;
062        
063        if(username==null) {
064                this.username=datasource.getUsername();
065                this.password=datasource.getPassword();
066        }
067        if(this.password==null)this.password="";
068                
069    }
070    
071    @Override
072    public Connection getConnection() {
073        return connection;
074    }
075
076    @Override
077    public DataSource getDatasource() {
078        return datasource;
079    }
080
081    @Override
082    public boolean isTimeout() {
083        int timeout=datasource.getConnectionTimeout();
084        if(timeout <= 0) return false;
085        timeout*=60000;      
086        return (time+timeout)<System.currentTimeMillis();
087    }
088
089    public boolean isLifecycleTimeout() {
090        int timeout=datasource.getConnectionTimeout()*5;// fo3 the moment simply 5 times the idle timeout
091        if(timeout <= 0) return false;
092        timeout*=60000;      
093        return (start+timeout)<System.currentTimeMillis();
094    }
095
096        public DatasourceConnection using() {
097                time=System.currentTimeMillis();
098                return this;
099        }
100
101        /**
102         * @return the password
103         */
104        public String getPassword() {
105                return password;
106        }
107
108        /**
109         * @return the username
110         */
111        public String getUsername() {
112                return username;
113        }
114
115        @Override
116        public boolean equals(Object obj) {
117                if(this==obj) return true;
118                
119                if(!(obj instanceof DatasourceConnectionImpl)) return false;
120                return equals(this, (DatasourceConnection) obj);
121                
122                
123                /*if(!(obj instanceof DatasourceConnectionImpl)) return false;
124                DatasourceConnectionImpl other=(DatasourceConnectionImpl) obj;
125                
126                if(!datasource.equals(other.datasource)) return false;
127                //print.out(username+".equals("+other.username+") && "+password+".equals("+other.password+")");
128                return username.equals(other.username) && password.equals(other.password);*/
129        }
130        
131        public static boolean equals(DatasourceConnection left,DatasourceConnection right) {
132                
133                if(!left.getDatasource().equals(right.getDatasource())) return false;
134                return StringUtil.emptyIfNull(left.getUsername()).equals(StringUtil.emptyIfNull(right.getUsername())) 
135                        && StringUtil.emptyIfNull(left.getPassword()).equals(StringUtil.emptyIfNull(right.getPassword()));
136        }
137        
138        
139
140        /**
141         * @return the transactionIsolationLevel
142         */
143        public int getTransactionIsolationLevel() {
144                return transactionIsolationLevel;
145        }
146
147        
148        public int getRequestId() {
149                return requestId;
150        }
151        public void setRequestId(int requestId) {
152                this.requestId=requestId;
153        }
154
155        @Override
156        public boolean supportsGetGeneratedKeys() {
157                if(supportsGetGeneratedKeys==null){
158                        try {
159                                supportsGetGeneratedKeys=Caster.toBoolean(getConnection().getMetaData().supportsGetGeneratedKeys());
160                        } catch (Throwable t) {
161                ExceptionUtil.rethrowIfNecessary(t);
162                                return false;
163                        }
164                }
165                return supportsGetGeneratedKeys.booleanValue();
166        }
167        
168        //private Map<String,PreparedStatement> preparedStatements=new HashMap<String, PreparedStatement>();
169        
170        @Override
171        public PreparedStatement getPreparedStatement(SQL sql, boolean createGeneratedKeys,boolean allowCaching) throws SQLException {
172                if(createGeneratedKeys) return getConnection().prepareStatement(sql.getSQLString(),Statement.RETURN_GENERATED_KEYS);
173                return getConnection().prepareStatement(sql.getSQLString());
174        }
175        
176        
177        /*public PreparedStatement getPreparedStatement(SQL sql, boolean createGeneratedKeys,boolean allowCaching) throws SQLException {
178                // create key
179                String strSQL=sql.getSQLString();
180                String key=strSQL.trim()+":"+createGeneratedKeys;
181                try {
182                        key = MD5.getDigestAsString(key);
183                } catch (IOException e) {}
184                PreparedStatement ps = allowCaching?preparedStatements.get(key):null;
185                if(ps!=null) {
186                        if(DataSourceUtil.isClosed(ps,true)) 
187                                preparedStatements.remove(key);
188                        else return ps;
189                }
190                
191                
192                if(createGeneratedKeys) ps= getConnection().prepareStatement(strSQL,Statement.RETURN_GENERATED_KEYS);
193                else ps=getConnection().prepareStatement(strSQL);
194                if(preparedStatements.size()>MAX_PS)
195                        closePreparedStatements((preparedStatements.size()-MAX_PS)+1);
196                if(allowCaching)preparedStatements.put(key,ps);
197                return ps;
198        }*/
199        
200        
201
202        @Override
203        public PreparedStatement getPreparedStatement(SQL sql, int resultSetType,int resultSetConcurrency) throws SQLException {
204                return getConnection().prepareStatement(sql.getSQLString(),resultSetType,resultSetConcurrency);
205        }
206        
207        /*
208         
209        public PreparedStatement getPreparedStatement(SQL sql, int resultSetType,int resultSetConcurrency) throws SQLException {
210                boolean allowCaching=false;
211                // create key
212                String strSQL=sql.getSQLString();
213                String key=strSQL.trim()+":"+resultSetType+":"+resultSetConcurrency;
214                try {
215                        key = MD5.getDigestAsString(key);
216                } catch (IOException e) {}
217                PreparedStatement ps = allowCaching?preparedStatements.get(key):null;
218                if(ps!=null) {
219                        if(DataSourceUtil.isClosed(ps,true)) 
220                                preparedStatements.remove(key);
221                        else return ps;
222                }
223                
224                ps=getConnection().prepareStatement(strSQL,resultSetType,resultSetConcurrency);
225                if(preparedStatements.size()>MAX_PS)
226                        closePreparedStatements((preparedStatements.size()-MAX_PS)+1);
227                if(allowCaching)preparedStatements.put(key,ps);
228                return ps;
229        }
230         */
231        
232
233        @Override
234        public void close() throws SQLException {
235                //closePreparedStatements(-1);
236                getConnection().close();
237        }
238
239        @Override
240        public Object execute(Config config) throws PageException {
241                ((ConfigImpl)config).getDatasourceConnectionPool().releaseDatasourceConnection(this);
242                return null;
243        }
244        
245}