All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.fascinator.sequences.SequenceService Maven / Gradle / Ivy

The newest version!
/*
 * The Fascinator - Sequence Service
 * Copyright (C) 2008-2010 University of Southern Queensland
 * Copyright (C) 2012 Queensland Cyber Infrastructure Foundation (http://www.qcif.edu.au/)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package com.googlecode.fascinator.sequences;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.googlecode.fascinator.api.access.AccessControlException;
import com.googlecode.fascinator.common.JsonSimple;
import com.googlecode.fascinator.common.JsonSimpleConfig;

@Component(value = "sequenceService")
public class SequenceService {

	/** Logging */
	private final Logger log = LoggerFactory
			.getLogger(SequenceService.class);

	/** JDBC Driver */
	private static String DERBY_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";

	/** Connection string prefix */
	private static String DERBY_PROTOCOL = "jdbc:derby:";

	/** Sequence database name */
	private static String SEQUENCE_DATABASE = "sequence";

	/** Records table */
	private static String SEQUENCE_TABLE = "sequences";


	/** Database home directory */
	private String derbyHome;

	/** Database connection */
	private Connection connection;


	/**
	 * Initialization of Sequences Service
	 * 
	 * @throws IOException 
	 * @throws SQLException 
	 * 
	 */
	@PostConstruct
	public void init() throws IOException, SQLException {
		JsonSimpleConfig config = new JsonSimpleConfig();
		init(config);
	}
	
	public void init(JsonSimple config) throws IOException, SQLException {
		
		// Find data directory
		derbyHome = config.getString(null, "database-service", "derbyHome");
		String oldHome = System.getProperty("derby.system.home");

		// Derby's data directory has already been configured
		if (oldHome != null) {
			if (derbyHome != null) {
				// Use the existing one, but throw a warning
				log.warn("Using previously specified data directory:"
						+ " '{}', provided value has been ignored: '{}'",
						oldHome, derbyHome);
			} else {
				// This is ok, no configuration conflicts
				log.info("Using existing data directory: '{}'", oldHome);
			}

			// We don't have one, config MUST have one
		} else {
			if (derbyHome == null) {
				log.error("No database home directory configured!");
				return;
			} else {
				// Establish its validity and existance, create if necessary
				File file = new File(derbyHome);
				if (file.exists()) {
					if (!file.isDirectory()) {
						throw new IOException("Database home '"
								+ derbyHome + "' is not a directory!");
					}
				} else {
					file.mkdirs();
					if (!file.exists()) {
						throw new IOException("Database home '"
								+ derbyHome
								+ "' does not exist and could not be created!");
					}
				}
				System.setProperty("derby.system.home", derbyHome);
			}
		}

		// Database prep work
		try {
			checkTable(SEQUENCE_TABLE);
		} catch (SQLException ex) {
			log.error("Error during database preparation:", ex);
			throw new SQLException(
					"Error during database preparation:", ex);
		}
		log.debug("Derby security database online!");
	}

	private Connection connection() throws SQLException {
		if (connection == null || !connection.isValid(1)) {
			// At least try to close if not null... even though its not valid
			if (connection != null) {
				log.error("!!! Database connection has failed, recreating.");
				try {
					connection.close();
				} catch (SQLException ex) {
					log.error("Error closing invalid connection, ignoring: {}",
							ex.getMessage());
				}
			}

			// Open a new connection
			Properties props = new Properties();
			// Load the JDBC driver
			try {
				Class.forName(DERBY_DRIVER).newInstance();
			} catch (Exception ex) {
				log.error("Driver load failed: ", ex);
				throw new SQLException("Driver load failed: ", ex);
			}

			// Establish a database connection
			connection = DriverManager.getConnection(DERBY_PROTOCOL
					+ SEQUENCE_DATABASE + ";create=true", props);
		}
		return connection;
	}

	/**
	 * Shuts down the plugin
	 * 
	 * @throws AccessControlException
	 *             if there was an error during shutdown
	 */
	public void shutdown() throws SQLException {
		// Derby can only be shutdown from one thread,
		// we'll catch errors from the rest.
		String threadedShutdownMessage = DERBY_DRIVER
				+ " is not registered with the JDBC driver manager";
		try {
			// Tell the database to close
			DriverManager.getConnection(DERBY_PROTOCOL + ";shutdown=true");
			// Shutdown just this database (but not the engine)
			// DriverManager.getConnection(DERBY_PROTOCOL + SECURITY_DATABASE +
			// ";shutdown=true");
		} catch (SQLException ex) {
			// These test values are used if the engine is NOT shutdown
			// if (ex.getErrorCode() == 45000 &&
			// ex.getSQLState().equals("08006")) {

			// Valid response
			if (ex.getErrorCode() == 50000 && ex.getSQLState().equals("XJ015")) {
				// Error response
			} else {
				// Make sure we ignore simple thread issues
				if (!ex.getMessage().equals(threadedShutdownMessage)) {
					log.error("Error during database shutdown:", ex);
					throw new SQLException(
							"Error during database shutdown:", ex);
				}
			}
		} finally {
			try {
				// Close our connection
				if (connection != null) {
					connection.close();
					connection = null;
				}
			} catch (SQLException ex) {
				log.error("Error closing connection:", ex);
			}
		}
	}

	

	
	/**
	 * Check for the existence of a table and arrange for its creation if not
	 * found.
	 * 
	 * @param table
	 *            The table to look for and create.
	 * @throws SQLException
	 *             if there was an error.
	 */
	private void checkTable(String table) throws SQLException {
		boolean tableFound = findTable(table);

		// Create the table if we couldn't find it
		if (!tableFound) {
			log.debug("Table '{}' not found, creating now!", table);
			createTable(table);

			// Double check it was created
			if (!findTable(table)) {
				log.error("Unknown error creating table '{}'", table);
				throw new SQLException("Could not find or create table '"
						+ table + "'");
			}
		}
	}

	/**
	 * Check if the given table exists in the database.
	 * 
	 * @param table
	 *            The table to look for
	 * @return boolean flag if the table was found or not
	 * @throws SQLException
	 *             if there was an error accessing the database
	 */
	private boolean findTable(String table) throws SQLException {
		boolean tableFound = false;
		DatabaseMetaData meta = connection().getMetaData();
		ResultSet result = (ResultSet) meta.getTables(null, null, null, null);
		while (result.next() && !tableFound) {
			if (result.getString("TABLE_NAME").equalsIgnoreCase(table)) {
				tableFound = true;
			}
		}
		close(result);
		return tableFound;
	}

	/**
	 * Create the given table in the database.
	 * 
	 * @param table
	 *            The table to create
	 * @throws SQLException
	 *             if there was an error during creation, or an unknown table
	 *             was specified.
	 */
	private void createTable(String table) throws SQLException {
		if (table.equals(SEQUENCE_TABLE)) {
			Statement sql = connection().createStatement();
			sql.execute("CREATE TABLE " + SEQUENCE_TABLE
					+ "(sequence_name VARCHAR(255) NOT NULL, " 
					+ "counter INTEGER NOT NULL,"
					+ "PRIMARY KEY (sequence_name))");
			close(sql);
			return;
		}

		throw new SQLException("Unknown table '" + table + "' requested!");
	}

	/**
	 * Attempt to close a ResultSet. Basic wrapper for exception catching and
	 * logging
	 * 
	 * @param resultSet
	 *            The ResultSet to try and close.
	 */
	private void close(ResultSet resultSet) {
		if (resultSet != null) {
			try {
				resultSet.close();
			} catch (SQLException ex) {
				log.error("Error closing result set: ", ex);
			}
		}
		resultSet = null;
	}

	/**
	 * Attempt to close a Statement. Basic wrapper for exception catching and
	 * logging
	 * 
	 * @param statement
	 *            The Statement to try and close.
	 */
	private void close(Statement statement) {
		if (statement != null) {
			try {
				statement.close();
			} catch (SQLException ex) {
				log.error("Error closing statement: ", ex);
			}
		}
		statement = null;
	}
	
	public synchronized Integer getSequence(String sequenceName) throws SQLException {
		Integer sequenceCount = null;

		PreparedStatement sql = connection().prepareStatement(
				"SELECT * FROM " + SEQUENCE_TABLE + " WHERE sequence_name = ?");

		// Prepare and execute
		sql.setString(1, sequenceName);
		ResultSet result = sql.executeQuery();

		// Build response
		while (result.next()) {
			sequenceCount = result.getInt("counter");
		}
		close(result);
		close(sql);
		
		if(sequenceCount == null) {
			sequenceCount = 1;
			createNewSequence(sequenceName);
		}
		
		incrementSequence(sequenceName, sequenceCount+1);
		
		return sequenceCount;
	}

	private void incrementSequence(String sequenceName, Integer sequenceCount) throws SQLException {
		PreparedStatement sql = connection().prepareStatement(
				"UPDATE " + SEQUENCE_TABLE + " SET counter = ? WHERE sequence_name = ?");

		// Prepare and execute
		sql.setInt(1, sequenceCount);
		sql.setString(2, sequenceName);
		
		sql.executeUpdate();
		close(sql);
		
	}

	private void createNewSequence(String sequenceName) throws SQLException {
		PreparedStatement sql = connection().prepareStatement(
				"INSERT INTO " + SEQUENCE_TABLE + " VALUES (?, ?)");

		// Prepare and execute
		sql.setString(1, sequenceName);
		sql.setInt(2, 1);
		sql.executeUpdate();
		close(sql);
		
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy