/* * 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.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 {
// 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) {
throw new RuntimeException(
"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);
}
}