/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT 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, version 3 of the License.
*
* OpenIoT 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 OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
* @author Jerome Rousselot
* @author Mehdi Riahi
* @author rhietala
* @author gsn_devs
* @author Ali Salehi
* @author Timotee Maret
* @author Mehdi Riahi
* @author Sofiane Sarni
* @author Milos Stojanovic
*/
package org.openiot.gsn.storage;
import org.openiot.gsn.Main;
import org.openiot.gsn.beans.DataField;
import org.openiot.gsn.beans.DataTypes;
import org.openiot.gsn.beans.StreamElement;
import org.openiot.gsn.http.datarequest.AbstractQuery;
import org.openiot.gsn.utils.GSNRuntimeException;
import org.openiot.gsn.utils.ValidityTools;
import java.io.Serializable;
import java.sql.*;
import java.util.ArrayList;
import org.apache.commons.dbcp.*;
import org.apache.log4j.Logger;
public abstract class StorageManager {
private static final transient Logger logger = Logger.getLogger(StorageManager.class);
private String databaseDriver;
private BasicDataSource pool;
public void init(String databaseDriver, String username, String password, String databaseURL, int maxDBConnections) {
this.databaseDriver = databaseDriver;
pool = DataSources.getDataSource(new DBConnectionInfo(databaseDriver,databaseURL,username,password));
pool.setMaxActive(maxDBConnections);
pool.setMaxIdle(maxDBConnections);
pool.setRemoveAbandoned(true); // removing unused connections, used to clean after poorly written code
pool.setRemoveAbandonedTimeout(300); // 5 minutes
//
Connection con = null;
try {
initDatabaseAccess(con = getConnection());
logger.info(new StringBuilder().append("StorageManager DB connection initialized successfuly. driver:").append(databaseDriver).append(" url:").append(databaseURL));
} catch (Exception e) {
logger.error(new StringBuilder().append("Connecting to the database with the following properties failed :").append("\n\t UserName :").append(username).append("\n\t Password : ").append(password).append("\n\t Driver class : ").append(databaseDriver).append("\n\t Database URL : ").append(databaseURL).toString());
logger.error(new StringBuilder().append(e.getMessage()).append(", Please refer to the logs for more detailed information.").toString());
logger.error("Make sure in the gsn.xml file, the <storage ...> element is correct.");
e.printStackTrace();
if (logger.isInfoEnabled())
logger.info(e.getMessage(), e);
} finally {
close(con);
}
}
public void initDatabaseAccess(Connection con) throws Exception {}
public abstract byte convertLocalTypeToGSN(int jdbcType, int precision);
public abstract String getStatementDropIndex();
public abstract String getStatementDropView();
public abstract int getTableNotExistsErrNo();
public abstract String addLimit(String query, int limit, int offset);
public abstract StringBuilder getStatementUselessDataRemoval(String virtualSensorName, long storageSize);
public byte convertLocalTypeToGSN(int jdbcType) {
return convertLocalTypeToGSN(jdbcType, 0);
}
/**
* Returns false if the table doesnt exist. Uses the current default
* connection.
*
* @param tableName
* @return False if the table doesn't exist in the current connection.
* @throws SQLException
*/
public boolean tableExists(CharSequence tableName) throws SQLException {
Connection connection = null;
try {
connection = getConnection();
return tableExists(tableName, new DataField[]{}, connection);
} finally {
close(connection);
}
}
/**
* Checks to see if the given tablename exists using the given connection.
*
* @param tableName
* @param connection
* @return
* @throws SQLException
*/
public boolean tableExists(CharSequence tableName, Connection connection) throws SQLException {
return tableExists(tableName, new DataField[]{}, connection);
}
public abstract StringBuilder getStatementRemoveUselessDataCountBased(String virtualSensorName, long storageSize) ;
public StringBuilder getStatementRemoveUselessDataTimeBased(String virtualSensorName, long storageSize) {
StringBuilder query = null;
long timedToRemove = -1;
Connection conn = null;
try {
ResultSet rs = Main.getStorage(virtualSensorName).executeQueryWithResultSet(new StringBuilder("SELECT MAX(timed) FROM ").append(virtualSensorName), conn = Main.getStorage(virtualSensorName).getConnection());
if (rs.next())
timedToRemove = rs.getLong(1);
} catch (SQLException e) {
logger.error(e.getMessage(), e);
} finally {
Main.getStorage(virtualSensorName).close(conn);
}
query = new StringBuilder().append("delete from ").append(virtualSensorName).append(" where ").append(virtualSensorName).append(".timed < ").append(timedToRemove);
query.append(" - ").append(storageSize);
return query;
}
public DataField[] tableToStructure(CharSequence tableName, Connection connection) throws SQLException {
StringBuilder sb = new StringBuilder("select * from ").append(tableName).append(" where 1=0 ");
ResultSet rs = null;
DataField[] toReturn = null;
try {
rs = executeQueryWithResultSet(sb, connection);
ResultSetMetaData structure = rs.getMetaData();
ArrayList<DataField> toReturnArr = new ArrayList<DataField>();
for (int i = 1; i <= structure.getColumnCount(); i++) {
String colName = structure.getColumnLabel(i);
if (colName.equalsIgnoreCase("pk")) continue;
int colType = structure.getColumnType(i);
byte colTypeInGSN = convertLocalTypeToGSN(colType);
toReturnArr.add(new DataField(colName, colTypeInGSN));
}
toReturn = toReturnArr.toArray(new DataField[]{});
} finally {
if (rs != null)
close(rs);
}
return toReturn;
}
/*
* Alternative method to 'tableToStructure'
* Useful for correctly creating structure for fields with variable length (like char, varchar, binary, blob)
* */
public DataField[] tableToStructureByString(String tableName, Connection connection) throws SQLException {
StringBuilder sb = new StringBuilder("select * from ").append(tableName).append(" where 1=0 ");
ResultSet rs = null;
DataField[] toReturn = null;
try {
rs = executeQueryWithResultSet(sb, connection);
ResultSetMetaData structure = rs.getMetaData();
ArrayList<DataField> toReturnArr = new ArrayList<DataField>();
for (int i = 1; i <= structure.getColumnCount(); i++) {
String colName = structure.getColumnLabel(i);
if (colName.equalsIgnoreCase("pk")) continue;
if (colName.equalsIgnoreCase("timed")) continue;
int colType = structure.getColumnType(i);
String colTypeName = structure.getColumnTypeName(i);
int precision = structure.getPrecision(i);
byte colTypeInGSN = convertLocalTypeToGSN(colType);
if ((colTypeInGSN == DataTypes.VARCHAR) || (colTypeInGSN == DataTypes.CHAR))
toReturnArr.add(new DataField(colName, colTypeName, precision, colName));
else
toReturnArr.add(new DataField(colName, colTypeInGSN));
}
toReturn = toReturnArr.toArray(new DataField[]{});
} finally {
if (rs != null)
close(rs);
}
return toReturn;
}
/**
* Returns false if the table doesnt exist. If the table exists but the
* structure is not compatible with the specified fields the method throws
* GSNRuntimeException. Note that this method doesn't close the connection
*
* @param tableName
* @param connection (this method will not close it and the caller is responsible
* for closing the connection)
* @return
* @throws SQLException
* @Throws GSNRuntimeException
*/
public boolean tableExists(CharSequence tableName, DataField[] fields, Connection connection) throws SQLException,
GSNRuntimeException {
if (!ValidityTools.isValidJavaVariable(tableName))
throw new GSNRuntimeException("Table name is not valid");
StringBuilder sb = new StringBuilder("select * from ").append(tableNameGeneratorInString(tableName)).append(" where 1=0 ");
ResultSet rs = null;
try {
rs = executeQueryWithResultSet(sb, connection);
ResultSetMetaData structure = rs.getMetaData();
if (fields != null && fields.length > 0)
nextField:for (DataField field : fields) {
for (int i = 1; i <= structure.getColumnCount(); i++) {
String colName = structure.getColumnLabel(i);
int colType = structure.getColumnType(i);
int colTypeScale = structure.getScale(i);
if (field.getName().equalsIgnoreCase(colName))
if (field.getDataTypeID() == convertLocalTypeToGSN(colType, colTypeScale))
continue nextField;
else
throw new GSNRuntimeException("The column : "
+ colName + " in the >" + tableName
+ "< table is not compatible with type : "
+ field.getType()
+ ". The actual type for this table (currently in the database): " + colType);
}
throw new GSNRuntimeException("The table " + tableName
+ " in the database, doesn't have the >" + field.getName()
+ "< column.");
}
} catch (SQLException e) {
if (e.getErrorCode() == getTableNotExistsErrNo() || e.getMessage().contains("does not exist"))
return false;
else {
logger.error(e.getErrorCode());
throw e;
}
} finally {
close(rs);
}
return true;
}
public boolean tableExists(CharSequence tableName, DataField[] fields)
throws SQLException {
Connection conn = null;
boolean to_return = true;
try {
conn = getConnection();
to_return = tableExists(tableName, fields, conn);
} finally {
close(conn);
}
return to_return;
}
/**
* Returns true if the specified query has any result in it's result set.
* The created result set will be closed automatically.
*
* @param sqlQuery
* @return
*/
public boolean isThereAnyResult(StringBuilder sqlQuery) {
boolean toreturn = false;
Connection connection = null;
try {
connection = getConnection();
PreparedStatement prepareStatement = connection.prepareStatement(
sqlQuery.toString());
ResultSet resultSet = prepareStatement.executeQuery();
toreturn = resultSet.next();
} catch (SQLException error) {
logger.error(error.getMessage(), error);
} finally {
close(connection);
}
return toreturn;
}
/**
* Executes the query of the database. Returns the specified colIndex of the
* first row. Useful for image recovery of the web interface.
*
* @param query The query to be executed.
* @return A resultset with only one row and one column. The user of the
* method should first call next on the result set to make sure that
* the row is there and then retrieve the value for the row.
* @throws SQLException
*/
public ResultSet getBinaryFieldByQuery(StringBuilder query,
String colName, long pk, Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(query.toString());
ps.setLong(1, pk);
return ps.executeQuery();
}
public void closeStatement(Statement stmt) {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
logger.error(e.getMessage(), e);
}
}
public void close(ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
logger.error(e.getMessage(), e);
}
}
public void close(PreparedStatement preparedStatement) {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
} catch (SQLException e) {
logger.error(e.getMessage(), e);
}
}
public void close(Connection conn) {
try {
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
logger.debug(e.getMessage(), e);
}
}
/**
* @throws SQLException
*/
public void shutdown() throws SQLException {
logger.warn("Closing the connection pool [done].");
}
/**
* ************************************************************************
* Various Statement Executors.
* ************************************************************************
*/
public void executeRenameTable(String oldName, String newName)
throws SQLException {
Connection conn = null;
try {
conn = getConnection();
executeRenameTable(oldName, newName, conn);
} finally {
close(conn);
}
}
public void executeRenameTable(String oldName, String newName, Connection connection) throws SQLException {
PreparedStatement prepareStatement = null;
try {
prepareStatement = connection.prepareStatement(getStatementRenameTable(oldName, newName));
prepareStatement.execute();
} finally {
close(prepareStatement);
}
}
public void executeDropTable(CharSequence tableName) throws SQLException {
Connection conn = null;
try {
conn = getConnection();
executeDropTable(tableName, conn);
} finally {
close(conn);
}
}
public void executeDropTable(CharSequence tableName, Connection connection) {
PreparedStatement prepareStatement = null;
try {
String stmt = getStatementDropTable(tableName, connection).toString();
if (logger.isDebugEnabled())
logger.debug("Dropping table structure: " + tableName + " With query: " + stmt);
prepareStatement = connection.prepareStatement(stmt);
prepareStatement.execute();
} catch (SQLException e) {
logger.info(e.getMessage(), e);
}
}
public void executeDropView(StringBuilder tableName) throws SQLException {
Connection conn = null;
try {
conn = getConnection();
executeDropView(tableName, conn);
} finally {
close(conn);
}
}
public void executeDropView(StringBuilder tableName, Connection connection) throws SQLException {
if (logger.isDebugEnabled())
logger.debug("Dropping table structure: " + tableName);
PreparedStatement prepareStatement = connection.prepareStatement(getStatementDropView(tableName, connection).toString());
prepareStatement.execute();
close(prepareStatement);
}
public void executeCreateTable(CharSequence tableName, DataField[] structure, boolean unique) throws SQLException {
Connection conn = null;
try {
conn = getConnection();
executeCreateTable(tableName, structure, unique, conn);
} finally {
close(conn);
}
}
/**
* Create a table with a index on the timed field.
*
* @param tableName
* @param structure
* @param unique , setting this true cause the system to create a unique index on time.
* @param connection
* @throws SQLException
*/
public void executeCreateTable(CharSequence tableName, DataField[] structure, boolean unique, Connection connection) throws SQLException {
StringBuilder sql = getStatementCreateTable(tableName, structure, connection);
if (logger.isDebugEnabled())
logger.debug(new StringBuilder().append("The create table statement is : ").append(sql).toString());
PreparedStatement prepareStatement = connection.prepareStatement(sql.toString());
prepareStatement.execute();
prepareStatement.close();
sql = getStatementCreateIndexOnTimed(tableName, unique);
if (logger.isDebugEnabled())
logger.debug(new StringBuilder().append(
"The create index statement is : ").append(sql).toString());
prepareStatement = connection.prepareStatement(sql.toString());
prepareStatement.execute();
}
public ResultSet executeQueryWithResultSet(StringBuilder query,Connection connection) throws SQLException {
return connection.prepareStatement(query.toString()).executeQuery();
}
public ResultSet executeQueryWithResultSet(AbstractQuery abstractQuery, Connection c) throws SQLException {
if (abstractQuery.getLimitCriterion() == null) {
return executeQueryWithResultSet(abstractQuery.getStandardQuery(), c);
}
String query = addLimit(abstractQuery.getStandardQuery().toString(), abstractQuery.getLimitCriterion().getSize(), abstractQuery.getLimitCriterion().getOffset());
return executeQueryWithResultSet(new StringBuilder(query), c);
}
public DataEnumerator executeQuery(StringBuilder query, boolean binaryFieldsLinked, Connection connection) throws SQLException {
if (logger.isDebugEnabled())
logger.debug("Executing query: " + query + "( Binary Field Linked:" + binaryFieldsLinked + ")");
return new DataEnumerator(this, connection.prepareStatement(query.toString()), binaryFieldsLinked);
}
/**
* Attention: Caller should close the connection.
*
* @param abstractQuery
* @param binaryFieldsLinked
* @param connection
* @return
* @throws SQLException
*/
public DataEnumerator executeQuery(AbstractQuery abstractQuery, boolean binaryFieldsLinked, Connection connection) throws SQLException {
if (abstractQuery.getLimitCriterion() == null) {
return executeQuery(abstractQuery.getStandardQuery(), binaryFieldsLinked, connection);
}
String query = addLimit(abstractQuery.getStandardQuery().toString(), abstractQuery.getLimitCriterion().getSize(), abstractQuery.getLimitCriterion().getOffset());
if (logger.isDebugEnabled())
logger.debug("Executing query: " + query + "(" + binaryFieldsLinked + ")");
return new DataEnumerator(this, connection.prepareStatement(query.toString()), binaryFieldsLinked);
}
public DataEnumerator streamedExecuteQuery(AbstractQuery abstractQuery, boolean binaryFieldsLinked, Connection connection) throws SQLException {
if (abstractQuery.getLimitCriterion() == null) {
return streamedExecuteQuery(abstractQuery.getStandardQuery().toString(), binaryFieldsLinked, connection);
}
String query = addLimit(abstractQuery.getStandardQuery().toString(), abstractQuery.getLimitCriterion().getSize(), abstractQuery.getLimitCriterion().getOffset());
if (logger.isDebugEnabled())
logger.debug("Executing query: " + query + "(" + binaryFieldsLinked + ")");
return streamedExecuteQuery(query, binaryFieldsLinked, connection);
}
public DataEnumerator executeQuery(StringBuilder query, boolean binaryFieldsLinked) throws SQLException {
return executeQuery(query, binaryFieldsLinked, getConnection());
}
public DataEnumerator streamedExecuteQuery(String query, boolean binaryFieldsLinked, Connection conn) throws SQLException {
return new DataEnumerator(this, conn.prepareStatement(query), binaryFieldsLinked);
}
public DataEnumerator streamedExecuteQuery(String query, boolean binaryFieldsLinked) throws SQLException {
return streamedExecuteQuery(query, binaryFieldsLinked, getConnection());
}
public void executeCreateView(CharSequence viewName, CharSequence selectQuery) throws SQLException {
Connection connection = null;
try {
connection = getConnection();
executeCreateView(viewName, selectQuery, connection);
} finally {
close(connection);
}
}
public void executeCreateView(CharSequence viewName, CharSequence selectQuery, Connection connection) throws SQLException {
StringBuilder statement = getStatementCreateView(viewName, selectQuery);
if (logger.isDebugEnabled())
logger.debug("Creating a view:" + statement);
final PreparedStatement prepareStatement = connection.prepareStatement(statement.toString());
prepareStatement.execute();
close(prepareStatement);
}
/**
* This method executes the provided statement over the connection. If there
* is an error retruns -1 otherwise it returns the output of the
* executeUpdate method on the PreparedStatement class which reflects the
* number of changed rows in the underlying table.
*
* @param sql
* @param connection
* @return Number of effected rows or -1 if there is an error.
*/
public void executeCommand(String sql, Connection connection) {
Statement stmt = null;
try {
stmt = connection.createStatement();
stmt.execute(sql);
} catch (SQLException error) {
logger.error(error.getMessage() + " FOR: " + sql, error);
} finally {
try {
if (stmt != null && !stmt.isClosed())
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public int executeUpdate(String updateStatement, Connection connection) {
int toReturn = -1;
//PreparedStatement prepareStatement = null;
try {
//prepareStatement = connection.prepareStatement(updateStatement);
//toReturn = prepareStatement.executeUpdate();
toReturn = connection.createStatement().executeUpdate(updateStatement);
} catch (SQLException error) {
logger.error(error.getMessage(), error);
}
return toReturn;
}
public int executeUpdate(StringBuilder updateStatement, Connection connection) {
int to_return = -1;
to_return = executeUpdate(updateStatement.toString(), connection);
return to_return;
}
public int executeUpdate(StringBuilder updateStatement) throws SQLException {
Connection connection = null;
try {
connection = getConnection();
return executeUpdate(updateStatement, connection);
} finally {
close(connection);
}
}
public void executeInsert(CharSequence tableName, DataField[] fields, StreamElement se) throws SQLException {
Connection connection = null;
try {
connection = getConnection();
executeInsert(tableName, fields, se, connection);
} finally {
close(connection);
}
}
public void executeInsert(CharSequence tableName, DataField[] fields, StreamElement streamElement, Connection connection) throws SQLException {
PreparedStatement ps = null;
String query = getStatementInsert(tableName, fields).toString();
try {
ps = connection.prepareStatement(query);
int counter = 1;
for (DataField dataField : fields) {
if (dataField.getName().equalsIgnoreCase("timed"))
continue;
Serializable value = streamElement.getData(dataField.getName());
switch (dataField.getDataTypeID()) {
case DataTypes.VARCHAR:
if (value == null)
ps.setNull(counter, Types.VARCHAR);
else
ps.setString(counter, value.toString());
break;
case DataTypes.CHAR:
if (value == null)
ps.setNull(counter, Types.CHAR);
else
ps.setString(counter, value.toString());
break;
case DataTypes.INTEGER:
if (value == null)
ps.setNull(counter, Types.INTEGER);
else
ps.setInt(counter, ((Number) value).intValue());
break;
case DataTypes.SMALLINT:
if (value == null)
ps.setNull(counter, Types.SMALLINT);
else
ps.setShort(counter, ((Number) value).shortValue());
break;
case DataTypes.TINYINT:
if (value == null)
ps.setNull(counter, Types.TINYINT);
else
ps.setByte(counter, ((Number) value).byteValue());
break;
case DataTypes.DOUBLE:
if (value == null)
ps.setNull(counter, Types.DOUBLE);
else
ps.setDouble(counter, ((Number) value).doubleValue());
break;
case DataTypes.BIGINT:
if (value == null)
ps.setNull(counter, Types.BIGINT);
else
ps.setLong(counter, ((Number) value).longValue());
break;
case DataTypes.BINARY:
if (value == null)
ps.setNull(counter, Types.BINARY);
else
ps.setBytes(counter, (byte[]) value);
break;
default:
logger.error("The type conversion is not supported for : "
+ dataField.getName() + "("
+ dataField.getDataTypeID() + ") : ");
}
counter++;
}
ps.setLong(counter, streamElement.getTimeStamp());
ps.execute();
} catch (GSNRuntimeException e) {
//if (e.getType() == GSNRuntimeException.UNEXPECTED_VIRTUAL_SENSOR_REMOVAL) {
// if (logger.isDebugEnabled())
// logger.debug("An stream element dropped due to unexpected virtual sensor removal. (Stream element: " + streamElement.toString() + ")+ Query: " + query, e);
//} else
logger.warn("Inserting a stream element failed : "
+ streamElement.toString(), e);
} catch (SQLException e) {
if (e.getMessage().toLowerCase().contains("duplicate entry"))
logger.info("Error occurred on inserting data to the database, an stream element dropped due to: " + e.getMessage() + ". (Stream element: " + streamElement.toString() + ")+ Query: " + query);
else
logger.warn("Error occurred on inserting data to the database, an stream element dropped due to: " + e.getMessage() + ". (Stream element: " + streamElement.toString() + ")+ Query: " + query);
throw e;
}
finally {
close(ps);
}
}
/***************************************************************************
* Statement Generators
**************************************************************************/
/**
* Creates a sql statement which can be used for inserting the specified
* stream element in to the specified table.
*
* @param tableName The table which the generated sql will pointing to.
* @param fields The stream element for which the sql statement is generated.
* @return A sql statement which can be used for inserting the provided
* stream element into the specified table.
*/
public StringBuilder getStatementInsert(CharSequence tableName, DataField fields[]) {
StringBuilder toReturn = new StringBuilder("insert into ").append(tableName).append(" ( ");
int numberOfQuestionMarks = 1; //Timed is always there.
for (DataField dataField : fields) {
if (dataField.getName().equalsIgnoreCase("timed"))
continue;
numberOfQuestionMarks++;
toReturn.append(dataField.getName()).append(" ,");
}
toReturn.append(" timed ").append(" ) values (");
for (int i = 1; i <= numberOfQuestionMarks; i++)
toReturn.append("?,");
toReturn.deleteCharAt(toReturn.length() - 1);
toReturn.append(")");
return toReturn;
}
public String getStatementRenameTable(String oldName, String newName) {
return new StringBuilder("alter table ").append(oldName).append(" rename to ").append(newName).toString();
}
public abstract StringBuilder getStatementDropTable(CharSequence tableName, Connection conn) throws SQLException ;
/**
* First detects the appropriate DB Engine to use. Get's the drop index
* statement syntax (which is DB dependent) and executes it.
*
* @param indexName
* @param connection
* @return
* @throws SQLException
*/
public StringBuilder getStatementDropIndex(CharSequence indexName, CharSequence tableName,Connection connection) throws SQLException {
return new StringBuilder(getStatementDropIndex().replace("#NAME",indexName).replace("#TABLE", tableName));
}
public StringBuilder getStatementDropView(CharSequence viewName,Connection connection) throws SQLException {
return new StringBuilder(getStatementDropView().replace("#NAME",viewName));
}
public StringBuilder getStatementCreateIndexOnTimed(
CharSequence tableName, boolean unique) throws SQLException {
StringBuilder toReturn = new StringBuilder("CREATE ");
if (unique)
toReturn.append(" UNIQUE ");
toReturn.append(" INDEX ").append(tableNamePostFixAppender(tableName, "_INDEX")).append(" ON ").append(tableName).append(" (timed DESC)");
return toReturn;
}
public StringBuilder getStatementCreateTable(CharSequence tableName, DataField[] structure, Connection connection) throws SQLException {
return getStatementCreateTable(tableName.toString(), structure);
}
public abstract StringBuilder getStatementCreateTable(String tableName, DataField[] structure);
public StringBuilder getStatementCreateView(CharSequence viewName,CharSequence selectQuery) {
return new StringBuilder("create view ").append(viewName).append(" AS ( ").append(selectQuery).append(" ) ");
}
private String driver = null;
/**
* The prefix is in lower case
*
* @return
*/
public abstract String getJDBCPrefix();
public String getJDBCDriverClass() {
return databaseDriver;
}
/*
* Converts from internal GSN data types to a supported DB data type.
* @param field The DataField to be converted @return convertedType The
* datatype name used by the target database.
*/
public abstract String convertGSNTypeToLocalType(DataField gsnType);
/**
* Obtains the default database connection.
* The conneciton comes from the data source which is configured through gsn.xml file.
* @return
* @throws SQLException
*/
public Connection getConnection() throws SQLException {
if (logger.isDebugEnabled())
logger.debug(new StringBuilder("Asking a con. to DB: ")
.append(pool.getUrl())
.append(" => busy: ")
.append(pool.getNumActive())
.append(", max-size: ")
.append(pool.getMaxActive())
.append(", idle: ")
.append(pool.getNumIdle()));
return pool.getConnection();
}
/**
* Retruns an approximation of the difference between the current time of the DB and that of the local system
*
* @return
*/
public long getTimeDifferenceInMillis() {
String query = getStatementDifferenceTimeInMillis();
Connection connection = null;
try {
connection = getConnection();
PreparedStatement prepareStatement = connection.prepareStatement(query);
long time1 = System.currentTimeMillis();
ResultSet resultSet;
resultSet = prepareStatement.executeQuery();
resultSet.next();
long time2 = System.currentTimeMillis();
return resultSet.getLong(1) - time2 + (time2 - time1) / 2;
} catch (SQLException error) {
logger.error(error.getMessage(), error);
} finally {
close(connection);
}
return 0;
}
public abstract String getStatementDifferenceTimeInMillis();
public ArrayList<String> getInternalTables() throws SQLException {
return new ArrayList<String>();
}
//
public String randomTableNameGenerator ( int length ) {
byte oneCharacter;
StringBuffer result = new StringBuffer ( length );
for ( int i = 0 ; i < length ; i++ ) {
oneCharacter = ( byte ) ( ( Math.random ( ) * ( 'z' - 'a' + 1 ) ) + 'a' );
result.append ( ( char ) oneCharacter );
}
return result.toString ( );
}
public int tableNameGenerator ( ) {
return randomTableNameGenerator ( 15 ).hashCode ( );
}
public StringBuilder tableNameGeneratorInString (CharSequence tableName) {
return new StringBuilder(tableName);
}
public StringBuilder tableNameGeneratorInString (int code) {
StringBuilder sb = new StringBuilder ("_");
if (code<0)
sb.append ( "_" );
sb.append ( Math.abs (code) );
return tableNameGeneratorInString(sb);
}
public String tableNamePostFixAppender(CharSequence table_name,String postFix) {
String tableName = table_name.toString();
if (tableName.endsWith("\""))
return (tableName.substring(0, tableName.length()-2))+postFix+"\"";
else
return tableName+postFix;
}
// deprecated
protected boolean isH2 ;
protected boolean isMysql ;
protected boolean isOracle ;
protected boolean isSqlServer ;
protected boolean isPostgres ;
public boolean isOracle(){ return isOracle; }
public boolean isH2(){ return isH2; }
public boolean isSqlServer(){ return isSqlServer; }
public boolean isMysqlDB(){ return isMysql; }
public boolean isPostgres(){ return isPostgres; }
}