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 021 022import java.sql.Connection; 023import java.sql.SQLException; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.Map; 027 028import lucee.commons.io.IOUtil; 029import lucee.runtime.PageContext; 030import lucee.runtime.PageContextImpl; 031import lucee.runtime.config.ConfigImpl; 032import lucee.runtime.engine.ThreadLocalPageContext; 033import lucee.runtime.exp.DatabaseException; 034import lucee.runtime.exp.DeprecatedException; 035import lucee.runtime.exp.ExceptionHandler; 036import lucee.runtime.exp.PageException; 037import lucee.runtime.exp.PageRuntimeException; 038import lucee.runtime.orm.ORMDatasourceConnection; 039import lucee.runtime.orm.ORMSession; 040 041/** 042 * this class handle multible db connection, transaction and logging 043 */ 044public final class DatasourceManagerImpl implements DataSourceManager { 045 046 public static final String QOQ_DATASOURCE_NAME = "_queryofquerydb"; 047 048 private ConfigImpl config; 049 050 boolean autoCommit=true; 051 private int isolation=Connection.TRANSACTION_NONE; 052 private Map<DataSource,DatasourceConnection> transConns=new HashMap<DataSource,DatasourceConnection>(); 053 //private DatasourceConnection transConn; 054 055 056 /** 057 * constructor of the class 058 * @param pc 059 */ 060 public DatasourceManagerImpl(ConfigImpl c) { 061 this.config=c; 062 } 063 064 private DatasourceConnection getTDC(DataSource ds) { 065 return transConns.get(ds); 066 } 067 068 069 @Override 070 public DatasourceConnection getConnection(PageContext pc,String _datasource, String user, String pass) throws PageException { 071 return getConnection(pc,((PageContextImpl)pc).getDataSource(_datasource), user, pass); 072 } 073 074 @Override 075 public DatasourceConnection getConnection(PageContext pc,DataSource ds, String user, String pass) throws PageException { 076 if(autoCommit) 077 return config.getDatasourceConnectionPool().getDatasourceConnection(ds,user,pass); 078 079 080 pc=ThreadLocalPageContext.get(pc); 081 DatasourceConnection newDC = ((PageContextImpl)pc)._getConnection(ds,user,pass); 082 083 // transaction 084 //if(!autoCommit) { 085 try { 086 DatasourceConnection existingDC = getTDC(ds); 087 if(existingDC==null) { 088 newDC.getConnection().setAutoCommit(false); 089 090 if(isolation!=Connection.TRANSACTION_NONE) 091 newDC.getConnection().setTransactionIsolation(isolation); 092 transConns.put(ds, newDC); 093 } 094 else if(!existingDC.equals(newDC)) { 095 if(QOQ_DATASOURCE_NAME.equalsIgnoreCase(ds.getName())) return newDC; 096 releaseConnection(pc, newDC); 097 throw new DatabaseException( 098 "can't use different connections to the same datasource inside a single transaction",null,null,newDC); 099 } 100 else if(newDC.getConnection().getAutoCommit()) { 101 newDC.getConnection().setAutoCommit(false); 102 } 103 } catch (SQLException e) { 104 ExceptionHandler.printStackTrace(e); 105 } 106 //} 107 return newDC; 108 } 109 110 public void add(PageContext pc,ORMSession session) throws PageException { 111 DataSource[] sources = session.getDataSources(); 112 for(int i=0;i<sources.length;i++){ 113 _add(pc,session,sources[i]); 114 } 115 116 } 117 118 private void _add(PageContext pc,ORMSession session, DataSource ds) throws PageException { 119 120 // transaction 121 if(!autoCommit) { 122 ORMDatasourceConnection newDC = new ORMDatasourceConnection(pc,session,ds); 123 124 try { 125 DatasourceConnection existingDC = getTDC(ds); 126 if(existingDC==null) { 127 if(isolation!=Connection.TRANSACTION_NONE) 128 newDC.getConnection().setTransactionIsolation(isolation); 129 transConns.put(ds, newDC); 130 131 } 132 else if(!existingDC.equals(newDC)) { 133 releaseConnection(pc,newDC); 134 throw new DatabaseException( 135 "can't use different connections to the same datasource inside a single transaction",null,null,newDC); 136 } 137 else if(newDC.isAutoCommit()) { 138 newDC.setAutoCommit(false); 139 } 140 } catch (SQLException e) { 141 ExceptionHandler.printStackTrace(e); 142 } 143 } 144 } 145 146 @Override 147 public void releaseConnection(PageContext pc,DatasourceConnection dc) { 148 if(autoCommit ) { 149 if(pc!=null && ((PageContextImpl)pc).getStopPosition()!=null) { 150 IOUtil.closeEL(dc.getConnection()); 151 } 152 else 153 config.getDatasourceConnectionPool().releaseDatasourceConnection(config,dc,false); 154 } 155 } 156 157 @Override 158 public void begin() { 159 this.autoCommit=false; 160 this.isolation=Connection.TRANSACTION_NONE; 161 } 162 163 @Override 164 public void begin(String isolation) { 165 this.autoCommit=false; 166 167 if(isolation.equalsIgnoreCase("read_uncommitted")) 168 this.isolation=Connection.TRANSACTION_READ_UNCOMMITTED; 169 else if(isolation.equalsIgnoreCase("read_committed")) 170 this.isolation=Connection.TRANSACTION_READ_COMMITTED; 171 else if(isolation.equalsIgnoreCase("repeatable_read")) 172 this.isolation=Connection.TRANSACTION_REPEATABLE_READ; 173 else if(isolation.equalsIgnoreCase("serializable")) 174 this.isolation=Connection.TRANSACTION_SERIALIZABLE; 175 else 176 this.isolation=Connection.TRANSACTION_NONE; 177 178 } 179 @Override 180 public void begin(int isolation) { 181 //print.out("begin:"+autoCommit); 182 this.autoCommit=false; 183 this.isolation=isolation; 184 } 185 186 @Override 187 public void rollback() throws DatabaseException { 188 if(autoCommit || transConns.size()==0)return; 189 190 Iterator<DatasourceConnection> it = this.transConns.values().iterator(); 191 DatasourceConnection dc=null; 192 try { 193 while(it.hasNext()){ 194 dc= it.next(); 195 dc.getConnection().rollback(); 196 } 197 } 198 catch (SQLException e) { 199 throw new DatabaseException(e,dc); 200 } 201 } 202 203 @Override 204 public void savepoint() throws DatabaseException { 205 if(autoCommit || transConns.size()==0)return; 206 207 Iterator<DatasourceConnection> it = this.transConns.values().iterator(); 208 DatasourceConnection dc=null; 209 try { 210 while(it.hasNext()){ 211 dc= it.next(); 212 dc.getConnection().setSavepoint(); 213 } 214 } 215 catch (SQLException e) { 216 throw new DatabaseException(e,dc); 217 } 218 } 219 220 @Override 221 public void commit() throws DatabaseException { 222 if(autoCommit || transConns.size()==0)return ; 223 224 Iterator<DatasourceConnection> it = this.transConns.values().iterator(); 225 DatasourceConnection dc=null; 226 try { 227 while(it.hasNext()){ 228 dc= it.next(); 229 dc.getConnection().commit(); 230 } 231 } 232 catch (SQLException e) { 233 throw new DatabaseException(e,dc); 234 } 235 } 236 237 @Override 238 public boolean isAutoCommit() { 239 return autoCommit; 240 } 241 242 @Override 243 public void end() { 244 autoCommit=true; 245 if(transConns.size()>0) { 246 Iterator<DatasourceConnection> it = this.transConns.values().iterator(); 247 DatasourceConnection dc; 248 while(it.hasNext()){ 249 dc = it.next(); 250 try { 251 dc.getConnection().setAutoCommit(true); 252 } 253 catch (SQLException e) { 254 ExceptionHandler.printStackTrace(e); 255 } 256 //releaseConnection(null, dc); 257 } 258 transConns.clear(); 259 } 260 } 261 262 public void remove(DataSource datasource) { 263 config.getDatasourceConnectionPool().remove(datasource); 264 } 265 266 public void remove(String datasource) { 267 throw new PageRuntimeException(new DeprecatedException("method no longer supported!")); 268 //config.getDatasourceConnectionPool().remove(datasource); 269 } 270 271 public void release() { 272 transConns.clear(); 273 this.isolation=Connection.TRANSACTION_NONE; 274 } 275 276}