/* * Copyright 2013 Matt Sicker and Contributors * * 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 atg.tools.dynunit.service.jdbc; import atg.nucleus.ServiceException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import java.sql.DriverManager; import java.sql.SQLException; /** * <b>Experimental since Apache Derby is not supported by ATG 9.0.</b> * <p/> * This datasource is used for testing. It starts up a Derby in memory instance * on localhost automatically. The database will be named "testdb" by default. * If you need to name it something else set the "databaseName" property on this * component. You may want to change the name if your test requires running two * databases at the same time. * * @author adamb * @version $Id:$ */ public class DerbyDataSource extends InitializingDataSourceBase { private static final Logger logger = LogManager.getLogger(); private static final String EMBEDDED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver"; private static final String PROTOCOL = "jdbc:derby:"; private String framework = "embedded"; private boolean addedShutdownHook = false; /** * Shuts down derby * * @param name */ private static void shutdown(String name) { try { // the shutdown=true attribute shuts down Derby DriverManager.getConnection(PROTOCOL + name + ";shutdown=true"); // To shut down a specific database only, but keep the // engine running (for example for connecting to other // databases), specify a database in the connection URL: // DriverManager.getConnection("jdbc:derby:" + dbName + // ";shutdown=true"); } catch (SQLException se) { if (((se.getErrorCode() == 50000) && ("XJ015".equals(se.getSQLState())))) { // we got the expected exception logger.info("Derby shut down normally"); // Note that for single database shutdown, the expected // SQL state is "08006", and the error code is 45000. } else if ((se.getErrorCode() == 45000) && ("08006".equals(se.getSQLState()))) { logger.trace("Derby was already shut down."); // database is already shutdown } else { // if the error code or SQLState is different, we have // an unexpected exception (shutdown failed) logger.error("Derby did not shut down normally", se); logSQLException(se); } } } /** * Logs details of an SQLException chain. Details included are SQL State, Error code, Exception message. * * @param e * the SQLException from which to print details. */ private static void logSQLException(@Nullable SQLException e) { for (; e != null; e = e.getNextException()) { logger.error("SQL State: {}. Error Code: {}. Message: {}.", e.getSQLState(), e.getErrorCode(), e.getMessage()); } } /** * Sets Derby JDBC properties to be used when the first client asks for a * connection. */ @Override public void doStartService() throws ServiceException { logger.trace("Starting up Derby data source"); loadDriver(); this.setURL(PROTOCOL + getDatabaseName() + ";create=true"); this.setDriver(EMBEDDED_DRIVER); this.setUser("user1"); this.setPassword("user1"); } /** * Cleans up for dynamo shutdown */ @Override public void doStopService() throws ServiceException { // Add a shutdown hook to shut down derby. // We can't shutdown now because not all dynamo services // that depend on us are guaranteed to be stopped when this method is // invoked. if (!addedShutdownHook) { addShutdownHook(getDatabaseName()); } } /** * Adds a shutdown hook to shutdown Derby when the JVM exits. * * @param pDBName */ private void addShutdownHook(String pDBName) { final String name = pDBName; Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { DerbyDataSource.shutdown(name); } } ); addedShutdownHook = true; } /** * Loads the appropriate JDBC driver for this environment/framework. For * example, if we are in an embedded environment, we load Derby's embedded * Driver, <code>org.apache.derby.jdbc.EmbeddedDriver</code>. * <p/> * The JDBC driver is loaded by loading its class. If you are using JDBC 4.0 * (Java SE 6) or newer, JDBC drivers may be automatically loaded, making * this code optional. * <p/> * In an embedded environment, this will also start up the Derby engine * (though not any databases), since it is not already running. In a client * environment, the Derby engine is being run by the network server * framework. * <p/> * In an embedded environment, any static Derby system properties must be * set before loading the driver to take effect. */ private void loadDriver() { try { Class.forName(EMBEDDED_DRIVER).newInstance(); } catch (ClassNotFoundException cnfe) { logger.catching(cnfe); logger.error("Unable to load the JDBC driver {}", EMBEDDED_DRIVER); } catch (InstantiationException ie) { logger.catching(ie); logger.error("Unable to instantiate the JDBC driver {}", EMBEDDED_DRIVER); } catch (IllegalAccessException iae) { logger.catching(iae); logger.error("Not allowed to access the JDBC driver {}", EMBEDDED_DRIVER); } } }