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.File; 24 import java.io.FileWriter; 25 import java.sql.Connection; 26 import java.sql.DatabaseMetaData; 27 import java.sql.ResultSet; 28 import java.text.SimpleDateFormat; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Date; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.StringTokenizer; 36 import java.util.TreeMap; 37 38 import javax.xml.parsers.DocumentBuilder; 39 import javax.xml.parsers.DocumentBuilderFactory; 40 import javax.xml.parsers.ParserConfigurationException; 41 42 import org.apache.log4j.Category; 43 import org.enableit.db.ConnFactory; 44 import org.enableit.db.DBException; 45 import org.enableit.db.DBUtils; 46 import org.enableit.db.SqlType; 47 import org.enableit.db.beans.Database; 48 import org.enableit.db.beans.Provider; 49 import org.enableit.db.beans.ProviderExt; 50 import org.enableit.db.beans.Schema; 51 import org.enableit.db.beans.Table; 52 import org.exolab.castor.xml.Marshaller; 53 import org.exolab.castor.xml.Unmarshaller; 54 import org.w3c.dom.Document; 55 import org.w3c.dom.Element; 56 57 58 /*** 59 * Export database schemas as XML. 60 * 61 * <p>Note on ASA interpretation of DatabaseMetaData object: 62 * SchemaConstants.SCHEMA = users and groups.</p> 63 */ 64 public class SchemaExporter extends AbstractSchemaHandler { 65 public static final String COL_TYPE = "col-type"; 66 public static final String NULLABLE = "null"; 67 public static final String PK = "primary-key"; 68 public static final String FK = "foreign-key"; 69 public static final String FK_TABLE = "fk-table-name"; 70 public static final String FK_DATA_COLUMN = "data-column"; 71 public static final String FK_DATA_COL_NAME = "col-name"; 72 public static final String FK_DISPLAY_COLUMN = "display-column"; 73 public static final String FK_DISPLAY_COL_NAME = "col-name"; 74 75 /*** 76 * Define a static Category instance for logging. 77 */ 78 private static Category logger = Category.getInstance(SchemaExporter.class); 79 private String catalog; 80 81 /*** 82 * The name of the database schema to be exported. 83 * <p>See JDBC documentation for definition of 'schema'. Actual implmentation 84 * varies from one RDBMS to the next.</p> 85 */ 86 private String targetSchema; 87 88 /*** 89 * File name for the exported database definition. 90 * <p>NB: MUST not contain characters that are illegal in file names.</p> 91 */ 92 private String fileName; 93 94 /*** 95 * The table types to export. 96 * <p>The available table types vary from one RDBMS to another, 97 * but typically includes TABLE and VIEW. Default is <em>TABLE</em></p> 98 */ 99 private String[] tableTypes = { "TABLE" }; 100 private boolean uniqueIndexOnly = false; 101 private boolean approxIndexStats = false; 102 private Provider provider; 103 104 /*** 105 * Responsible for exporting VIEW definitions. 106 */ 107 private ViewExporter viewExporter = new ViewExporter(); 108 109 /*** 110 * Formats date info within the schema export meta data. 111 */ 112 private SimpleDateFormat formatter; 113 114 /*** 115 * Default Constructor. 116 */ 117 public SchemaExporter() { 118 formatter = new SimpleDateFormat(); 119 } 120 121 /*** 122 * The name of the file to write schema to if <code>exportToFile</code> 123 * invoked. 124 * <p>If not specified the file will be named according to the logical 125 * name contained in the <code>Provider</code> parameter.</p> 126 * @param fileName to export to. 127 */ 128 public void setFileName(String fileName) { 129 logger.info("METHOD_ENTRY: setFileName: " + fileName); 130 131 this.fileName = fileName; 132 133 logger.info("METHOD_EXIT: setFileName"); 134 } 135 136 /*** 137 * Algorhythm is as follows: 138 * <ol> 139 * <li>If set, use the filename provided.</li> 140 * <li>If set, use the name of the provider.</li> 141 * <li>Use data source name or URL within <code>Provider</code> 142 * to infer a name that should be unique in most cases.</li> 143 * </ol> 144 * @return fileName To write to. 145 */ 146 public String getFileName(Provider provider) { 147 logger.info("METHOD_ENTRY: getFileName, provider: " 148 + ProviderExt.toString(provider)); 149 150 String retVal = fileName; 151 152 if (retVal == null) { 153 retVal = provider.getName(); 154 } 155 156 if (retVal == null) { 157 String dsName = provider.getJdbc2DatasourceName(); 158 159 if (dsName != null) { 160 if (dsName.indexOf("/") != -1) { 161 retVal = dsName.substring(dsName.lastIndexOf("/") + 1); 162 } else { 163 retVal = dsName; 164 } 165 } else { 166 retVal = provider.getUrl(); 167 retVal = retVal.replace(':', '_'); 168 retVal = retVal.replace('/', '_'); 169 } 170 171 if (!retVal.endsWith(".xml")) { 172 retVal += ".xml"; 173 } 174 } 175 176 logger.info("METHOD_EXIT: getFileName, fileName=" + retVal); 177 178 return retVal; 179 } 180 181 /*** 182 * The name of the schema to be exported. 183 * <p> 184 * If no schema is specified all will be exported. 185 */ 186 public void setSchemaName(String targetSchema) { 187 logger.info("METHOD_ENTRY: setSchemaName: " + targetSchema); 188 189 this.targetSchema = targetSchema; 190 191 logger.info("METHOD_EXIT: setSchemaName"); 192 } 193 194 /*** 195 * Set the table types to export. 196 */ 197 public void setTableTypes(String tableTypes) { 198 StringTokenizer st = new StringTokenizer(tableTypes, ","); 199 String[] tableTypesArray = new String[st.countTokens()]; 200 int i = 0; 201 202 while (st.hasMoreTokens()) { 203 tableTypesArray[i++] = st.nextToken().trim(); 204 } 205 206 setTableTypes(tableTypesArray); 207 } 208 209 /*** 210 * An array of table types to be exported. 211 * <p> 212 * Table types include such things as 'TABLE' and 'VIEW', 213 * though the exact list depends 214 * on the database vendor. 215 * <p> 216 * If no table types are specified all will be exported. 217 */ 218 public void setTableTypes(String[] tableTypes) { 219 if (tableTypes == null) { 220 this.tableTypes = null; 221 } else { 222 /* 223 * The objective here is to set the local array to the 224 * parameter array but to exclude VIEW 225 */ 226 this.tableTypes = new String[tableTypes.length - 1]; 227 logger.debug("No. of table types =" + tableTypes.length); 228 229 int j = 0; 230 231 try { 232 for (int i = 0; i < tableTypes.length; i++) { 233 if (!SchemaConstants.VIEW.equalsIgnoreCase(tableTypes[i])) { 234 this.tableTypes[j++] = tableTypes[i]; 235 } 236 } 237 } catch (ArrayIndexOutOfBoundsException e) { 238 this.tableTypes = tableTypes; 239 } 240 241 logger.info("tableTypes=" + Arrays.asList(this.tableTypes)); 242 } 243 244 logger.info("METHOD_EXIT: setTableTypes"); 245 } 246 247 /*** 248 * Export the schema identified by the <code>Provider</code> parameter. 249 * @param provider Encapsulation of the Data Source information. 250 */ 251 public Document export(Provider provider) 252 throws DBException { 253 logger.info("METHOD_ENTRY: export(Provider)"); 254 255 this.provider = provider; 256 257 Document doc = null; 258 Connection conn = null; 259 260 try { 261 conn = ConnFactory.getConnection(provider); 262 263 if (conn == null) { 264 throw new DBException("Null connection when connecting " 265 + "to database"); 266 } 267 268 doc = export(conn); 269 } catch (DBException e) { 270 throw e; 271 } catch (Exception e) { 272 logger.error(e.getMessage(), e); 273 throw new DBException(e.getClass().getName() + ":" + e.getMessage(), 274 e); 275 } finally { 276 try { 277 conn.close(); 278 } catch (Exception e) { 279 ; 280 } 281 282 conn = null; 283 } 284 285 logger.info("METHOD_EXIT: export(Provider)"); 286 287 return doc; 288 } 289 290 /*** 291 * Export the schema identified by the provided connection parameters. 292 */ 293 public Document export(String driver, String url, String userid, 294 String password) 295 throws DBException { 296 logger.info("METHOD_ENTRY: export: " + driver + "," + url + "," 297 + userid + "," + password); 298 299 Document doc = null; 300 301 try { 302 // Set the provider to avoid NPE in output 303 ProviderExt provider = new ProviderExt(); 304 305 provider.setConnectionProperties(driver, url, userid, password); 306 doc = export(provider); 307 } catch (DBException e) { 308 throw e; 309 } catch (Exception e) { 310 logger.error(e.getMessage()); 311 throw new DBException(e.getClass().getName() + ":" + e.getMessage(), 312 e); 313 } 314 315 logger.info("METHOD_EXIT: export"); 316 317 return doc; 318 } 319 320 /*** 321 * Export the schema identified by the provided connection parameters. 322 */ 323 public Document export(String dataSourceName) 324 throws DBException { 325 logger.info("METHOD_ENTRY: export(String): dataSourceName=" 326 + dataSourceName); 327 328 Document doc = null; 329 330 try { 331 // Set the provider to avoid NPE in output 332 ProviderExt provider = new ProviderExt(); 333 334 provider.setJdbc2DatasourceName(dataSourceName); 335 doc = export(provider); 336 } catch (DBException e) { 337 throw e; 338 } catch (Exception e) { 339 logger.error(e.getMessage(), e); 340 throw new DBException(e.getClass().getName() + ":" + e.getMessage()); 341 } 342 343 logger.info("METHOD_EXIT: export(String)"); 344 345 return doc; 346 } 347 348 /*** 349 * Export the schema at the database already connected to. 350 */ 351 private Document export(Connection conn) 352 throws DBException { 353 logger.info("METHOD_ENTRY: export(Connection)"); 354 355 Document doc = null; 356 357 // Construct an xml doc 358 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 359 360 try { 361 DocumentBuilder docBuilder = factory.newDocumentBuilder(); 362 363 doc = docBuilder.newDocument(); 364 } catch (ParserConfigurationException e) { 365 logger.error(e.getMessage(), e); 366 throw new DBException(e.getMessage(), e); 367 } 368 369 // get schema items to place in doc 370 try { 371 Element databaseTag = doc.createElement(SchemaConstants.ROOT_ELEMENT); 372 DatabaseMetaData dmd = conn.getMetaData(); 373 374 /* 375 * General database provider information 376 */ 377 Element providerTag = doc.createElement(SchemaConstants.PROVIDER); 378 379 Element nameTag = doc.createElement(SchemaConstants.NAME); 380 381 nameTag.appendChild(doc.createTextNode(dmd.getDatabaseProductName())); 382 providerTag.appendChild(nameTag); 383 384 Element productNameTag = doc.createElement(SchemaConstants.PRODUCT_NAME); 385 386 productNameTag.appendChild(doc.createTextNode( 387 dmd.getDatabaseProductName())); 388 providerTag.appendChild(productNameTag); 389 390 Element productVsnTag = doc.createElement(SchemaConstants.VSN); 391 392 productVsnTag.appendChild(doc.createTextNode( 393 dmd.getDatabaseProductVersion())); 394 providerTag.appendChild(productVsnTag); 395 396 // Database driver info 397 Element driverTag = doc.createElement(SchemaConstants.DRIVER); 398 Element driverNameTag = doc.createElement(SchemaConstants.NAME); 399 400 driverNameTag.appendChild(doc.createTextNode(dmd.getDriverName())); 401 driverTag.appendChild(driverNameTag); 402 403 Element driverVsnTag = doc.createElement(SchemaConstants.VSN); 404 405 driverVsnTag.appendChild(doc.createTextNode(dmd.getDriverVersion())); 406 driverTag.appendChild(driverVsnTag); 407 408 Element driverClassNameTag = doc.createElement(SchemaConstants.CLASS_NAME); 409 410 driverClassNameTag.appendChild(doc.createTextNode((provider 411 .getDriver() == null) ? "" 412 : provider.getDriver().getClassName())); 413 driverTag.appendChild(driverClassNameTag); 414 415 Element urlTag = doc.createElement(SchemaConstants.URL_STRING); 416 417 urlTag.appendChild(doc.createTextNode(dmd.getURL())); 418 driverTag.appendChild(urlTag); 419 420 Element usernameTag = doc.createElement(SchemaConstants.USERNAME); 421 422 usernameTag.appendChild(doc.createTextNode(dmd.getUserName())); 423 driverTag.appendChild(usernameTag); 424 425 Element passwordTag = doc.createElement(SchemaConstants.PASSWORD); 426 String password = (provider.getPassword() == null) ? "" 427 : provider 428 .getPassword(); 429 430 passwordTag.appendChild(doc.createTextNode(password)); 431 driverTag.appendChild(passwordTag); 432 433 providerTag.appendChild(driverTag); 434 435 Element creationDateTag = doc.createElement(SchemaConstants.CREATION_DATE); 436 437 creationDateTag.appendChild(doc.createTextNode(formatter.format( 438 new Date()))); 439 providerTag.appendChild(creationDateTag); 440 441 databaseTag.appendChild(providerTag); 442 443 /* 444 * Database metadata information (not sure if this is useful yet) 445 */ 446 Element metadataTag = doc.createElement(SchemaConstants.METADATA); 447 Element catalogsTag = doc.createElement(SchemaConstants.CATALOGS); 448 ResultSet catalogs = dmd.getCatalogs(); 449 450 while (catalogs.next()) { 451 //logger.debug("TABLE TYPE: " + tableTypes.getString("TABLE_TYPE")); 452 Element catalogNameTag = doc.createElement(SchemaConstants.NAME); 453 454 catalogNameTag.appendChild(doc.createTextNode( 455 catalogs.getString("TABLE_CAT").trim())); 456 catalogsTag.appendChild(catalogNameTag); 457 } 458 459 catalogs.close(); 460 metadataTag.appendChild(catalogsTag); 461 462 Element tableTypesTag = doc.createElement(SchemaConstants.TABLE_TYPES); 463 ResultSet tableTypes = dmd.getTableTypes(); 464 465 while (tableTypes.next()) { 466 //logger.debug("TABLE TYPE: " + tableTypes.getString("TABLE_TYPE")); 467 Element tableTypeNameTag = doc.createElement(SchemaConstants.NAME); 468 469 tableTypeNameTag.appendChild(doc.createTextNode( 470 tableTypes.getString("TABLE_TYPE").trim())); 471 tableTypesTag.appendChild(tableTypeNameTag); 472 } 473 474 metadataTag.appendChild(tableTypesTag); 475 476 Element catalogTermTag = doc.createElement(SchemaConstants.CATALOG_TERM); 477 478 catalogTermTag.appendChild(doc.createTextNode(dmd.getCatalogTerm())); 479 metadataTag.appendChild(catalogTermTag); 480 481 Element schemaTermTag = doc.createElement(SchemaConstants.SCHEMA_TERM); 482 483 schemaTermTag.appendChild(doc.createTextNode(dmd.getSchemaTerm())); 484 metadataTag.appendChild(schemaTermTag); 485 486 if (getDebug()) { 487 databaseTag.appendChild(metadataTag); 488 } 489 490 /* 491 * Get the schema data (including the table data) 492 */ 493 ResultSet schemas = dmd.getSchemas(); 494 ArrayList schList = (ArrayList) DBUtils.convertResultToList(schemas); 495 496 for (Iterator i = schList.iterator(); i.hasNext();) { 497 Element schemaTag = doc.createElement(SchemaConstants.SCHEMA); 498 TreeMap row = (TreeMap) i.next(); 499 String schemaName = (String) row.get("TABLE_SCHEM"); 500 501 logger.debug("Found schema named: " + schemaName); 502 503 // If no target schema was specified, or if this is the one specified - export it 504 if ((targetSchema == null) 505 || schemaName.equalsIgnoreCase(targetSchema)) { 506 logger.debug("Exporting schema named: " + schemaName); 507 508 Element schemaNameTag = doc.createElement(SchemaConstants.NAME); 509 510 schemaNameTag.appendChild(doc.createTextNode(schemaName)); 511 schemaTag.appendChild(schemaNameTag); 512 513 if (this.tableTypes.length > 0) { 514 addTables(dmd, doc, schemaName, schemaTag, 515 this.tableTypes); 516 } 517 518 String[] viewType = new String[1]; 519 520 viewType[0] = "VIEW"; // TODO is this constant for all dbms? 521 viewExporter.setProvider(provider); 522 addTables(dmd, doc, schemaName, schemaTag, viewType); 523 databaseTag.appendChild(schemaTag); 524 } else { 525 logger.debug("Ignoring schema named: " + schemaName); 526 } 527 } 528 529 doc.appendChild(databaseTag); 530 } catch (Exception e) { 531 // Things to catch : array out of bounds from no column results found 532 logger.error(e.getMessage(), e); 533 } 534 535 logger.info("METHOD_EXIT: export(Connection)"); 536 537 return doc; 538 } 539 540 /*** 541 * Extract table metadata adding appropriate tags. 542 */ 543 private void addTables(DatabaseMetaData dmd, Document doc, 544 String schemaName, Element schemaTag, String[] tableTypes) 545 throws SchemaHandlingException { 546 logger.info("METHOD_ENTRY: addTables, schemaName: " + schemaName); 547 548 try { 549 ResultSet tables = dmd.getTables(null, schemaName, 550 getTablePattern(), tableTypes); 551 552 while (tables.next()) { 553 String tableName = tables.getString("TABLE_NAME"); 554 String tableType = tables.getString("TABLE_TYPE"); 555 556 /* 557 * The table portion 558 */ 559 Element tableTag = null; 560 561 if (SchemaConstants.VIEW.equalsIgnoreCase(tableType)) { 562 tableTag = doc.createElement(SchemaConstants.VIEW); 563 } else { 564 tableTag = doc.createElement(SchemaConstants.TABLE); 565 } 566 567 Element nameTag = doc.createElement(SchemaConstants.NAME); 568 569 nameTag.appendChild(doc.createTextNode(tableName)); 570 tableTag.appendChild(nameTag); 571 572 Element typeTag = doc.createElement(SchemaConstants.TYPE); 573 574 typeTag.appendChild(doc.createTextNode(tableType)); 575 tableTag.appendChild(typeTag); 576 577 // Capture Primary Key Name 578 ResultSet pk = dmd.getPrimaryKeys(catalog, schemaName, tableName); 579 ArrayList pkList = new ArrayList(); 580 581 while (pk.next()) { 582 logger.debug("Primary Key List: " + pkList.toString()); 583 584 String pkValue = pk.getString("COLUMN_NAME"); 585 586 pkList.add(pkValue); 587 } 588 589 pk.close(); 590 591 if (pkList.size() == 0) { 592 logger.warn("No primary key was found for " + tableName); 593 594 //throw new DBException(msg) ; 595 } 596 597 // All columns' data 598 ResultSet columns = dmd.getColumns(catalog, schemaName, 599 tableName, null); 600 ArrayList colList = (ArrayList) DBUtils.convertResultToList(columns); 601 602 columns.close(); 603 604 ResultSet exKeys = dmd.getExportedKeys(catalog, schemaName, 605 tableName); 606 607 //ArrayList fKeyList = (ArrayList)DBUtils.convertResultToList(exKeys) ; 608 ArrayList exKeyList = new ArrayList(); 609 610 while (exKeys.next()) { 611 exKeyList.add(exKeys.getString("PKCOLUMN_NAME")); 612 } 613 614 exKeys.close(); 615 616 // TODO: Nothing done with the exported keys yet... 617 //pks.add(((TreeMap)colList.get(0)).get("COLUMN_NAME")) ; 618 // All foreign keys' data 619 ResultSet fKeys = dmd.getImportedKeys(catalog, schemaName, 620 tableName); 621 ArrayList fKeyList = (ArrayList) DBUtils.convertResultToList(fKeys); 622 623 //logger.debug("FKs found=" + fKeyList); 624 fKeys.close(); 625 626 // For each list element (represents a table column) ... 627 Element columnTag = null; 628 629 for (Iterator i = colList.iterator(); i.hasNext();) { 630 TreeMap tableCol = (TreeMap) i.next(); 631 632 // ... for each column construct column element 633 columnTag = doc.createElement(SchemaConstants.COLUMN); 634 635 // get column name & description 636 String colName = (String) tableCol.get("COLUMN_NAME"); 637 Element colDescTag = doc.createElement(SchemaConstants.DESCRIPTION); 638 639 colDescTag.appendChild(doc.createTextNode( 640 (String) tableCol.get("REMARKS"))); 641 642 Element columnNameTag = doc.createElement(SchemaConstants.COL_NAME); 643 644 columnNameTag.appendChild(doc.createTextNode(colName)); 645 columnTag.appendChild(colDescTag); 646 columnTag.appendChild(columnNameTag); 647 648 // get column datatype 649 // defined as short in JDBC doco, Oracle returns BigDecimal, Sybase an Integer 650 Number typeObj = (Number) tableCol.get("DATA_TYPE"); 651 int type = ((typeObj == null) ? Integer.MIN_VALUE 652 : typeObj.intValue()); 653 Number scaleObj = (Number) tableCol.get("COLUMN_SIZE"); 654 int scale = ((scaleObj == null) ? Integer.MIN_VALUE 655 : scaleObj.intValue()); 656 Number precisionObj = (Number) tableCol.get( 657 "DECIMAL_DIGITS"); 658 int precision = ((precisionObj == null) ? Integer.MIN_VALUE 659 : precisionObj 660 .intValue()); 661 String colType = new SqlType(type, scale, precision) 662 .toString(); 663 Element columnTypeTag = doc.createElement(COL_TYPE); 664 665 columnTypeTag.appendChild(doc.createTextNode(colType)); 666 columnTag.appendChild(columnTypeTag); 667 668 String colDefault = (String) tableCol.get("COLUMN_DEF"); 669 670 if (colDefault != null) { 671 logger.debug("Column " + colName + " has default: " 672 + colDefault); 673 674 // Translate to generic name for recognised defaults 675 DDLGenerator dbms = DDLFactory.getInstance().getGenerator(provider) ; 676 colDefault = dbms.getGenericDefault(colDefault) ; 677 678 Element colDefaultTag = doc.createElement(SchemaConstants.DEFAULT); 679 680 colDefaultTag.appendChild(doc.createTextNode(colDefault)); 681 columnTag.appendChild(colDefaultTag); 682 } 683 684 // get column null / not null 685 // assume the worst (includes don't know case) 686 boolean isNullable = false; 687 688 logger.debug("Nullable: " 689 + tableCol.get("IS_NULLABLE").toString()); 690 691 if (((String) tableCol.get("IS_NULLABLE")).equals("YES")) { 692 isNullable = true; 693 } 694 695 Element nullableTag = doc.createElement(NULLABLE); 696 697 nullableTag.appendChild(doc.createTextNode(String.valueOf( 698 isNullable))); 699 columnTag.appendChild(nullableTag); 700 701 // determine if column is primary key 702 if (pkList.contains(colName)) { 703 Element pkTag = doc.createElement(SchemaConstants.PK); 704 705 pkTag.appendChild(doc.createTextNode("true")); 706 columnTag.appendChild(pkTag); 707 } 708 709 // determine if column is a foreign key 710 if (fKeyList.size() > 1) { 711 logger.warn("More than one foreign key for " + colName) ; 712 logger.warn("... all but 1st will be ignored:" + fKeyList) ; 713 } 714 //for (Iterator j = fKeyList.iterator(); j.hasNext();) { 715 if (fKeyList.size() > 0) { 716 TreeMap key = (TreeMap) fKeyList.get(0); 717 String fKeyName = (String) key.get("FKCOLUMN_NAME"); 718 719 if ((fKeyName != null) && fKeyName.equals(colName)) { 720 logger.debug("Found foreign key for column: " 721 + fKeyName); 722 723 Element fkTag = doc.createElement(FK); 724 Element fkTableTag = doc.createElement(FK_TABLE); 725 String fkTableName = (String) key.get( 726 "PKTABLE_NAME"); 727 728 fkTableTag.appendChild(doc.createTextNode( 729 fkTableName)); 730 fkTag.appendChild(fkTableTag); 731 732 // foreign key data col (the actual foreign key) 733 Element fkDataColTag = doc.createElement(FK_DATA_COLUMN); 734 String pkName = (String) key.get("PKCOLUMN_NAME"); 735 Element fkDataColDescTag = doc.createElement(SchemaConstants.DESCRIPTION); 736 737 fkDataColDescTag.appendChild(doc.createTextNode( 738 (String) key.get("REMARKS"))); 739 fkDataColTag.appendChild(fkDataColDescTag); 740 741 Element fkDataColNameTag = doc.createElement(SchemaConstants.FK_DATA_COL_NAME); 742 743 fkDataColNameTag.appendChild(doc.createTextNode( 744 pkName)); 745 746 // fk display column 747 Element fkDisplayColTag = doc.createElement(FK_DISPLAY_COLUMN); 748 Element fkDisplayColNameTag = doc.createElement(SchemaConstants.FK_DISPLAY_COL_NAME); 749 750 // try to guess display name ... 751 ResultSet fkRS = dmd.getColumns(catalog, 752 schemaName, fkTableName, null); 753 List fkList = DBUtils.convertResultToList(fkRS); 754 //System.out.println("FKs for " + colName + ": " + fkList); 755 756 fkRS.close(); 757 fkRS = null; 758 759 String displayName = null; 760 761 logger.debug("Looking for display col for: " 762 + fkTableName + "." + pkName); 763 764 for (Iterator k = fkList.iterator(); k.hasNext();) { 765 Map fk = (Map) k.next(); 766 String fkColName = (String) fk.get( 767 "COLUMN_NAME"); 768 769 logger.debug("... examining " + fkColName); 770 771 if (fkColName.equalsIgnoreCase(fkTableName 772 + "name") 773 || fkColName.equalsIgnoreCase(fkTableName 774 + "_name")) { 775 displayName = fkColName; 776 logger.debug("... found: " + fkColName); 777 778 break; 779 } 780 } 781 782 if (displayName == null) { 783 for (Iterator k = fkList.iterator(); 784 k.hasNext();) { 785 Map fk = (Map) k.next(); 786 String fkColName = (String) fk.get( 787 "COLUMN_NAME"); 788 789 logger.debug("... examining " + fkColName); 790 791 if (fkColName.equalsIgnoreCase(fkTableName 792 + "desc") 793 || fkColName.equalsIgnoreCase(fkTableName 794 + "_desc")) { 795 displayName = fkColName; 796 logger.debug("... found: " + fkColName); 797 798 break; 799 } 800 } 801 } 802 803 // ... fall back on data column's name 804 if (displayName == null) { 805 displayName = pkName; 806 } 807 808 fkDisplayColNameTag.appendChild(doc.createTextNode( 809 displayName)); 810 fkDataColTag.appendChild(fkDataColNameTag); 811 812 Element fkDisplayColDescTag = doc.createElement(SchemaConstants.DESCRIPTION); 813 814 fkDisplayColDescTag.appendChild(doc.createTextNode( 815 (String) key.get("REMARKS"))); 816 fkDisplayColTag.appendChild(fkDisplayColDescTag); 817 fkDisplayColTag.appendChild(fkDisplayColNameTag); 818 fkTag.appendChild(fkDataColTag); 819 fkTag.appendChild(fkDisplayColTag); 820 821 columnTag.appendChild(fkTag); 822 } 823 } 824 825 tableTag.appendChild(columnTag); 826 } 827 828 if (SchemaConstants.VIEW.equalsIgnoreCase(tableType)) { 829 // For views find, their definition 830 Element definitionTag = doc.createElement(SchemaConstants.DEFINITION); 831 String definition = viewExporter.export(dmd.getConnection(), 832 tableName); 833 834 logger.debug("definition=" + definition); 835 definitionTag.appendChild(doc.createTextNode(definition)); 836 tableTag.appendChild(definitionTag); 837 } else { 838 // Get Index info - 1st null indicates drop catalog from criteria 839 ResultSet indexes = dmd.getIndexInfo(null, schemaName, 840 tableName, uniqueIndexOnly, approxIndexStats); 841 int indexCount = 0; 842 String lastIndexName = null; 843 Element indexTag = null; 844 845 while (indexes.next()) { 846 switch (indexes.getInt("TYPE")) { 847 case DatabaseMetaData.tableIndexStatistic: 848 849 // do nothing 850 break; 851 default: 852 853 String indexName = indexes.getString("INDEX_NAME"); 854 855 if ((lastIndexName == null) 856 || !indexName.equals(lastIndexName)) { 857 // Create new index 858 if ((indexTag != null) 859 && (lastIndexName != null)) { 860 tableTag.appendChild(indexTag); 861 } 862 863 indexCount++; 864 indexTag = doc.createElement(SchemaConstants.INDEX); 865 866 Element indexNameTag = doc.createElement(SchemaConstants.NAME); 867 868 indexNameTag.appendChild(doc.createTextNode( 869 String.valueOf(indexName))); 870 indexTag.appendChild(indexNameTag); 871 872 Element indexTypeTag = doc.createElement(SchemaConstants.TYPE); 873 874 indexTypeTag.appendChild(doc.createTextNode( 875 String.valueOf(indexes.getInt("TYPE")))); 876 indexTag.appendChild(indexTypeTag); 877 878 Element indexUniqueTag = doc.createElement(SchemaConstants.INDEX_UNIQUE); 879 880 // This is horrible - the jdk1.3.1 javadoc says the column should be called 'NON-UNIQUE' 881 String unique = "UNKNOWN"; 882 883 if ((provider != null) 884 && (provider.getDriver() != null) 885 && (provider.getDriver().getName() != null)) { 886 if (provider.getDriver().getName().indexOf("sybase") > 0) { 887 unique = (indexes.getBoolean( 888 "TABLE_NAME") ? "UNIQUE" 889 : "NON-UNIQUE"); 890 } else if (provider.getDriver().getName() 891 .indexOf("microsoft") > 0) { 892 unique = (indexes.getBoolean( 893 "TABLE_NAME") ? "UNIQUE" 894 : "NON-UNIQUE"); 895 } else if (provider.getDriver().getName() 896 .indexOf("oracle") > 0) { 897 unique = (indexes.getBoolean( 898 "TABLE_NAME") ? "UNIQUE" 899 : "NON-UNIQUE"); 900 } 901 } 902 903 indexUniqueTag.appendChild(doc.createTextNode( 904 unique)); 905 indexTag.appendChild(indexUniqueTag); 906 } 907 908 // Add index column details 909 Element colNameTag = doc.createElement(SchemaConstants.COL_NAME); 910 911 colNameTag.appendChild(doc.createTextNode( 912 indexes.getString("COLUMN_NAME"))); 913 indexTag.appendChild(colNameTag); 914 915 lastIndexName = indexName; 916 } 917 } 918 919 if (indexTag != null) { 920 tableTag.appendChild(indexTag); 921 } 922 923 indexCount++; 924 925 logger.warn("Index count for table " + tableName + "=" 926 + indexCount); 927 indexes.close(); 928 } 929 930 schemaTag.appendChild(tableTag); 931 } 932 933 // end while have tables 934 } catch (Exception e) { 935 logger.error("Error whilst trying to process schema named: " 936 + schemaName, e); 937 throw new SchemaHandlingException(e.getMessage()); 938 } 939 940 logger.info("METHOD_EXIT: addTables"); 941 } 942 943 /*** 944 * Export the schema identified by the <code>Provider</code> parameter 945 * to a file. 946 * <p>To control where the file is written call <code>setOperDir</code> 947 * first.</p> 948 * @param provider Encapsulation of the Data Source information. 949 * @param outputMode Controls the number of files output. 950 * Enumerated in <code>SchemaConstants</code>. One of <code> 951 * OM_SINGLE_FILE, OM_SINGLE_FILE_AND_FILE_PER_TABLE, 952 * OM_FILE_PER_TABLE.</code> 953 */ 954 public Document exportToFile(Provider provider, int outputMode) 955 throws DBException { 956 logger.info("METHOD_ENTRY: exportToFile(Provider)"); 957 958 Document doc = null; 959 960 try { 961 File schemaFile = new File(getOperDir(), getFileName(provider)); 962 Database database = null; 963 964 doc = export(provider); 965 966 FileWriter fw = null; 967 968 logger.debug("Output mode is:" + outputMode); 969 970 switch (outputMode) { 971 case SchemaConstants.OM_SINGLE_FILE: 972 973 // deliberately omit break ; 974 case SchemaConstants.OM_SINGLE_FILE_AND_FILE_PER_TABLE: 975 976 if (!getOperDir().exists()) { 977 getOperDir().mkdirs(); 978 } 979 980 logger.warn("Writing single schema file to: " + schemaFile); 981 database = (Database) Unmarshaller.unmarshal(Database.class, doc); 982 fw = new FileWriter(schemaFile); 983 Marshaller.marshal(database, fw); 984 985 // TODO: replace Castor with Axis, because of pretty printing 986 //DOM2Writer.serializeAsXML(doc, fw, false, true) ; 987 fw.close(); 988 fw = null; // encourage garbage collection 989 990 if (outputMode == SchemaConstants.OM_SINGLE_FILE) { 991 break; // otherwise continue with file per table as well 992 } 993 994 // else deliberately omit break ; 995 case SchemaConstants.OM_FILE_PER_TABLE: 996 database = (Database) Unmarshaller.unmarshal(Database.class, doc); 997 998 File outputDir = new File(getOperDir(), 999 getFileName(provider).substring(0, 1000 getFileName(provider).indexOf(".xml"))); 1001 1002 logger.warn("Writing schema file per table in dir: " 1003 + outputDir); 1004 1005 if (!outputDir.exists()) { 1006 outputDir.mkdirs(); 1007 } 1008 1009 /* TODO: This sort of works but leads to funny whitespace (not 'pretty') 1010 NodeList tables = doc.getElementsByTagName("table") ; 1011 for (int j = 0; j < tables.getLength() ; j++) { 1012 String name = null; 1013 Node table = tables.item(j) ; 1014 NodeList tableChildren = table.getChildNodes() ; 1015 for (int k = 0 ; k < tableChildren.getLength() ; k++) { 1016 Node tableChild = tableChildren.item(k) ; 1017 if ("name".equals(tableChild.getNodeName())) { 1018 name = tableChild.getFirstChild().getNodeValue() ; 1019 break ; 1020 } 1021 } 1022 File file = new File(outputDir, name + ".xml") ; 1023 fw = new FileWriter(file) ; 1024 DOM2Writer.serializeAsXML(doc, fw, false, true) ; // true => pretty 1025 fw.close() ; 1026 fw = null; // encourage garbage collection 1027 }*/ 1028 for (int j = 0; j < database.getSchemaCount(); j++) { 1029 Schema schema = database.getSchema(j); 1030 1031 for (int i = 0; i < schema.getTableCount(); i++) { 1032 Table table = schema.getTable(i); 1033 File file = new File(outputDir, table.getName() 1034 + ".xml"); 1035 1036 fw = new FileWriter(file); 1037 Marshaller.marshal(table, fw); 1038 fw.close(); 1039 fw = null; // encourage garbage collection 1040 } 1041 } 1042 1043 break; 1044 default: 1045 logger.error("Unsupported output mode: " + outputMode); 1046 } 1047 } catch (DBException e) { 1048 throw e; 1049 } catch (Exception e) { 1050 logger.error(e.getMessage(), e); 1051 throw new DBException(e.getClass().getName() + ":" + e.getMessage(), 1052 e); 1053 } 1054 1055 logger.info("METHOD_EXIT: exportToFile(Provider)"); 1056 1057 return doc; 1058 } 1059 1060 /*** 1061 * 1062 * 1063 * @param args 1064 * args[0] - tableName to turn into XML representation 1065 */ 1066 public static void main(String[] args) { 1067 try { 1068 SchemaExporter transformer = new SchemaExporter(); 1069 1070 transformer.export(args[0], args[1], args[2], args[3]); 1071 } catch (Exception e) { 1072 System.out.println("Failed: " + e.getMessage()); 1073 } 1074 } 1075 1076 /*** 1077 * Returns the directory name classes should be stored in based on the 1078 * Java package name received 1079 */ 1080 private String getPackageDir(String javaPackage) { 1081 StringBuffer dir = new StringBuffer(); 1082 StringTokenizer st = new StringTokenizer(javaPackage, "."); 1083 1084 while (st.hasMoreTokens()) { 1085 dir.append(st.nextToken()); 1086 dir.append("/"); 1087 } 1088 1089 // remove final '/' and return 1090 return dir.substring(0, dir.length() - 1); 1091 } 1092 }

This page was automatically generated by Maven