View Javadoc
1 /*
2 * PROJECT : DAR Runtime and Tools
3 * COPYRIGHT : Copyright (C) 1999-2004 tim.stephenson@enableit.org
4 * LICENSE : GNU LESSER GENERAL PUBLIC LICENSE
5 * Version 2.1, February 1999
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 package org.enableit.db.darrt;
22
23 import java.sql.Connection;
24 import java.sql.SQLException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30
31 import org.apache.log4j.Logger;
32 import org.enableit.db.ConnFactory;
33 import org.enableit.db.DBException;
34 import org.enableit.db.DatabaseProxy;
35 import org.enableit.db.beans.Provider;
36 import org.enableit.db.darrt.beans.DiffData;
37 import org.enableit.db.darrt.ext.DeclarativeDDLGenerator;
38
39
40 /***
41 * Utility Singleton for creating Data Definition Language.
42 * @author Tim Stephenson
43 */
44 public class DDLFactory {
45 /***
46 * Internal cache of <code>DDLGenerator</code>s for various RDBMS.
47 */
48 private static Map generators = new HashMap();
49
50 /***
51 * Singleton instance.
52 */
53 private static DDLFactory me;
54
55 /***
56 * The Log4J <code>Logger</code> doing the logging.
57 */
58 protected static Logger logger = Logger.getLogger(DDLFactory.class);
59
60 /***
61 * Constructor to preserve singleton status.
62 */
63 private DDLFactory() {
64
65 }
66
67 /***
68 * Factory method.
69 */
70 public static synchronized DDLFactory getInstance() {
71 if (me == null) {
72 me = new DDLFactory();
73 }
74
75 return me;
76 }
77
78 /***
79 * Create a database object that does not currently exist.
80 *
81 * @param provider
82 * @param table
83 * @return <code>List</code> of DDL statements (<code>String</code> type).
84 * @deprecated Use <code>getGenerator(provider).getCreate(dataObject)</code>.
85 */
86 public List getCreate(Provider provider, Object dataObject)
87 throws ConfigurationException {
88 logger.info("METHOD_ENTRY: getCreate");
89
90 DDLGenerator generator = getGenerator(provider);
91 List ddl = generator.getCreate(dataObject);
92
93 logger.info("METHOD_EXIT: getCreate");
94
95 return ddl;
96 }
97
98 /***
99 * @param provider
100 * @param diff
101 * @return <code>List</code> of DDL statements (<code>String</code> type).
102 * @deprecated Use <code>getGenerator(provider).getModify(diff)</code>.
103 */
104 public List getModify(Provider provider, DiffData diff)
105 throws ConfigurationException {
106 logger.info("METHOD_ENTRY: getModify");
107
108 DDLGenerator generator = getGenerator(provider);
109 List ddl = generator.getModify(diff);
110
111 logger.info("METHOD_EXIT: getModify");
112
113 return ddl;
114 }
115
116 /***
117 * Install the schema specified in the location specified.
118 * <p>
119 * It is critically important that error handling is such that either
120 * all changes are applied or none. In the latter case reasons should be
121 * provided.
122 *
123 * @param provider Specifies the location to create schema.
124 * @param database Specifies the schema to create (embedded provider details will be ignored).
125 */
126
127 /* public void install(Provider provider, Database database)
128 throws SchemaHandlingException {
129 logger.info("METHOD_ENTRY: install");
130
131 try {
132
133 } catch (Exception e) {
134 logger.debug(e);
135 throw new SchemaHandlingException("Error during alteration, all changes have been undone.");
136 }
137
138 logger.info("METHOD_EXIT: install");
139 } */
140
141 /***
142 * Install the necessary changes in the provider database to
143 * eliminate the differences in the <code>diffs</code> parameter.
144 * <p>
145 * It is critically important that error handling is such that either
146 * all changes are applied or none. In the latter case reasons should be
147 * provided.
148 *
149 * @param provider Specifies the location to create schema.
150 * @param database Specifies the schema to create (embedded provider details will be ignored).
151 */
152 public void install(Connection conn, DiffData diff)
153 throws SchemaHandlingException {
154 logger.info("METHOD_ENTRY: install");
155
156 if (conn == null) {
157 throw new SchemaHandlingException("Database connection was null");
158 }
159
160 List deferredDDL = new ArrayList() ;
161 try {
162 DDLGenerator generator = getGenerator(conn.getMetaData()
163 .getDatabaseProductName());
164
165 // get the ddl
166 List ddlList = null;
167
168 if (diff.isMissingFromTarget()) {
169 logger.debug("Requesting ddl from "
170 + generator.getClass().getName() + generator.getName());
171 ddlList = generator.getCreate(diff.getRefObj());
172 } else {
173 ddlList = generator.getModify(diff);
174 }
175
176 for (Iterator j = ddlList.iterator(); j.hasNext();) {
177 // execute it on the connection
178 String ddl = (String) j.next();
179
180 if (!DDLGenerator.NO_DEFINITION_AVAILABLE.equals(ddl)) {
181 try {
182 int result = DatabaseProxy.executeUpdate(conn, ddl);
183 } catch (DBException e) {
184 // Probably a foreign key constraint, try to defer
185 deferredDDL.add(ddl) ;
186 }
187 }
188 }
189
190 if (deferredDDL.size() > 0) {
191 String msg = "Could not add these items: " ;
192 logger.warn(msg + deferredDDL) ;
193 throw new TempSchemaHandlingException(msg, deferredDDL);
194 }
195 } catch (TempSchemaHandlingException e) {
196 throw e ; // just bubble this up
197 } catch (Exception e) {
198 // typed exceptions: SQLException and DBException
199 logger.error(e.getClass().getName() + ":" + e.getMessage());
200
201 try {
202 logger.error("Attempting rollback...") ;
203 conn.rollback();
204 } catch (Exception e2) {
205 logger.error(e2.getClass().getName() + ":" + e2.getMessage());
206 }
207
208 throw new SchemaHandlingException(
209 "Error during alteration, all changes have been undone.");
210 }
211
212 logger.info("METHOD_EXIT: install");
213 }
214
215 /***
216 * Returns the appropriate generator for the provider specified.
217 *
218 * @param provider Defines database <code>DDLGenerator</code>
219 * is required for. May be <code>null</code>.
220 * @return <code>DDLGenerator</code>.
221 */
222 public DDLGenerator getGenerator(Provider provider)
223 throws ConfigurationException {
224 logger.info("METHOD_ENTRY: getGenerator");
225
226 DDLGenerator generator = null;
227
228 if (provider == null) {
229 logger.error("Provider is null.");
230 throw new ConfigurationException("Unable to identify database") ;
231 } else if (provider.getProductName() == null) {
232 Connection conn = null;
233
234 try {
235 conn = ConnFactory.getConnection(provider);
236
237 String prodName = removeWhitespace(conn.getMetaData()
238 .getDatabaseProductName());
239
240 provider.setProductName(prodName);
241 logger.warn("Assigning product name: " + prodName);
242 } catch (DBException e) {
243 logger.error("Unable to identify database");
244 throw new ConfigurationException("Unable to identify database") ;
245 } catch (SQLException e) {
246 logger.error("Unable to identify database");
247 throw new ConfigurationException("Unable to identify database") ;
248 } finally {
249 try {
250 conn.close();
251 } catch (SQLException e2) {
252 ;
253 }
254 }
255
256 generator = getGenerator(provider);
257 } else {
258 String productName = provider.getProductName();
259
260 try {
261 if (provider.getVersion() == null) {
262 throw new ConfigurationException() ;
263 } else {
264 generator = getGenerator(productName + provider.getVersion());
265 }
266 } catch (ConfigurationException e) {
267 generator = getGenerator(productName);
268 // If this throws config ex, then too bad
269 }
270 }
271
272 logger.info("METHOD_EXIT: getGenerator, generator="
273 + generator.getName());
274
275 return generator;
276 }
277
278 /***
279 * Returns the appropriate generator for the provider specified.
280 *
281 * @param productName Defines database <code>DDLGenerator</code>
282 * is required for. May be <code>null</code>.
283 * @return <code>DDLGenerator</code>.
284 */
285 public DDLGenerator getGenerator(String productName)
286 throws ConfigurationException {
287 logger.info("METHOD_ENTRY: getGenerator(String), " + "productName:"
288 + productName);
289
290 DDLGenerator generator = null;
291
292 productName = removeWhitespace(productName);
293 generator = (DDLGenerator) generators.get(productName);
294
295 if (generator == null) {
296 generator = new DeclarativeDDLGenerator(productName);
297
298 synchronized (DDLFactory.class) {
299 generators.put(generator.getName(), generator);
300 }
301 }
302
303 logger.info("METHOD_EXIT: getGenerator(String), generator="
304 + generator.getName());
305
306 return generator;
307 }
308
309 /***
310 * Removes embedded whitespace from the string parameter.
311 */
312 private String removeWhitespace(String s) {
313 logger.info("METHOD_ENTRY: removeWhitespace, s=" + s);
314
315 StringBuffer sb = new StringBuffer();
316
317 for (int i = 0; i < s.length(); i++) {
318 char c = s.charAt(i);
319
320 if (!Character.isWhitespace(c)) {
321 sb.append(c);
322 }
323 }
324
325 logger.info("METHOD_EXIT: removeWhitespace, returning=" + sb);
326
327 return sb.toString();
328 }
329 }
This page was automatically generated by Maven