View Javadoc

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 }