/**
* 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.SQLException;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TimeZone;
import lucee.commons.io.log.Log;
import lucee.commons.lang.ClassException;
import lucee.commons.lang.StringUtil;
import lucee.runtime.config.Config;
import lucee.runtime.config.ConfigWebUtil;
import lucee.runtime.engine.ThreadLocalPageContext;
import lucee.runtime.op.Caster;
import lucee.runtime.type.Collection.Key;
import lucee.runtime.type.KeyImpl;
import lucee.runtime.type.Struct;
import lucee.runtime.type.util.CollectionUtil;
import org.osgi.framework.BundleException;
/**
*
*/
public final class DataSourceImpl extends DataSourceSupport {
private String connStr;
private String host;
private String database;
private int port;
private String connStrTranslated;
private Struct custom;
private boolean validate;
private String dbdriver;
private final ParamSyntax paramSyntax;
private final boolean literalTimestampWithTSOffset;
private final boolean alwaysSetTimeout;
/**
* constructor of the class
* @param name
* @param className
* @param host
* @param dsn
* @param database
* @param port
* @param username
* @param password
* @param connectionLimit
* @param connectionTimeout
* @param blob
* @param clob
* @param allow
* @param custom
* @param readOnly
* @param ps
* @throws ClassNotFoundException
* @throws ClassException
* @throws BundleException
* @throws SQLException
*/
public DataSourceImpl(Config config,JDBCDriver driver,String name, ClassDefinition cd, String host, String connStr, String database, int port, String username, String password,
int connectionLimit, int connectionTimeout, long metaCacheTimeout, boolean blob, boolean clob, int allow, Struct custom, boolean readOnly,
boolean validate, boolean storage, TimeZone timezone, String dbdriver,ParamSyntax paramSyntax, boolean literalTimestampWithTSOffset,boolean alwaysSetTimeout, Log log) throws BundleException, ClassException, SQLException {
super(config,driver,name, cd,username,ConfigWebUtil.decrypt(password),blob,clob,connectionLimit, connectionTimeout, metaCacheTimeout, timezone, allow<0?ALLOW_ALL:allow, storage, readOnly,log);
this.host=host;
this.database=database;
this.connStr=connStr;
this.port=port;
this.custom=custom;
this.validate=validate;
this.connStrTranslated=connStr;
this.paramSyntax=(paramSyntax==null)?ParamSyntax.DEFAULT:paramSyntax;
this.literalTimestampWithTSOffset=literalTimestampWithTSOffset;
this.alwaysSetTimeout=alwaysSetTimeout;
translateConnStr();
this.dbdriver = dbdriver;
}
private void translateConnStr() {
connStrTranslated=replace(connStrTranslated,"host",host,false,false);
connStrTranslated=replace(connStrTranslated,"database",database,false,false);
connStrTranslated=replace(connStrTranslated,"port",Caster.toString(port),false,false);
connStrTranslated=replace(connStrTranslated,"username",getUsername(),false,false);
connStrTranslated=replace(connStrTranslated,"password",getPassword(),false,false);
//Collection.Key[] keys = custom==null?new Collection.Key[0]:custom.keys();
if(custom!=null) {
Iterator<Entry<Key, Object>> it = custom.entryIterator();
Entry<Key, Object> e;
boolean leading=true;
while(it.hasNext()) {
e = it.next();
connStrTranslated=replace(connStrTranslated,e.getKey().getString(),Caster.toString(e.getValue(),""),true,leading);
leading=false;
}
}
}
private String replace(String src, String name, String value,boolean doQueryString, boolean leading) {
if(StringUtil.indexOfIgnoreCase(src,"{"+name+"}")!=-1) {
return StringUtil.replace(connStrTranslated,"{"+name+"}",value,false);
}
if(!doQueryString) return src;
// FUTURE remove; this is for backward compatibility to old MSSQL driver
if(ParamSyntax.DEFAULT.equals(paramSyntax) && getClassDefinition().getClassName().indexOf("microsoft")!=-1 || getClassDefinition().getClassName().indexOf("jtds")!=-1)
return src+=';'+name+'='+value;
return src+=(leading?paramSyntax.leadingDelimiter:paramSyntax.delimiter)+name+paramSyntax.separator+value;
//return src+=((src.indexOf('?')!=-1)?'&':'?')+name+'='+value;
}
@Override
public String getDsnOriginal() {
return getConnectionString();
}
@Override
public String getConnectionString() {
return connStr;
}
@Override
public String getDsnTranslated() {
return getConnectionStringTranslated();
}
@Override
public String getConnectionStringTranslated() {
return connStrTranslated;
}
@Override
public String getDatabase() {
return database;
}
@Override
public int getPort() {
return port;
}
@Override
public String getHost() {
return host;
}
// FUTURE add to interface
public ParamSyntax getParamSyntax() {
return paramSyntax;
}
// FUTURE add to interface
public boolean getLiteralTimestampWithTSOffset() {
return literalTimestampWithTSOffset;
}
// FUTURE add to interface
public boolean getAlwaysSetTimeout() {
return alwaysSetTimeout;
}
@Override
public Object clone() {
return _clone(isReadOnly());
}
@Override
public DataSource cloneReadOnly() {
return _clone(true);
}
public DataSource _clone(boolean readOnly) {
try {
return new DataSourceImpl(ThreadLocalPageContext.getConfig(),jdbc,getName(),getClassDefinition(), host, connStr, database, port, getUsername(), getPassword(), getConnectionLimit(), getConnectionTimeout(),getMetaCacheTimeout(), isBlob(), isClob(), allow, custom, readOnly, validate, isStorage(),getTimeZone(), dbdriver,getParamSyntax(),literalTimestampWithTSOffset,alwaysSetTimeout,getLog());
} catch (RuntimeException re) {
throw re; // this should never happens, because the class was already loaded in this object
} catch (Exception e) {
throw new RuntimeException(e); // this should never happens, because the class was already loaded in this object
}
}
@Override
public String getCustomValue(String key) {
return Caster.toString(custom.get(KeyImpl.init(key),null),"");
}
@Override
public String[] getCustomNames() {
return CollectionUtil.keysAsString(custom);
}
@Override
public Struct getCustoms() {
return (Struct)custom.clone();
}
@Override
public boolean validate() {
return validate;
}
public String getDbDriver() {
return dbdriver;
}
}