streamcruncher.innards.db.DatabaseInterface.java Source code

Java tutorial

Introduction

Here is the source code for streamcruncher.innards.db.DatabaseInterface.java

Source

/*
 * StreamCruncher:  Copyright (c) 2006-2008, Ashwin Jayaprakash. All Rights Reserved.
 * Contact:         ashwin {dot} jayaprakash {at} gmail {dot} com
 * Web:             http://www.StreamCruncher.com
 * 
 * This file is part of StreamCruncher.
 * 
 *     StreamCruncher 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 3 of the License, or
 *     (at your option) any later version.
 * 
 *     StreamCruncher 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 StreamCruncher. If not, see <http://www.gnu.org/licenses/>.
 */
package streamcruncher.innards.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.dbcp.BasicDataSource;

import streamcruncher.api.DBName;
import streamcruncher.api.artifact.IndexSpec;
import streamcruncher.api.artifact.MiscSpec;
import streamcruncher.api.artifact.RowSpec;
import streamcruncher.api.artifact.TableSpec;
import streamcruncher.boot.Component;
import streamcruncher.boot.ConfigKeys;
import streamcruncher.boot.Registry;
import streamcruncher.innards.core.partition.aggregate.AbstractAggregatedColumnDDLHelper;
import streamcruncher.innards.impl.query.DDLHelper;
import streamcruncher.innards.query.Parser;
import streamcruncher.innards.util.Helper;
import streamcruncher.util.AtomicX;
import streamcruncher.util.LoggerManager;
import streamcruncher.util.sysevent.SystemEvent;
import streamcruncher.util.sysevent.SystemEventBus;
import streamcruncher.util.sysevent.SystemEvent.Priority;

/*
 * Author: Ashwin Jayaprakash Date: Jan 2, 2006 Time: 9:40:59 AM
 */

public abstract class DatabaseInterface implements Component {
    protected Properties properties;

    protected BasicDataSource dataSource;

    protected String schema;

    protected boolean preservesArtifactsOnShutdown;

    protected boolean privateVolatileInstance;

    /**
     * Auto-commit is <code>false</code>.
     */
    protected Connection sentinelConnection;

    protected Timer sentinelConnectionKeeper;

    // ---------------------

    /**
     * {@inheritDoc} <code>params</code> requires the first parameter to be a
     * {@link java.util.Properties} object loaded with the necessary Database
     * properties.
     */
    public void start(Object... params) throws Exception {
        this.properties = (Properties) params[0];

        String driver = properties.getProperty(ConfigKeys.DB.DRIVER_CLASS_NAME);
        String url = properties.getProperty(ConfigKeys.DB.DRIVER_URL);
        String user = properties.getProperty(ConfigKeys.DB.USER);
        String password = properties.getProperty(ConfigKeys.DB.PASSWORD);

        Class.forName(driver);

        // ----------------------

        schema = properties.getProperty(ConfigKeys.DB.SCHEMA);
        if (schema != null && schema.length() == 0) {
            schema = null;
        }

        String preservesArtifactsStr = properties.getProperty(ConfigKeys.DB.PRESERVES_ARTIFACTS_ON_SHUTDOWN);
        preservesArtifactsOnShutdown = Boolean.parseBoolean(preservesArtifactsStr);

        String maxPoolSizeStr = properties.getProperty(ConfigKeys.DB.CONNECTION_POOL_MAX_SIZE);
        int maxPoolSize = Integer.parseInt(maxPoolSizeStr);

        String privateVolatileInstanceStr = properties.getProperty(ConfigKeys.DB.PRIVATE_VOLATILE_INSTANCE);
        privateVolatileInstance = Boolean.parseBoolean(privateVolatileInstanceStr);

        dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        dataSource.setMaxActive(maxPoolSize);
        dataSource.setMaxIdle(maxPoolSize);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        dataSource.setTimeBetweenEvictionRunsMillis(2 * 60 * 1000);
        dataSource.setMinEvictableIdleTimeMillis(30 * 1000);
        dataSource.setAccessToUnderlyingConnectionAllowed(true);
        dataSource.setPoolPreparedStatements(true);

        setupLastStandingConnection();

        // ----------------------

        Logger logger = Registry.getImplFor(LoggerManager.class).getLogger(DatabaseInterface.class.getName());
        logger.log(Level.INFO, "Started");
    }

