/**
* Copyright (c) 2014, the Railo Company Ltd.
* Copyright (c) 2015, Lucee Assosication Switzerland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
package lucee.runtime.db;
import java.sql.Connection;
import java.sql.SQLException;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.SystemOut;
import lucee.commons.lang.types.RefInteger;
import lucee.commons.lang.types.RefIntegerImpl;
class DCStack {
private Item item;
private DataSource datasource;
private String user;
private String pass;
private final RefInteger counter;
DCStack(DataSource datasource, String user, String pass) {
this.datasource=datasource;
this.user=user;
this.pass=pass;
this.counter=new RefIntegerImpl(0);
}
public DataSource getDatasource() {
return datasource;
}
public String getUsername() {
return user;
}
public String getPassword() {
return pass;
}
public void add(DatasourceConnection dc){
// make sure the connection is not already in stack, this can happen when the conn is released twice
Item test = item;
while(test!=null){
if(test.dc==dc) {
SystemOut.print("a datasource connection was released twice!");
return;
}
test=test.prev;
}
item=new Item(item,dc);
}
public DatasourceConnection get(){
if(item==null) return null;
DatasourceConnection rtn = item.dc;
item=item.prev;
try {
if(!rtn.getConnection().isClosed()){
return rtn;
}
return get();
}
catch (SQLException e) {}
return null;
}
public boolean isEmpty(){
return item==null;
}
public int size(){
int count=0;
Item i = item;
while(i!=null){
count++;
i=i.prev;
}
return count;
}
public int openConnectionsIn(){
int count=0;
Item i = item;
while(i!=null){
try {
if(!i.dc.getConnection().isClosed())count++;
}
catch (SQLException e) {}
i=i.prev;
}
return count;
}
public int openConnectionsOut(){
return counter.toInt();
}
public int openConnections(){
return openConnectionsIn()+openConnectionsOut();
}
class Item {
private DatasourceConnection dc;
private Item prev;
private int count=1;
public Item(Item item,DatasourceConnection dc) {
this.prev=item;
this.dc=dc;
if(prev!=null)count=prev.count+1;
}
@Override
public String toString(){
return "("+prev+")<-"+count;
}
}
public synchronized void clear(boolean force) {
clear(item,null,force);
}
/**
*
* @param current
* @param next
* @param timeout timeout in seconds used to validate existing connections
* @throws SQLException
*/
private void clear(Item current,Item next, boolean force) {
if(current==null) return;
// timeout or closed
if( force ||
current.dc.isTimeout() ||
current.dc.isLifecycleTimeout() ||
isClosedEL(current.dc.getConnection()) ||
Boolean.FALSE.equals(isValidEL(current.dc.getConnection()))) {
// when timeout was reached but it is still open, close it
if(!isClosedEL(current.dc.getConnection())){
try {
current.dc.close();
}
catch (SQLException e) {}
}
// remove this connection from chain
if(next==null)item=current.prev;
else {
next.prev=current.prev;
}
clear(current.prev,next,force);
}
else clear(current.prev,current,force);
counter.setValue(0);
}
private boolean isClosedEL(Connection conn) {
try {
return conn.isClosed();
} catch (SQLException se) {
datasource.getLog().error("Connection Pool", se);
// in case of a exception we see this conn as useless and close the connection
try {
conn.close();
}
catch (SQLException e) {
datasource.getLog().error("Connection Pool", e);
}
return true;
}
}
private Boolean isValidEL(Connection conn) {
try {
return conn.isValid(datasource.getNetworkTimeout())?Boolean.TRUE:Boolean.FALSE;
}
catch(Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
return null;
}
}
public RefInteger getCounter() {
return counter;
}
}