From: <mwl...@us...> - 2009-01-26 18:31:16
|
Revision: 856 http://cishell.svn.sourceforge.net/cishell/?rev=856&view=rev Author: mwlinnem Date: 2009-01-26 18:31:07 +0000 (Mon, 26 Jan 2009) Log Message: ----------- Major code cleanup. Improved error handling. Modified Paths: -------------- trunk/core/org.cishell.reference.service.database/META-INF/MANIFEST.MF Added Paths: ----------- trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DerbyDatabaseService.java Removed Paths: ------------- trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DatabaseServiceImpl.java Modified: trunk/core/org.cishell.reference.service.database/META-INF/MANIFEST.MF =================================================================== --- trunk/core/org.cishell.reference.service.database/META-INF/MANIFEST.MF 2009-01-24 19:34:25 UTC (rev 855) +++ trunk/core/org.cishell.reference.service.database/META-INF/MANIFEST.MF 2009-01-26 18:31:07 UTC (rev 856) @@ -4,7 +4,7 @@ Bundle-SymbolicName: org.cishell.reference.service.database Bundle-Version: 1.0.0 Bundle-RequiredExecutionEnvironment: J2SE-1.4 -Bundle-Activator: org.cishell.reference.service.database.DatabaseServiceImpl +Bundle-Activator: org.cishell.reference.service.database.DerbyDatabaseService X-AutoStart: true Import-Package: org.apache.commons.dbcp, org.apache.commons.pool, Deleted: trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DatabaseServiceImpl.java =================================================================== --- trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DatabaseServiceImpl.java 2009-01-24 19:34:25 UTC (rev 855) +++ trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DatabaseServiceImpl.java 2009-01-26 18:31:07 UTC (rev 856) @@ -1,183 +0,0 @@ -package org.cishell.reference.service.database; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Hashtable; - -import javax.sql.DataSource; - -import org.apache.commons.dbcp.ConnectionFactory; -import org.apache.commons.dbcp.DriverManagerConnectionFactory; -import org.apache.commons.dbcp.PoolableConnectionFactory; -import org.apache.commons.dbcp.PoolingDataSource; -import org.apache.commons.pool.KeyedObjectPoolFactory; -import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory; -import org.apache.commons.pool.impl.GenericObjectPool; -import org.cishell.service.database.DataSourceWithID; -import org.cishell.service.database.DatabaseCreationException; -import org.cishell.service.database.DatabaseService; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.ServiceRegistration; - - - -public class DatabaseServiceImpl implements DatabaseService, BundleActivator { - /* TODO: These variables should be abstracted out in a Preferences page at some - point (I guess). - */ - private static final String DEFAULT_DRIVER_NAME = - "org.apache.derby.jdbc.EmbeddedDriver"; - private static final String DEFAULT_PROTOCOL = "jdbc:derby:"; - private static final String DEFAULT_CREATE_CONNECTION_STRING = ";create=true"; - private static final String DEFAULT_DB_NAME = "ultra_sasquatch"; - - //db name must be unique per running instance of the same NWB installation, otherwise they will overlap - private static int dbNameCounter = 0; - - private ServiceRegistration databaseServiceRegistration; - - private PoolingDataSource poolingDataSource = null; - // TODO: Needed? I just want to make sure nothing goes wrong for now. - private DataSourceWithID myDataSource = null; - - private String driver; - - public void start(BundleContext context) throws Exception { - this.driver = DEFAULT_DRIVER_NAME; - - System.err.println("starting!"); - - // Register me as a service! (This doesn't work?) - databaseServiceRegistration = context.registerService - (DatabaseService.class.getName(), this, new Hashtable()); - - ServiceReference ref = context.getServiceReference(DatabaseService.class.getName()); - if (ref == null) { - System.out.println("REEEEEEEEEEEEEEEEEEEEEEEFFFFFFF IS NUUUUUUUULLLLLLL!!@!(@!(!)!"); - } - DatabaseService dbService = (DatabaseService) context.getService(ref); - if (dbService == null) { - System.out.println("DEEEBEESEERVICE IS NUUUUL!L!@L!@!@!@@!@!@L..1"); - } else { - - } - - // Get MY data source! It's mine, and you can't have it! - try { - myDataSource = createDatabase(); - } - catch (DatabaseCreationException e) { - System.err.println(":'( " + e.getMessage()); - throw e; - } - - System.err.println("meep?"); - cleanOutDatabaseTables(); - } - - private void cleanOutDatabaseTables() throws Exception { - //TODO: We need to be cleaning up and starting the DB correctly - //TODO: Clean this up - DataSource ds = getDataSource(); - Connection c = ds.getConnection(); - DatabaseMetaData dmd = c.getMetaData(); - ResultSet tableNames = dmd.getTables(null, null, null, null); - while (tableNames.next()) { - for (int ii = 1; ii <= tableNames.getMetaData().getColumnCount(); ii++) { - String tableContents = tableNames.getString(ii); - System.out.print(tableContents + ", "); - } - if (tableNames.getString(2).indexOf("APP") != -1) { - System.out.println("MAGOOT!"); - Statement s = c.createStatement(); - System.out.println(tableNames.getString(3)); - System.out.println(s.executeUpdate("DROP TABLE APP." + tableNames.getString(3))); - - } - System.out.println(""); - } - } - - public void stop(BundleContext context) throws Exception { - cleanOutDatabaseTables(); - try { - DriverManager.getConnection("jdbc:derby:;shutdown=true"); - } catch (Exception e) { - - } - } - - // If one hasn't been created yet, create a connection pool and return it. - private PoolingDataSource getDataSource() throws DatabaseCreationException - { - if (poolingDataSource != null) - return poolingDataSource; - - try { - System.err.println("Loading driver"); - // This loads the database driver. - Class.forName(DEFAULT_DRIVER_NAME); - - // We can use this later to check acceptsUrl for better error reporting. - // Driver jdbcDriver = (Driver) Class.forName(driver).newInstance(); - - //TODO: It may exist from before, actually. Fix this. - String newDatabaseName = - DEFAULT_DB_NAME; - String newDatabaseConnectionURL = DEFAULT_PROTOCOL + - newDatabaseName + - DEFAULT_CREATE_CONNECTION_STRING; - - System.err.println("connection url: " + newDatabaseConnectionURL); - - // This connection factory actually uses the loaded database driver to - // generate connections. - ConnectionFactory connectionFactory = new DriverManagerConnectionFactory - (newDatabaseConnectionURL, null, null); - - // This is a generic object pool. It must be linked to a poolable - // object factory (PoolableObjectFactory), which the new - // PoolableConnectionFactory below is. - GenericObjectPool connectionPool = new GenericObjectPool(); - - // Not sure what this does? - KeyedObjectPoolFactory stmtPool = new GenericKeyedObjectPoolFactory(null); - - // This is a poolable object factory (PoolableObjectFactory) used to - // create connections for an object pool. It is the glue between the - // connection factory and the object pool. - // It links itself up to the connect pool inside its constructor, which - // is why it's not assigned to a variable out here. - new PoolableConnectionFactory(connectionFactory, connectionPool, stmtPool, null, false, true); - - // Finally, create the connection pool. - poolingDataSource = new PoolingDataSource(connectionPool); - - // TODO: Remove this? - // Make sure we can get a connection from the connection pool. - try { - poolingDataSource.getConnection().close(); - } - catch (SQLException e) { - throw new DatabaseCreationException - ("Could not properly initiate database.", e); - } - } - catch (ClassNotFoundException e) { - throw new DatabaseCreationException - ("Database driver (" + driver + ") could not be found", e); - } - - return poolingDataSource; - } - - public DataSourceWithID createDatabase() throws DatabaseCreationException { - return new DataSourceWithIDImpl(getDataSource()); - } -} Copied: trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DerbyDatabaseService.java (from rev 853, trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DatabaseServiceImpl.java) =================================================================== --- trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DerbyDatabaseService.java (rev 0) +++ trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DerbyDatabaseService.java 2009-01-26 18:31:07 UTC (rev 856) @@ -0,0 +1,163 @@ +package org.cishell.reference.service.database; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Hashtable; + +import javax.sql.DataSource; + +import org.apache.commons.dbcp.ConnectionFactory; +import org.apache.commons.dbcp.DriverManagerConnectionFactory; +import org.apache.commons.dbcp.PoolableConnectionFactory; +import org.apache.commons.dbcp.PoolingDataSource; +import org.apache.commons.pool.KeyedObjectPoolFactory; +import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory; +import org.apache.commons.pool.impl.GenericObjectPool; +import org.cishell.service.database.DataSourceWithID; +import org.cishell.service.database.DatabaseCreationException; +import org.cishell.service.database.DatabaseService; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; + + + +public class DerbyDatabaseService implements DatabaseService, BundleActivator { + + /* + * Variables used to connect to the internal derby database + * + * TODO: Eventually support connecting databases other than derby, + * and connecting to databases other than our internal one + * (maybe not by modifying this class directly though). + */ + private static final String DERBY_DRIVER_NAME = + "org.apache.derby.jdbc.EmbeddedDriver"; + private static final String DERBY_PROTOCOL = "jdbc:derby:"; + private static final String DEFAULT_CREATE_CONNECTION_STRING = ";create=true"; + private static final String DEFAULT_DB_NAME = "cishell_database"; + + //where the database exists on the filesystem (relative to the application root directory) + private static final String DATABASE_DIRECTORY = "database/"; + + //(new connections to the database will come from a pool of connections, improving performance) + private PoolingDataSource poolingDataSource = null; + + //hold on to our service registration so we can unregister when this plugin stops. + private ServiceRegistration databaseServiceRegistration; + + public void start(BundleContext context) throws Exception { + + /* + * Tell Derby to look for an existing database or create a new database + * in the default directory (within our application's directory) + */ + System.setProperty("derby.system.home", DATABASE_DIRECTORY); + + /* + * Make sure we don't have any leftover cruft in the db from previous sessions. + * This is our second chance, if the database wasn't properly cleaned out + * in the stop() method on application exit. + * TODO: If we ever implement some kind of persistence we may not want to do this so bluntly + */ + removeAllNonSystemDatabaseTables(); + + //allow the database service to be found by other services/plugins + databaseServiceRegistration = context.registerService + (DatabaseService.class.getName(), this, new Hashtable()); + + } + + public void stop(BundleContext context) { + //disallow the database service to be found by other services/plugins + this.databaseServiceRegistration.unregister(); + + //try to clean out the database and shut it down. + try { + removeAllNonSystemDatabaseTables(); + DriverManager.getConnection("jdbc:derby:;shutdown=true"); + } catch (Exception e) { + String message = + "An unexpected exception occurred while shutting down the internal database." + + "Aborting database shutdown process." + + "Database may not be left in a valid state (but it will probably be okay)."; + throw new RuntimeException(message, e); + } + } + + //Return a link to a "database" that cannot interfere with other "databases" in the system. + //TODO: May need to refactor to improve the terminology used surrounding this functionality. + public DataSourceWithID createDatabase() throws DatabaseCreationException { + return new DataSourceWithIDImpl(getDataSource()); + } + + + private static final int SCHEMA_NAME_INDEX = 2; + private static final int TABLE_NAME_INDEX = 3; + private static final String NONSYSTEM_SCHEMA_NAME = "APP"; + + private void removeAllNonSystemDatabaseTables() throws Exception { + + DataSource db = getDataSource(); + Connection dbConnection = db.getConnection(); + DatabaseMetaData dbMetadata = dbConnection.getMetaData(); + ResultSet allTableNames = dbMetadata.getTables(null, null, null, null); + + Statement removeTables = dbConnection.createStatement(); + + while (allTableNames.next()) { + if (allTableNames.getString(SCHEMA_NAME_INDEX).indexOf(NONSYSTEM_SCHEMA_NAME) != -1) { + String removeTableSQL = + "DROP TABLE " + + NONSYSTEM_SCHEMA_NAME + "." + allTableNames.getString(TABLE_NAME_INDEX); + removeTables.addBatch(removeTableSQL); + } + } + + removeTables.executeBatch(); + } + + //TODO: It could be that we should give everyone different datasources instead of the same one + private PoolingDataSource getDataSource() throws DatabaseCreationException + { + if (this.poolingDataSource == null) { initializePoolingDataSource(); }; + return poolingDataSource; + } + + //lazy-load the pooling data source + private void initializePoolingDataSource() throws DatabaseCreationException { + if (this.poolingDataSource != null) { + return; + } + + try { + //Load the database driver + Class.forName(DERBY_DRIVER_NAME); + + /* + * TODO:We can use this later to check acceptsUrl for better error reporting. + * Driver jdbcDriver = (Driver) Class.forName(driver).newInstance(); + */ + String newDatabaseConnectionURL = DERBY_PROTOCOL + + DEFAULT_DB_NAME + + DEFAULT_CREATE_CONNECTION_STRING; + + ConnectionFactory connectionFactory = new DriverManagerConnectionFactory + (newDatabaseConnectionURL, null, null); + GenericObjectPool connectionPool = new GenericObjectPool(); + KeyedObjectPoolFactory stmtPool = new GenericKeyedObjectPoolFactory(null); + //(side-effects the connection-pool so it gets its connections from the connection factory (I think)) + new PoolableConnectionFactory(connectionFactory, connectionPool, stmtPool, null, false, true); + this.poolingDataSource = new PoolingDataSource(connectionPool); + } + catch (ClassNotFoundException e) { + throw new DatabaseCreationException + ("Database driver '" + DERBY_DRIVER_NAME + "' could not be found", e); + } + } +} Property changes on: trunk/core/org.cishell.reference.service.database/src/org/cishell/reference/service/database/DerbyDatabaseService.java ___________________________________________________________________ Added: svn:mergeinfo + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |