| 1 | /** |
| 2 | * Copyright 2009 ATG DUST Project Licensed under the Apache License, Version |
| 3 | * 2.0 (the "License"); you may not use this file except in compliance with the |
| 4 | * License. You may obtain a copy of the License at |
| 5 | * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law |
| 6 | * or agreed to in writing, software distributed under the License is |
| 7 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 8 | * KIND, either express or implied. See the License for the specific language |
| 9 | * governing permissions and limitations under the License. |
| 10 | */ |
| 11 | |
| 12 | package atg.service.jdbc; |
| 13 | |
| 14 | import java.sql.DriverManager; |
| 15 | import java.sql.SQLException; |
| 16 | |
| 17 | import org.apache.log4j.Logger; |
| 18 | |
| 19 | import atg.nucleus.ServiceException; |
| 20 | |
| 21 | /** |
| 22 | * <b>Experimental since Apache Derby is not supported by ATG 9.0.</b> |
| 23 | * |
| 24 | * This datasource is used for testing. It starts up a Derby in memory instance |
| 25 | * on localhost automatically. The database will be named "testdb" by default. |
| 26 | * If you need to name it something else set the "databaseName" property on this |
| 27 | * component. You may want to change the name if your test requires running two |
| 28 | * databases at the same time. |
| 29 | * |
| 30 | * @author adamb |
| 31 | * @version $Id:$ |
| 32 | */ |
| 33 | |
| 34 | public class DerbyDataSource extends InitializingDataSourceBase { |
| 35 | |
| 36 | static Logger sLog = Logger.getLogger(DerbyDataSource.class); |
| 37 | |
| 38 | private String framework = "embedded"; |
| 39 | private String driver = "org.apache.derby.jdbc.EmbeddedDriver"; |
| 40 | private String protocol = "jdbc:derby:"; |
| 41 | private boolean mAddedShutdownHook = false; |
| 42 | |
| 43 | /** |
| 44 | * Sets Derby JDBC properties to be used when the first client asks for a |
| 45 | * connection. |
| 46 | */ |
| 47 | @Override |
| 48 | public void doStartService() throws ServiceException { |
| 49 | logInfo("Starting DerbyDataSource."); |
| 50 | loadDriver(); |
| 51 | this.setURL(protocol + getDatabaseName() + ";create=true"); |
| 52 | this.setDriver(driver); |
| 53 | this.setUser("user1"); |
| 54 | this.setPassword("user1"); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Cleans up for dynamo shutdown |
| 59 | */ |
| 60 | @Override |
| 61 | public void doStopService() throws ServiceException { |
| 62 | // Add a shutdown hook to shut down derby. |
| 63 | // We can't shutdown now because not all dynamo services |
| 64 | // that depend on us are guaranteed to be stopped when this method is |
| 65 | // invoked. |
| 66 | if (!mAddedShutdownHook) |
| 67 | addShutdownHook(getDatabaseName()); |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Adds a shutdown hook to shutdown Derby when the JVM exits. |
| 72 | * |
| 73 | * @param pDBName |
| 74 | * |
| 75 | */ |
| 76 | private void addShutdownHook(String pDBName) { |
| 77 | final String name = pDBName; |
| 78 | Runtime.getRuntime().addShutdownHook(new Thread() { |
| 79 | public void run() { |
| 80 | DerbyDataSource.shutdown(name); |
| 81 | } |
| 82 | }); |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Shuts down derby |
| 87 | * |
| 88 | * @param name |
| 89 | */ |
| 90 | private static void shutdown(String name) { |
| 91 | try { |
| 92 | // the shutdown=true attribute shuts down Derby |
| 93 | DriverManager.getConnection("jdbc:derby:" + name + ";shutdown=true"); |
| 94 | |
| 95 | // To shut down a specific database only, but keeep the |
| 96 | // engine running (for example for connecting to other |
| 97 | // databases), specify a database in the connection URL: |
| 98 | // DriverManager.getConnection("jdbc:derby:" + dbName + |
| 99 | // ";shutdown=true"); |
| 100 | } catch (SQLException se) { |
| 101 | |
| 102 | if (((se.getErrorCode() == 50000) && ("XJ015".equals(se.getSQLState())))) { |
| 103 | // we got the expected exception |
| 104 | sLog.info("Derby shut down normally"); |
| 105 | // Note that for single database shutdown, the expected |
| 106 | // SQL state is "08006", and the error code is 45000. |
| 107 | } else if ((se.getErrorCode() == 45000) |
| 108 | && ("08006".equals(se.getSQLState()))) { |
| 109 | // database is already shutdown |
| 110 | } else { |
| 111 | // if the error code or SQLState is different, we have |
| 112 | // an unexpected exception (shutdown failed) |
| 113 | sLog.error("Derby did not shut down normally", se); |
| 114 | printSQLException(se); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Prints details of an SQLException chain to <code>System.err</code>. Details |
| 122 | * included are SQL State, Error code, Exception message. |
| 123 | * |
| 124 | * @param e |
| 125 | * the SQLException from which to print details. |
| 126 | */ |
| 127 | public static void printSQLException(SQLException e) { |
| 128 | // Unwraps the entire exception chain to unveil the real cause of the |
| 129 | // Exception. |
| 130 | while (e != null) { |
| 131 | System.err.println("\n----- SQLException -----"); |
| 132 | System.err.println(" SQL State: " + e.getSQLState()); |
| 133 | System.err.println(" Error Code: " + e.getErrorCode()); |
| 134 | System.err.println(" Message: " + e.getMessage()); |
| 135 | // for stack traces, refer to derby.log or uncomment this: |
| 136 | // e.printStackTrace(System.err); |
| 137 | e = e.getNextException(); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * Loads the appropriate JDBC driver for this environment/framework. For |
| 143 | * example, if we are in an embedded environment, we load Derby's embedded |
| 144 | * Driver, <code>org.apache.derby.jdbc.EmbeddedDriver</code>. |
| 145 | */ |
| 146 | private void loadDriver() { |
| 147 | /* |
| 148 | * The JDBC driver is loaded by loading its class. If you are using JDBC 4.0 |
| 149 | * (Java SE 6) or newer, JDBC drivers may be automatically loaded, making |
| 150 | * this code optional. |
| 151 | * |
| 152 | * In an embedded environment, this will also start up the Derby engine |
| 153 | * (though not any databases), since it is not already running. In a client |
| 154 | * environment, the Derby engine is being run by the network server |
| 155 | * framework. |
| 156 | * |
| 157 | * In an embedded environment, any static Derby system properties must be |
| 158 | * set before loading the driver to take effect. |
| 159 | */ |
| 160 | try { |
| 161 | Class.forName(driver).newInstance(); |
| 162 | } catch (ClassNotFoundException cnfe) { |
| 163 | sLog.error("\nUnable to load the JDBC driver " + driver); |
| 164 | sLog.error("Please check your CLASSPATH."); |
| 165 | cnfe.printStackTrace(System.err); |
| 166 | } catch (InstantiationException ie) { |
| 167 | sLog.error("\nUnable to instantiate the JDBC driver " + driver); |
| 168 | ie.printStackTrace(System.err); |
| 169 | } catch (IllegalAccessException iae) { |
| 170 | sLog.error("\nNot allowed to access the JDBC driver " + driver); |
| 171 | iae.printStackTrace(System.err); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | } |