/*******************************************************************************
* Copyright (c) 2012-2015 INRIA.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Generoso Pagano - initial API and implementation
******************************************************************************/
package fr.inria.soctrace.lib.storage.dbmanager;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.inria.soctrace.lib.model.utils.SoCTraceException;
import fr.inria.soctrace.lib.storage.utils.SQLConstants;
import fr.inria.soctrace.lib.storage.utils.SQLConstants.FramesocTable;
import fr.inria.soctrace.lib.utils.Configuration;
import fr.inria.soctrace.lib.utils.Configuration.SoCTraceProperty;
import fr.inria.soctrace.lib.utils.DBMS;
/**
* Abstract class providing the API for all the DBMS dependent functionalities needed by the
* infrastructure.
*
* <p>
* Furthermore, it provides the basic implementation for all the methods used to define the
* different DB table schema. Concrete implementation of this class can override the default
* implementation if needed (e.g., for different SQL dialect issues).
*
* <p>
* Each table creator method assume that the statement has been created.
*
* <p>
* WARNING: When/If overriding these methods, ensure that:
* <ul>
* <li>all TEXT are CASE SENSITIVE.
* <li>all VARCHAR are CASE SENSITIVE.
* </ul>
*
* <p>
* Normal table creator utilization protocol:
* <ul>
* <li>createTableStatement();
* <li>...
* <li>initTable1()
* <li>initTable2()
* <li>...
* <li>closeTableStatement();
* </ul>
*
* @author "Generoso Pagano <generoso.pagano@inria.fr>"
*/
public abstract class DBManager {
/** Debug connections */
private static boolean DBG = false;
protected static Map<String, Integer> realopen = new HashMap<String, Integer>();
protected static void incOpen(String s) {
if (!DBG)
return;
if (!realopen.containsKey(s))
realopen.put(s, 0);
realopen.put(s, realopen.get(s) + 1);
logger.debug("Open: {}, {}", s, realopen.get(s));
if (realopen.get(s) == 0)
realopen.remove(s);
}
protected static void decOpen(String s) {
if (!DBG)
return;
if (!realopen.containsKey(s))
realopen.put(s, 0);
realopen.put(s, realopen.get(s) - 1);
logger.debug("Open: {}, {}", s, realopen.get(s));
if (realopen.get(s) == 0)
realopen.remove(s);
}
/**
* Logger
*/
protected static final Logger logger = LoggerFactory.getLogger(DBManager.class);
/**
* Index flags
*/
protected final boolean TABLE_EVENT_PARAM_COLUMN_EVENT_ID_INDEX = false;
protected final boolean TABLE_EVENT_COLUMN_TIMESTAMP_INDEX = false;
/**
* DB name
*/
protected String dbName;
/**
* Connection with the DB
*/
protected Connection connection;
/**
* Statement used to create DB tables
*/
protected Statement tableStatement;
/**
* Constructor.
*
* @param dbName
* DB name
* @throws SoCTraceException
*/
public DBManager(String dbName) throws SoCTraceException {
this.dbName = dbName;
logger.debug("Creating DBManager for DB: {}", dbName);
}
/*
* Methods to implement (DBMS specific)
*/
/**
* Create a new connection
*/
protected abstract void createConnection() throws SoCTraceException;
/**
* Check if the DB is existing.
*
* @return true if the DB exists
* @throws SoCTraceException
*/
public abstract boolean isDBExisting() throws SoCTraceException;
/**
* Check if the DB settings are correct
*
* @return true if they are correct
* @throws SoCTraceException
*/
public abstract boolean checkSettings() throws SoCTraceException;
/**
* Create the DB.
*
* @return the connection
* @throws SoCTraceException
*/
public abstract Connection createDB() throws SoCTraceException;
/**
* Open the DB connection
*
* @return the connection
* @throws SoCTraceException
*/
public abstract Connection openConnection() throws SoCTraceException;
/**
* Drop the DB and close the connection.
*
* @throws SoCTraceException
*/
public abstract void dropDB() throws SoCTraceException;
/**
* Export (dump) the DB on the given file.
*
* @param path
* @throws SoCTraceException
*/
public abstract void exportDB(String path) throws SoCTraceException;
/**
* Import the DB from the given file. The assumption is that the DB name is not already present
* among SoC-Trace DBs. The database is correctly added to SoC-Trace databases, but the SystemDB
* is not modified.
*
* @param path
* @throws SoCTraceException
*/
public abstract void importDB(String path) throws SoCTraceException;
/**
* Create an index on this database.
*
* @param table
* table name
* @param column
* column name
* @param name
* index name
* @throws SoCTraceException
*/
public abstract void createIndex(String table, String column, String name)
throws SoCTraceException;
/**
* Drop the given index.
*
* @param table
* table name
* @param name
* index name
* @throws SoCTraceException
*/
public abstract void dropIndex(String table, String name) throws SoCTraceException;
/*
* Common methods
*/
/**
* Static factory for the concrete DB manager. The DBMS used is read from the configuration
* file.
*
* @param name
* db name
* @return a concrete DB manager object
*/
public static DBManager getDBManager(String name) {
try {
// create DB manager and open the connection
String dbms = Configuration.getInstance().get(SoCTraceProperty.soctrace_dbms);
if (dbms.equals(DBMS.SQLITE.toString())) {
return new SQLiteDBManager(name);
} else if (dbms.equalsIgnoreCase(DBMS.MYSQL.toString())) {
return new MySqlDBManager(name);
}
} catch (SoCTraceException e) {
e.printStackTrace();
}
return null;
}
/**
* DB name getter
*
* @return the DB name
*/
public String getDBName() {
return dbName;
}
/**
* Connection getter. The connection is lazy initialized.
*
* @return the connection
* @throws SoCTraceException
*/
public Connection getConnection() throws SoCTraceException {
try {
if (connection == null || connection.isClosed()) {
createConnection();
}
} catch (SQLException e) {
throw new SoCTraceException();
}
return connection;
}
/**
* Close the connection
*
* @throws SQLException
*/
public void closeConnection() throws SoCTraceException {
try {
if (connection != null && !connection.isClosed()) {
decOpen(dbName);
connection.close();
}
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
/**
* Create the statement used to initialize tables. This method must be called before any of the
* table creator method.
*
* @throws SoCTraceException
*/
public void createTableStatement() throws SoCTraceException {
try {
tableStatement = getConnection().createStatement();
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
/**
* Close the table creation statement. This method should be called at the end of tables
* creation.
*
* @throws SQLException
*/
public void closeTableStatement() throws SoCTraceException {
try {
if (tableStatement != null) {
tableStatement.close();
tableStatement = null;
}
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
/*
* Default table creators: override them if necessary, following the rules explained in the
* documentation of this class.
*/
public void initTrace() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS + FramesocTable.TRACE
+ "(ID INTEGER PRIMARY KEY, " + "TRACE_TYPE_ID INTEGER, "
+ "TRACING_DATE TIMESTAMP, " + "TRACED_APPLICATION TEXT, " + "BOARD TEXT, "
+ "OPERATING_SYSTEM TEXT, " + "NUMBER_OF_CPUS INTEGER, "
+ "NUMBER_OF_EVENTS INTEGER, " + "OUTPUT_DEVICE TEXT," + "DESCRIPTION TEXT, "
+ "PROCESSED BOOLEAN, " + "TRACE_DB_NAME TEXT, " + "ALIAS TEXT, "
+ "MIN_TIMESTAMP BIGINT, " + "MAX_TIMESTAMP BIGINT, " + "TIMEUNIT INTEGER, "
+ "NUMBER_OF_PRODUCERS INTEGER) ");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initTraceType() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.TRACE_TYPE + "(ID INTEGER PRIMARY KEY, "
+ "NAME VARCHAR(128) UNIQUE)");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initTraceParam() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.TRACE_PARAM + "(ID INTEGER PRIMARY KEY, "
+ "TRACE_ID INTEGER, " + "TRACE_PARAM_TYPE_ID INTEGER, " + "VALUE TEXT )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initTraceParamType() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.TRACE_PARAM_TYPE + "(ID INTEGER PRIMARY KEY, "
+ "TRACE_TYPE_ID INTEGER, " + "NAME VARCHAR(128), " + "TYPE TEXT, "
+ "CONSTRAINT uc_type UNIQUE(TRACE_TYPE_ID, NAME) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initTool() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS + FramesocTable.TOOL
+ "(ID INTEGER PRIMARY KEY, " + "NAME VARCHAR(128) UNIQUE, " + "TYPE TEXT, "
+ "COMMAND TEXT, " + "IS_PLUGIN BOOLEAN, " + "DOC TEXT, "
+ "EXTENSION_ID TEXT)");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initEvent() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS + FramesocTable.EVENT
+ "(ID INTEGER PRIMARY KEY, " + "EVENT_TYPE_ID INTEGER, "
+ "EVENT_PRODUCER_ID INTEGER, " + "TIMESTAMP BIGINT, " + "CPU INTEGER, "
+ "PAGE INTEGER, " + "CATEGORY INTEGER, " + "LPAR BIGINT, " + "DPAR DOUBLE )");
if (TABLE_EVENT_COLUMN_TIMESTAMP_INDEX)
tableStatement.execute("CREATE INDEX timestamp_index ON " + FramesocTable.EVENT
+ " (TIMESTAMP)");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initEventType() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.EVENT_TYPE + "(ID INTEGER PRIMARY KEY, " + "CATEGORY INTEGER, "
+ "NAME VARCHAR(128) UNIQUE)");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initEventParam() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.EVENT_PARAM + "(ID INTEGER PRIMARY KEY, "
+ "EVENT_ID INTEGER, " + "EVENT_PARAM_TYPE_ID INTEGER, " + "VALUE TEXT )");
if (TABLE_EVENT_PARAM_COLUMN_EVENT_ID_INDEX)
tableStatement.execute("CREATE INDEX event_id_index ON "
+ FramesocTable.EVENT_PARAM + " (EVENT_ID)");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initEventParamType() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.EVENT_PARAM_TYPE + "(ID INTEGER PRIMARY KEY, "
+ "EVENT_TYPE_ID INTEGER, " + "NAME VARCHAR(128), " + "TYPE TEXT,"
+ "CONSTRAINT uc_type UNIQUE(EVENT_TYPE_ID, NAME) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initEventProducer() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.EVENT_PRODUCER + "(ID INTEGER PRIMARY KEY, "
+ "TYPE VARCHAR(128), " + "LOCAL_ID VARCHAR(128), " + "NAME TEXT, "
+ "PARENT_ID INTEGER," + "CONSTRAINT uc_type UNIQUE(TYPE, LOCAL_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initFile() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS + FramesocTable.FILE
+ "(ID INTEGER PRIMARY KEY, " + "PATH TEXT, " + "DESCRIPTION TEXT )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initAnalysisResult() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.ANALYSIS_RESULT + "(ID INTEGER PRIMARY KEY, "
+ "TOOL_ID INTEGER, " + "TYPE TEXT, " + "DATE TIMESTAMP, "
+ "DESCRIPTION TEXT )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initAnnotation() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.ANNOTATION + "(ANALYSIS_RESULT_ID INTEGER, "
+ "ANNOTATION_ID INTEGER, " + "ANNOTATION_TYPE_ID INTEGER, " + "NAME TEXT, "
+ "PRIMARY KEY (ANALYSIS_RESULT_ID, ANNOTATION_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initAnnotationType() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.ANNOTATION_TYPE + "(ANALYSIS_RESULT_ID INTEGER, "
+ "ANNOTATION_TYPE_ID INTEGER, " + "NAME VARCHAR(128),"
+ "PRIMARY KEY (ANALYSIS_RESULT_ID, ANNOTATION_TYPE_ID),"
+ "CONSTRAINT uc_type UNIQUE(ANALYSIS_RESULT_ID, NAME) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initAnnotationParam() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.ANNOTATION_PARAM + "(ANALYSIS_RESULT_ID INTEGER, "
+ "ANNOTATION_PARAM_ID INTEGER, " + "ANNOTATION_ID INTEGER, "
+ "ANNOTATION_PARAM_TYPE_ID INTEGER, " + "VALUE TEXT, "
+ "PRIMARY KEY (ANALYSIS_RESULT_ID, ANNOTATION_PARAM_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initAnnotationParamType() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.ANNOTATION_PARAM_TYPE + "(ANALYSIS_RESULT_ID INTEGER, "
+ "ANNOTATION_PARAM_TYPE_ID INTEGER, " + "ANNOTATION_TYPE_ID INTEGER, "
+ "NAME VARCHAR(128), " + "TYPE TEXT,"
+ "PRIMARY KEY (ANALYSIS_RESULT_ID, ANNOTATION_PARAM_TYPE_ID),"
+ "CONSTRAINT uc_type UNIQUE(ANALYSIS_RESULT_ID, ANNOTATION_TYPE_ID, NAME) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initGroup() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.ENTITY_GROUP + "(ANALYSIS_RESULT_ID INTEGER, "
+ "GROUP_ID INTEGER, " + "PARENT_GROUP_ID INTEGER, " + "NAME TEXT, "
+ "TARGET_ENTITY TEXT, " + "GROUPING_OPERATOR TEXT, " + "ORDERED BOOLEAN, "
+ "SEQUENCE_NUMBER INTEGER, " + "PRIMARY KEY (ANALYSIS_RESULT_ID, GROUP_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initGroupMapping() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.GROUP_MAPPING + "(ANALYSIS_RESULT_ID INTEGER, "
+ "MAPPING_ID INTEGER, " + "GROUP_ID INTEGER, " + "TARGET_ENTITY_ID INTEGER, "
+ "SEQUENCE_NUMBER INTEGER, "
+ "PRIMARY KEY (ANALYSIS_RESULT_ID, MAPPING_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initSearch() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS + FramesocTable.SEARCH
+ "(ANALYSIS_RESULT_ID INTEGER, " + "SEARCH_COMMAND TEXT, "
+ "TARGET_ENTITY TEXT, " + "PRIMARY KEY (ANALYSIS_RESULT_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initSearchMapping() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.SEARCH_MAPPING + "(ANALYSIS_RESULT_ID INTEGER, "
+ "TARGET_ENTITY_ID INTEGER, "
+ "PRIMARY KEY (ANALYSIS_RESULT_ID, TARGET_ENTITY_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
public void initProcessedTrace() throws SoCTraceException {
try {
tableStatement.execute(SQLConstants.CREATE_TABLE_IF_NOT_EXISTS
+ FramesocTable.PROCESSED_TRACE + "(ANALYSIS_RESULT_ID INTEGER, "
+ "PROCESSED_TRACE_ID INTEGER, " + "PRIMARY KEY (ANALYSIS_RESULT_ID) )");
} catch (SQLException e) {
throw new SoCTraceException(e);
}
}
/**
* Build the query to build in order to get the info
*
* @param framesocTable
* the table from which we want the info
* @return the query
*/
public abstract String getTableInfoQuery(FramesocTable framesocTable);
/**
* Replace a database by another. The old DB will be replace by the old one,
* but will still have the old DB name. Used only for the import form
* updater mechanism
*
* @param oldBDName
* the name of the old bd that will be replace
* @param newDBName
* the name of the new DB that will replace the old one
* @throws SoCTraceException
*/
public abstract void replaceDB(String oldBDName, String newDBName) throws SoCTraceException;
/**
* Set the database model version which is a custom field used by Framesoc to
* identify the model version of the database
*
* @param databaseVersion
* the database version number
* @throws SoCTraceException
*/
public abstract void setDBVersion(int databaseVersion) throws SoCTraceException;
/**
* Get the database version number.
*
* Default value set by SQLite is 0
*
* @return the version number stored in DB
*
* @throws SoCTraceException
*/
public abstract int getDBVersion() throws SoCTraceException;
/**
* Get the the index of the column giving the name of the column in table in
* the table description
*
* @return the index of the column
*/
public abstract int getColumnNameIndex();
}