    private void setupLastStandingConnection() throws SQLException {
        if (privateVolatileInstance == true) {
            sentinelConnection = createConnection();
            sentinelConnection.setAutoCommit(false);

            sentinelConnectionKeeper = new Timer("SentinelConnectionKeeper", true);

            /*
             * Keep checking periodically and ensure that the sentinelConnection
             * does not timeout.
             */
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    boolean isClosed = false;
                    Throwable error = null;
                    try {
                        isClosed = DatabaseInterface.this.sentinelConnection.isClosed();
                    } catch (SQLException e1) {
                        error = e1;
                    }

                    if (error != null || isClosed) {
                        Logger logger = Registry.getImplFor(LoggerManager.class)
                                .getLogger(DatabaseInterface.class.getName());
                        String msg = "The Private/Volatile Database instance may be at"
                                + " risk, because the sentinel Connection has been lost.";
                        if (error == null) {
                            logger.log(Level.SEVERE, msg);
                        } else {
                            logger.log(Level.SEVERE, msg, error);
                        }

                        SystemEventBus bus = Registry.getImplFor(SystemEventBus.class);
                        SystemEvent event = new SystemEvent(DatabaseInterface.class.getName(),
                                "Sentinel Connection Lost", error, Priority.SEVERE);
                        bus.submit(event);

                        // -----------

                        // Try and create a new one.
                        try {
                            Timer oldTimer = DatabaseInterface.this.sentinelConnectionKeeper;

                            DatabaseInterface.this.setupLastStandingConnection();

                            // Cancel this old timer.
                            oldTimer.cancel();
                        } catch (SQLException e) {
                            logger.log(Level.SEVERE,
                                    "An error occurred while the sentinel" + " Connection was being re-created.",
                                    e);

                            event = new SystemEvent(DatabaseInterface.class.getName(),
                                    "Sentinel Connection Re-creation Error", null, Priority.SEVERE);
                            bus.submit(event);

                            return;
                        }
                    }

                    // ------------

                    try {
                        DatabaseInterface.this.sentinelConnection.commit();
                    } catch (SQLException e) {
                        Logger logger = Registry.getImplFor(LoggerManager.class)
                                .getLogger(DatabaseInterface.class.getName());
                        logger.log(Level.SEVERE,
                                "An error occurred while the sentinel" + " Connection was pinging the Database.",
                                e);

                        SystemEventBus bus = Registry.getImplFor(SystemEventBus.class);
                        SystemEvent event = new SystemEvent(DatabaseInterface.class.getName(),
                                "Sentinel Connection Ping Error", null, Priority.SEVERE);
                        bus.submit(event);
                    }
                }
            };
            sentinelConnectionKeeper.scheduleAtFixedRate(timerTask, 10 * 1000, 30 * 1000);
        }
    }

    public void stop() throws Exception {
        if (privateVolatileInstance == true) {
            sentinelConnectionKeeper.cancel();

            /*
             * Hold on to this until the very end. Otherwise, the In-mem DBs
             * will shutdown.
             */
            Helper.closeConnection(sentinelConnection);
            sentinelConnection = null;
        }

        dataSource.close();
        dataSource = null;

        // ---------------------

        Logger logger = Registry.getImplFor(LoggerManager.class).getLogger(DatabaseInterface.class.getName());
        logger.log(Level.INFO, "Stopped");
    }

    // ---------------------

    public Connection createConnection() throws SQLException {
        return dataSource.getConnection();
    }

    // ---------------------

    public abstract Class<? extends Parser> getParser();

    public abstract DBName getDBName();

    public String getSchema() {
        return schema;
    }

    public abstract AbstractAggregatedColumnDDLHelper getAggregatedColumnDDLHelper();

    public abstract DDLHelper getDDLHelper();

    public TableSpec createUnpartitionedTableSpec(String schema, String name, RowSpec rowSpec,
            IndexSpec[] indexSpecs, MiscSpec[] otherClauses) {
        return new TableSpec(schema, name, rowSpec, indexSpecs, otherClauses);
    }

    public IndexSpec createIndexSpec(String schema, String name, String tableName, boolean unique,
            String columnName, boolean ascending) {
        return new IndexSpec(schema, name, tableName, unique, columnName, ascending);
    }

    public IndexSpec createIndexSpec(String schema, String name, String tableName, boolean unique,
            String[] columnNames, boolean[] ascending) {
        return new IndexSpec(schema, name, tableName, unique, columnNames, ascending);
    }

    public boolean dbPreservesArtifactsOnShutdown() {
        return preservesArtifactsOnShutdown;
    }

    public AtomicX createRowIdGenerator() {
        return new AtomicX(new AtomicLong(Constants.DEFAULT_MONOTONIC_ID_VALUE));
    }

    public ResultSet wrapResultSet(ResultSet resultSet) throws SQLException {
        return resultSet;
    }
}