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