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.io.FileWriter;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.sql.Connection;
27 import java.sql.DatabaseMetaData;
28 import java.util.ArrayList;
29 import java.util.Iterator;
30 import java.util.List;
31
32 import org.apache.log4j.Logger;
33 import org.enableit.db.ConnFactory;
34 import org.enableit.db.DatabaseProxy;
35 import org.enableit.db.beans.Database;
36 import org.enableit.db.beans.Provider;
37 import org.enableit.db.beans.ProviderExt;
38 import org.enableit.db.beans.Schema;
39 import org.enableit.db.beans.Table;
40 import org.enableit.db.darrt.beans.DiffData;
41 import org.exolab.castor.xml.Marshaller;
42
43
44 /***
45 * The entry point or facade for the library.
46 * External tools should only need to access this class.
47 * <p>
48 * This class expects always to have a reference and a target schema.
49 * Sometimes they may be taken from the same XML file, as in the case
50 * of installing a .dar file. On other occasions they may be completly
51 * separate as with the case of comparing two online datasources.
52 * </p><p>
53 * Either schema may be specified in a range of ways such as by setting
54 * the URL where a <code>darrt-schema</code> document may be found, specifying
55 * online datasources or providing pre-instantiated object representations.
56 * </p>
57 *
58 * @author default
59 */
60 public class SchemaHandler extends AbstractSchemaHandler {
61 /***
62 * The Log4J <code>Logger</code> doing the logging.
63 */
64 private static Logger logger = Logger.getLogger(SchemaHandler.class);
65
66 /***
67 * Default constructor.
68 */
69 public SchemaHandler() {
70 }
71
72 /***
73 * Parses a schema.
74 */
75 private void parseSchemas()
76 throws SchemaHandlingException {
77 logger.info("METHOD_ENTRY: parseSchema");
78
79 try {
80 // It is possible that the schema may have been set directly (ie no need to parse)
81 if (getRefDatabase() == null) {
82 if (getRefSchemaUrl() != null) {
83 logger.warn("Parsing reference schema from URL:"
84 + getRefSchemaUrl());
85
86 InputStream input = getRefSchemaUrl().openStream();
87
88 setRefDatabase(Database.unmarshal(
89 new InputStreamReader(input)));
90 input.close();
91
92 //logger.debug("No of tables=" + getRefDatabase().getSchema().getTableCount()) ;
93 //logger.debug("Tables:" + Arrays.asList(getRefDatabase().getSchema().getTable())) ;
94 } else {
95 // get the schema from online database
96 logger.warn("Parsing reference schema from database:"
97 + getOnlineRefSchema().getUrl());
98 setRefDatabase(getDatabase(getOnlineRefSchema()));
99 }
100 }
101
102 // It is possible that the schema may have been set directly (ie no need to parse)
103 if (getTargetDatabase() == null) {
104 if (getTargetSchemaUrl() != null) {
105 logger.warn("Parsing target schema from URL:"
106 + getTargetSchemaUrl());
107
108 InputStream input = getTargetSchemaUrl().openStream();
109
110 setTargetDatabase(Database.unmarshal(
111 new InputStreamReader(input)));
112 input.close();
113
114 //logger.debug("No of tables=" + getTargetDatabase().getSchema().getTableCount()) ;
115 //logger.debug("Tables:" + Arrays.asList(getTargetDatabase().getSchema().getTable())) ;
116 } else {
117 // get the schema from online database
118 logger.warn("Parsing target schema from database:"
119 + getOnlineTargetSchema().getUrl());
120 setTargetDatabase(getDatabase(getOnlineTargetSchema()));
121 }
122 }
123 } catch (java.io.IOException e) {
124 // TODO attempt to continue
125 logger.fatal(e.getMessage(), e);
126 throw new SchemaHandlingException(e.getMessage());
127 } catch (org.exolab.castor.xml.MarshalException e) {
128 // TODO attempt to continue
129 logger.fatal(e.getMessage(), e);
130 throw new SchemaHandlingException(e.getMessage());
131 } catch (org.exolab.castor.xml.ValidationException e) {
132 // TODO attempt to continue
133 logger.fatal(e.getMessage(), e);
134 throw new SchemaHandlingException(e.getMessage());
135 }
136
137 // Must ALWAYS have two schemas by this point
138 if (getRefDatabase() == null) {
139 throw new SchemaHandlingException("Reference schema is missing.");
140 }
141
142 if (getTargetDatabase() == null) {
143 throw new SchemaHandlingException("Target schema is missing.");
144 }
145
146 logger.info("METHOD_EXIT: parseSchema");
147 }
148
149 /***
150 * Export the schema identified by the reference provider property.
151 * @return <code>Database</code> instance representing the exported schema.
152 */
153 public Database export() {
154 logger.info("METHOD_ENTRY: export");
155
156 logger.info("METHOD_EXIT: export");
157
158 return null;
159 }
160
161 /***
162 * Creates the reference schema at the location identified first by the provider
163 * parameter or if null by the provider details inside the target schema.
164 * <p>
165 * Note that if this method is used to create a schema in a database
166 * where any tables exist with the same names, then those tables will be
167 * modified to fit the reference schema.
168 *
169 */
170 public void createSchema()
171 throws SchemaHandlingException {
172 try {
173 // Do the required changes (if any)
174 alterSchema();
175 } catch (SchemaHandlingException e) {
176 // Should already be logged
177 throw e;
178 } catch (Exception e) {
179 logger.error(e);
180
181 //e.printStackTrace() ;
182 throw new SchemaHandlingException(
183 "Error during creation, all changes have been undone.");
184 }
185 }
186
187 /***
188 * Compares the reference and target schemas, then modifies the target
189 * as necessary to match the reference.
190 */
191 public void alterSchema()
192 throws SchemaHandlingException {
193 checkMandatory();
194
195 Connection conn = null;
196
197 try {
198 List diffs = diffSchemas();
199
200 if ((diffs != null) && (diffs.size() > 0)) {
201 DDLFactory ddlFactory = DDLFactory.getInstance();
202
203 raiseEvent(DarrtEvent.START_ALTER_SCHEMA,
204 getTargetDatabase().getProvider().getUrl());
205
206 conn = ConnFactory.getConnection(getTargetDatabase()
207 .getProvider());
208 if (getTargetDatabase().getProvider().getProductName() == null) {
209 DatabaseMetaData dmd = conn.getMetaData() ;
210 getTargetDatabase().getProvider().setProductName(
211 dmd.getDatabaseProductName()) ;
212 }
213
214 List deferredDDL = new ArrayList() ;
215 for (Iterator i = diffs.iterator(); i.hasNext();) {
216 DiffData diff = (DiffData) i.next();
217
218 raiseEvent(DarrtEvent.SCHEMA_MODIFICATION, diff);
219 try {
220 ddlFactory.install(conn, diff);
221 } catch (TempSchemaHandlingException e) {
222 String msg = "Error executing: "
223 + e.getDeferredDDL() + ", may be temporary." ;
224 logger.fatal(msg) ;
225 deferredDDL.addAll(e.getDeferredDDL());
226 }
227 }
228
229 for (Iterator i = deferredDDL.iterator(); i.hasNext();) {
230 String ddl = (String) i.next() ;
231 // If throws exception again, then nothing more to be done
232 int result = DatabaseProxy.executeUpdate(conn, ddl) ;
233 }
234
235 raiseEvent(DarrtEvent.END_ALTER_SCHEMA,
236 getTargetDatabase().getProvider().getUrl());
237 }
238 } catch (SchemaHandlingException e) {
239 // Should already be logged
240 logger.fatal(e.getMessage(), e);
241 throw e;
242 } catch (Exception e) {
243 logger.error(e.getClass().getName() + ":" + e.getMessage());
244 //if (logger.isDebugEnabled()) {
245 logger.error(e.getMessage(), e) ;
246 //}
247 throw new SchemaHandlingException(
248 "Error during alteration, database may be in unexpected state.");
249 } finally {
250 try {
251 if (conn != null) {
252 conn.close();
253 conn = null;
254 }
255 } catch (java.sql.SQLException e) {
256 ;
257 }
258
259 }
260 }
261
262 /***
263 * Changes the target database defined by the reference schema XML document
264 * to the values held in the target provider.
265 */
266 public void changeTarget()
267 throws SchemaHandlingException {
268 checkMandatory();
269
270 try {
271 // Check have the ref url (not other ref format)
272 if (getRefSchemaUrl() == null) {
273 throw new SchemaHandlingException(
274 "You must specify a reference schema to change the target "
275 + "RDBMS on.");
276 } else {
277 logger.warn("Parsing reference schema from URL:"
278 + getRefSchemaUrl());
279
280 InputStream input = getRefSchemaUrl().openStream();
281
282 setRefDatabase(Database.unmarshal(new InputStreamReader(input)));
283 input.close();
284 }
285
286 // Check have the target url (not other target format)
287 if (getTargetSchemaUrl() == null) {
288 throw new SchemaHandlingException(
289 "You must specify the target schema URL where output will be written.");
290 }
291
292 /*
293 * Get the target, check 'online' first as URL is mandatory.
294 * in order to determine where output should be written
295 */
296 Provider targetProvider = null;
297
298 if (getOnlineTargetSchema() != null) {
299 logger.debug("Setting target provider to: "
300 + ProviderExt.toString(getOnlineTargetSchema()));
301 targetProvider = getOnlineTargetSchema();
302 } else if (getTargetSchemaUrl() != null) {
303 logger.warn("Parsing target provider from URL:"
304 + getTargetSchemaUrl());
305
306 InputStream input = getTargetSchemaUrl().openStream();
307
308 setTargetDatabase(Database.unmarshal(
309 new InputStreamReader(input)));
310 input.close();
311 targetProvider = getTargetDatabase().getProvider();
312 } else {
313 targetProvider = getTargetDatabase().getProvider();
314 }
315
316 // Set the target onto the reference and save it to file.
317 logger.debug("Replacing ref with target provider="
318 + ProviderExt.toString(targetProvider));
319 getRefDatabase().setProvider(targetProvider);
320
321 if ("file".equals(getTargetSchemaUrl().getProtocol())) {
322 logger.debug("Writing new target schema to "
323 + getTargetSchemaUrl().getFile());
324
325 FileWriter out = new FileWriter(getTargetSchemaUrl().getFile());
326 Marshaller temp = new Marshaller(out);
327
328 temp.setValidation(true);
329 temp.setRootElement("database");
330 temp.marshal(getRefDatabase());
331
332 //Marshaller.marshal(getRefDatabase(), out) ;
333 out.close();
334 } else {
335 throw new SchemaHandlingException(
336 "Unsupported output URL protocol: "
337 + getTargetSchemaUrl().getProtocol());
338 }
339 } catch (SchemaHandlingException e) {
340 throw e;
341 } catch (Exception e) {
342 logger.error(e.getClass().getName() + ":" + e.getMessage());
343 e.printStackTrace();
344 }
345 }
346
347 /*public void alterSchemaScript()
348 throws SchemaHandlingException {
349 // TODO: implement
350 }*/
351
352 /***
353 * Checks the mandatory properties have been set, so can continue.
354 */
355 private void checkMandatory()
356 throws SchemaHandlingException {
357 // Verify have sufficient input
358 if ((getRefSchemaUrl() == null) && (getOnlineRefSchema() == null)
359 && (getRefDatabase() == null)) {
360 throw new SchemaHandlingException(
361 "You must provide a reference schema.");
362 }
363
364 if ((getTargetSchemaUrl() == null) && (getOnlineTargetSchema() == null)
365 && (getTargetDatabase() == null)) {
366 throw new SchemaHandlingException(
367 "You must provide a target schema.");
368 }
369 }
370
371 /***
372 * Notify listeners of the specified event.
373 */
374 private void raiseEvent(int eventId, Object eventBean) {
375 DarrtEvent event = new DarrtEvent(eventId, eventBean);
376
377 for (Iterator i = getListeners().iterator(); i.hasNext();) {
378 DarrtListener listener = (DarrtListener) i.next();
379
380 listener.handle(event);
381 }
382 }
383
384 /***
385 * Order the schema's tables for data import (i.e. referenced tables first)
386 * @param schema The schema to order tables for.
387 * @return List of table names.
388 */
389 public List orderDependencies(Schema schema) {
390 List tables = new ArrayList() ;
391
392 for (int i = 0 ; i < schema.getTableCount() ; i++) {
393 Table table = schema.getTable(i) ;
394 tables.add(table.getName()) ;
395 }
396
397 for (int i = 0 ; i < tables.size() ; i++) {
398 // iterate cols, if find FK reiterate tables removing from current
399 // spot and adding after
400 }
401
402 return tables ;
403 }
404
405 }
This page was automatically generated by Maven