org.openiot.gsn.storage.StorageManager.java Source code

Java tutorial

Introduction

Here is the source code for org.openiot.gsn.storage.StorageManager.java

Source

/**
*    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;
    }

}