/* * Copyright 2005-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wamblee.support.persistence; import java.io.File; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import junit.framework.Assert; import junit.framework.TestCase; import org.apache.derby.drda.NetworkServerControl; import org.wamblee.io.FileSystemUtils; /** * Derby database setup. The external JDBC url used to connect to a running * instance is * *
 *     jdbc:derby:net://localhost:1527/testdb
 * 
* * and the driver class is * *
 * com.ibm.db2.jcc.DB2Driver
 * 
* * The following jars will have to be used db2jcc.jar and * db2jcc_license_c.jar. */ public class DerbyDatabase extends AbstractDatabase { /** * Logger. */ private static final Logger LOGGER = Logger.getLogger(DerbyDatabase.class .getName()); /** * Database user name. */ private static final String USERNAME = "sa"; /** * Database password. */ private static final String PASSWORD = "123"; /** * Poll interval for the checking the server status. */ private static final int POLL_INTERVAL = 100; /** * Maximum time to wait until the server has started or stopped. */ private static final int MAX_WAIT_TIME = 10000; /** * Database name to use. */ private static final String DATABASE_NAME = "testdb"; /** * Path on the file system where derby files are stored. */ private static final String DATABASE_PATH = "target/db/persistence/derby"; /** * Derby property required to set the file system path * {@link #DATABASE_PATH}. */ private static final String SYSTEM_PATH_PROPERTY = "derby.system.home"; private boolean inmemory; /** * Constructs derby database class to allow creation of derby database * instances. */ public DerbyDatabase() { inmemory = true; } public DerbyDatabase(boolean aInMemoryFlag) { inmemory = aInMemoryFlag; } /* * (non-Javadoc) * * @see org.wamblee.persistence.Database#start() */ public void doStart() { try { System.setProperty("derby.stream.error.file", "target/derby.log"); // just in case a previous run was killed without the // cleanup cleanPersistentStorage(); if (!inmemory) { // set database path. Properties lProperties = System.getProperties(); lProperties.put(SYSTEM_PATH_PROPERTY, DATABASE_PATH); } Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance(); runDatabase(); waitUntilStartedOrStopped(true); // Force creation of the database. Connection lConnection = createConnection(); lConnection.close(); LOGGER.info("Database started: \n URL = " + getExternalJdbcUrl() + "\n user = " + getUsername() + "\n password = " + getPassword()); createDataSource(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { if (isStarted()) { LOGGER.warning("Shutting down db"); DerbyDatabase.this.stop(); } } }); } catch (Exception e) { throw new RuntimeException("Problem starting database", e); } } /** * Waits until the database server has started or stopped. * * @param aStarted * If true, waits until the server is up, if false, waits until * the server is down. * @throws InterruptedException */ private void waitUntilStartedOrStopped(boolean aStarted) throws InterruptedException { long lWaited = 0; while (aStarted != isStarted()) { Thread.sleep(POLL_INTERVAL); lWaited += POLL_INTERVAL; if (lWaited > MAX_WAIT_TIME) { Assert.fail("Derby database did not start within " + MAX_WAIT_TIME + "ms"); } } } /** * Checks if the database server has started or not. * * @return True if started, false otherwise. */ private boolean isStarted() { try { getControl().ping(); return true; } catch (Exception e) { return false; } } /** * Gets the controller for the database server. * * @return Controller. * @throws Exception */ private NetworkServerControl getControl() throws Exception { return new NetworkServerControl(); } /** * Runs the database. * */ private void runDatabase() { try { getControl().start(new PrintWriter(System.out)); } catch (Exception e) { throw new RuntimeException(e); } } /* * (non-Javadoc) * * @see org.wamblee.persistence.Database#getJdbcUrl() */ public String getJdbcUrl() { return getBaseJdbcUrl() + ";create=true;retrieveMessagesFromServerOnGetMessage=true;"; } private String getBaseJdbcUrl() { return (inmemory ? "jdbc:derby:memory:" : "jdbc:derby:") + DATABASE_NAME; } /* * (non-Javadoc) * * @see org.wamblee.persistence.Database#getExternalJdbcUrl() */ public String getExternalJdbcUrl() { return "jdbc:derby://localhost:1527/" + (inmemory ? "memory:" : "") + DATABASE_NAME; } /** * Shuts down the derby database and cleans up all created files. * */ private void shutdownDerby() { try { DriverManager.getConnection("jdbc:derby:;shutdown=true"); throw new RuntimeException( "Derby did not shutdown, should always throw exception at shutdown"); } catch (Exception e) { LOGGER.info("Derby has been shut down."); } } /** * Gets the user name. */ public String getUsername() { return USERNAME; } /** * Gets the password. * * @return */ public String getPassword() { return PASSWORD; } /* * (non-Javadoc) * * @see org.wamblee.persistence.Database#createConnection() */ public Connection createConnection() throws SQLException { Connection c = DriverManager.getConnection(getJdbcUrl(), getUsername(), getPassword()); return c; } /** * Stops the derby database and cleans up all derby files. */ public void doStop() { try { // shutdown network server. getControl().shutdown(); waitUntilStartedOrStopped(false); // shutdown inmemory access. shutdownDerby(); cleanPersistentStorage(); } catch (Exception e) { LOGGER.log(Level.WARNING, "Problem stopping database", e); } } /** * Cleans up persistent storage of Derby. */ private void cleanPersistentStorage() { File lFile = new File(DATABASE_PATH); if (lFile.isFile()) { TestCase.fail("A regular file by the name " + DATABASE_PATH + " exists, clean this up first"); } if (!lFile.isDirectory()) { return; // no-op already cleanup up. } FileSystemUtils.deleteDirRecursively(DATABASE_PATH); } }