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 | } |