package ch.elexis.data;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.elexis.core.constants.Preferences;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.core.data.cache.IPersistentObjectCache;
import ch.elexis.core.data.cache.MultiGuavaCache;
import ch.rgw.tools.JdbcLink;
import ch.rgw.tools.JdbcLink.Stm;
import ch.rgw.tools.StringTool;
import ch.rgw.tools.net.NetTool;
/**
* Class managing a connection to a DB using a {@link JdbcLink}, and also the cache used by
* {@link PersistentObject} loaded. </br>
* </br>
* By introducing this class it is possible to manage {@link PersistentObject} from different DBs in
* one Elexis instance.
*
* @author thomas
*
*/
public class DBConnection {
private static Logger logger = LoggerFactory.getLogger(DBConnection.class);
public static final int CACHE_DEFAULT_LIFETIME = 15;
public static final int CACHE_MIN_LIFETIME = 5;
public static final int CACHE_TIME_MAX = 300;
protected int default_lifetime;
private IPersistentObjectCache<String> cache;
private JdbcLink jdbcLink;
private String username;
private String pcname;
private String tracetable;
private boolean runningFromScratch = false;
private String dbUser;
private String dbPw;
private String dbFlavor;
private String dbConnectString;
private String dbDriver;
private File runFromScratchDB = null;
public DBConnection(){
default_lifetime =
CoreHub.localCfg.get(Preferences.ABL_CACHELIFETIME, CACHE_DEFAULT_LIFETIME);
if (default_lifetime < CACHE_MIN_LIFETIME) {
default_lifetime = CACHE_MIN_LIFETIME;
CoreHub.localCfg.set(Preferences.ABL_CACHELIFETIME, CACHE_MIN_LIFETIME);
}
cache = new MultiGuavaCache<String>(default_lifetime, TimeUnit.SECONDS);
logger.info("Cache setup: default_lifetime " + default_lifetime);
}
/**
* Lookup if {@link Preferences.ABL_TRACE} in global and local config, and start trace if set.
*
*/
public void initTrace(){
// Wenn trace global eingeschaltet ist, gilt es für alle
setTrace(CoreHub.globalCfg.get(Preferences.ABL_TRACE, null));
// wenn trace global nicht eingeschaltet ist, kann es immer noch für
// diese Station eingeschaltet sein
if (tracetable == null) {
setTrace(CoreHub.localCfg.get(Preferences.ABL_TRACE, null));
}
}
public void setDBPassword(String password){
this.dbPw = password;
}
public void setDBUser(String username){
this.dbUser = username;
}
public void setDBFlavor(String dbFlavor){
this.dbFlavor = dbFlavor;
}
public String getDBFlavor(){
return jdbcLink.DBFlavor;
}
public String getDBConnectString(){
if (jdbcLink != null) {
return jdbcLink.getConnectString();
} else {
return dbConnectString;
}
}
public void setDBConnectString(String connectString){
this.dbConnectString = connectString;
}
public void setDBDriver(String driver){
this.dbDriver = driver;
}
public String getDBDriver(){
return dbDriver;
}
/**
* Check if the configured values of dbFlavor, dbSpec, dbUser & dbPw allow connecting directly
* to a database.
*
* @return
*/
public boolean isDirectConnectConfigured(){
return dbFlavor != null && dbFlavor.length() >= 2 && dbConnectString != null
&& dbConnectString.length() > 5 && dbUser != null && dbPw != null;
}
/**
* Try connecting directly to a database using the configured dbFlavor, dbSpec, dbUser & dbPw
* values.
*
* @return
*/
public boolean directConnect(){
String msg = "Connecting to DB using " + dbFlavor + " " + dbConnectString + " " + dbUser;
logger.info(msg);
if (dbFlavor.equalsIgnoreCase("mysql"))
dbDriver = "com.mysql.jdbc.Driver";
else if (dbFlavor.equalsIgnoreCase("postgresql"))
dbDriver = "org.postgresql.Driver";
else if (dbFlavor.equalsIgnoreCase("h2"))
dbDriver = "org.h2.Driver";
else
dbDriver = "invalid";
if (!dbDriver.equalsIgnoreCase("invalid")) {
jdbcLink = new JdbcLink(dbDriver, dbConnectString, dbFlavor);
boolean ret = jdbcLink.connect(dbUser, dbPw);
if (ret) {
logger.debug("Verbunden mit " + dbDriver + ", " + dbConnectString);
} else {
logger.debug("Verbindung fehlgeschlagen mit " + dbDriver + ", " + dbConnectString);
}
return ret;
} else {
msg = "can't connect to test database invalid. dbFlavor" + dbFlavor;
logger.error(msg);
}
return false;
}
/**
* Directly set the {@link JdbcLink} used.
*
* @param jdbcLink
*/
public void setJdbcLink(JdbcLink jdbcLink){
this.jdbcLink = jdbcLink;
}
/**
* Create a H2 db {@link JdbcLink}. It is not connected, but driver, connection string, etc. is
* configured.
*
* @param string
*/
public void createH2Link(String string){
jdbcLink = JdbcLink.createH2Link(string);
}
/**
* Connect to the database using a {@link JdbcLink}.</br>
* </br>
* Following configurations are possible:</br>
* - dbDriver, dbConnectString, dbFlavor, dbUser, dbPass</br>
* - dbConnectString, dbUser, dbPass</br>
* - jdbcLink, dbUser, dbPass</br>
*
* @return success
*/
public boolean connect(){
if (jdbcLink == null && dbDriver != null && dbConnectString != null && dbFlavor != null) {
jdbcLink = new JdbcLink(dbDriver, dbConnectString, dbFlavor);
} else if (jdbcLink == null && dbConnectString != null && dbFlavor == null
&& dbDriver == null) {
if (parseConnectString()) {
return directConnect();
}
}
if (jdbcLink != null && dbUser != null && dbPw != null) {
boolean ret = jdbcLink.connect(dbUser, dbPw);
if (ret) {
logger.debug("Verbunden mit " + dbDriver + ", " + dbConnectString);
} else {
logger.debug("Verbindung fehlgeschlagen mit " + dbDriver + ", " + dbConnectString);
}
return ret;
}
return false;
}
private boolean parseConnectString(){
if (dbConnectString != null && dbConnectString.length() > 5) {
String url = dbConnectString;
String cleanURI = url.substring(5);
URI uri = URI.create(cleanURI);
setDBFlavor(uri.getScheme());
return true;
}
return false;
}
public void setRunningFromScratch(boolean runningFromScratch){
this.runningFromScratch = runningFromScratch;
}
public boolean isRunningFromScratch(){
return runningFromScratch;
}
public void runFromScatch() throws IOException{
runFromScratchDB = File.createTempFile("elexis", "db");
logger
.info("RunFromScratch test database created in " + runFromScratchDB.getAbsolutePath());
dbUser = "sa";
dbPw = StringTool.leer;
jdbcLink = JdbcLink.createH2Link(runFromScratchDB.getAbsolutePath());
}
/**
* Trace (protokollieren aller Schreibvorgänge) ein- und ausschalten. Die Trace-Tabelle muss
* folgende Spalten haben: logtime (long), Workstation (VARCHAR), Username(Varchar), action
* (Text/Longvarchar)
*
* @param Tablename
* Name der Trace-tabelle oder null: Trace aus.
*/
public void setTrace(String Tablename){
if ((Tablename != null) && (Tablename.equals("none") || Tablename.equals(""))) {
Tablename = null;
}
tracetable = Tablename;
username = JdbcLink.wrap(System.getProperty("user.name"));
pcname = JdbcLink.wrap(NetTool.hostname);
}
public void disconnect(){
if (jdbcLink.DBFlavor.startsWith("hsqldb")) {
jdbcLink.exec("SHUTDOWN COMPACT");
}
jdbcLink.disconnect();
logger.info("Verbindung zur Datenbank " + jdbcLink.getConnectString() + " getrennt.");
jdbcLink = null;
if (runFromScratchDB != null) {
File dbFile = new File(runFromScratchDB.getAbsolutePath() + ".h2.db");
logger.info("Deleting runFromScratchDB was " + runFromScratchDB + " and " + dbFile);
dbFile.delete();
runFromScratchDB.delete();
}
cache.stat();
}
public String queryString(String sql){
return jdbcLink.queryString(sql);
}
public IPersistentObjectCache<String> getCache(){
return cache;
}
public PreparedStatement getPreparedStatement(String sql){
return jdbcLink.getPreparedStatement(sql);
}
public void releasePreparedStatement(PreparedStatement statement){
jdbcLink.releasePreparedStatement(statement);
}
public Stm getStatement(){
return jdbcLink.getStatement();
}
public void releaseStatement(Stm stm){
jdbcLink.releaseStatement(stm);
}
public int exec(String sql){
return jdbcLink.exec(sql);
}
public String wrapFlavored(String wert){
return jdbcLink.wrapFlavored(wert);
}
public boolean isTrace(){
return tracetable != null;
}
public int getDefaultLifeTime(){
return default_lifetime;
}
public void doTrace(String sql){
StringBuffer tracer = new StringBuffer();
tracer.append("INSERT INTO ").append(tracetable);
tracer.append(" (logtime,Workstation,Username,action) VALUES (");
tracer.append(System.currentTimeMillis()).append(",");
tracer.append(pcname).append(",");
tracer.append(username).append(",");
tracer.append(JdbcLink.wrap(sql.replace('\'', '/'))).append(")");
exec(tracer.toString());
}
public JdbcLink getJdbcLink(){
return jdbcLink;
}
public Connection getConnection(){
return jdbcLink.getConnection();
}
}