View Javadoc
1 package org.apache.torque.task; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001-2003 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache Turbine" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache Turbine", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>. 55 */ 56 57 import java.io.FileOutputStream; 58 import java.io.PrintWriter; 59 60 import java.sql.Connection; 61 import java.sql.DatabaseMetaData; 62 import java.sql.DriverManager; 63 import java.sql.ResultSet; 64 import java.sql.SQLException; 65 import java.sql.Types; 66 67 import java.util.ArrayList; 68 import java.util.Collection; 69 import java.util.Hashtable; 70 import java.util.Iterator; 71 import java.util.List; 72 73 import org.apache.tools.ant.BuildException; 74 import org.apache.tools.ant.Task; 75 76 import org.apache.torque.engine.database.model.TypeMap; 77 import org.apache.torque.engine.database.transform.DTDResolver; 78 79 import org.apache.xerces.dom.DocumentImpl; 80 import org.apache.xerces.dom.DocumentTypeImpl; 81 82 import org.apache.xml.serialize.Method; 83 import org.apache.xml.serialize.OutputFormat; 84 import org.apache.xml.serialize.XMLSerializer; 85 86 import org.w3c.dom.Element; 87 import org.w3c.dom.Node; 88 89 /*** 90 * This class generates an XML schema of an existing database from 91 * JDBC metadata. 92 * 93 * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a> 94 * @author <a href="mailto:fedor.karpelevitch@barra.com">Fedor Karpelevitch</a> 95 * @version $Id: TorqueJDBCTransformTask.java,v 1.6 2003/08/22 17:01:42 mpoeschl Exp $ 96 */ 97 public class TorqueJDBCTransformTask extends Task 98 { 99 /*** Name of XML database schema produced. */ 100 protected String xmlSchema; 101 102 /*** JDBC URL. */ 103 protected String dbUrl; 104 105 /*** JDBC driver. */ 106 protected String dbDriver; 107 108 /*** JDBC user name. */ 109 protected String dbUser; 110 111 /*** JDBC password. */ 112 protected String dbPassword; 113 114 /*** DB schema to use. */ 115 protected String dbSchema; 116 117 /*** DOM document produced. */ 118 protected DocumentImpl doc; 119 120 /*** The document root element. */ 121 protected Node databaseNode; 122 123 /*** Hashtable of columns that have primary keys. */ 124 protected Hashtable primaryKeys; 125 126 /*** Hashtable to track what table a column belongs to. */ 127 protected Hashtable columnTableMap; 128 129 protected boolean sameJavaName; 130 131 private XMLSerializer xmlSerializer; 132 133 public String getDbSchema() 134 { 135 return dbSchema; 136 } 137 138 public void setDbSchema(String dbSchema) 139 { 140 this.dbSchema = dbSchema; 141 } 142 143 public void setDbUrl(String v) 144 { 145 dbUrl = v; 146 } 147 148 public void setDbDriver(String v) 149 { 150 dbDriver = v; 151 } 152 153 public void setDbUser(String v) 154 { 155 dbUser = v; 156 } 157 158 public void setDbPassword(String v) 159 { 160 dbPassword = v; 161 } 162 163 public void setOutputFile (String v) 164 { 165 xmlSchema = v; 166 } 167 168 public void setSameJavaName(boolean v) 169 { 170 this.sameJavaName = v; 171 } 172 173 public boolean isSameJavaName() 174 { 175 return this.sameJavaName; 176 } 177 178 /*** 179 * Default constructor. 180 * 181 * @throws BuildException 182 */ 183 public void execute() throws BuildException 184 { 185 log("Torque - JDBCToXMLSchema starting"); 186 log("Your DB settings are:"); 187 log("driver : " + dbDriver); 188 log("URL : " + dbUrl); 189 log("user : " + dbUser); 190 // log("password : " + dbPassword); 191 log("schema : " + dbSchema); 192 193 DocumentTypeImpl docType = new DocumentTypeImpl(null, "database", null, 194 DTDResolver.WEB_SITE_DTD); 195 doc = new DocumentImpl(docType); 196 doc.appendChild(doc.createComment( 197 " Autogenerated by JDBCToXMLSchema! ")); 198 199 try 200 { 201 generateXML(); 202 log(xmlSchema); 203 xmlSerializer = new XMLSerializer( 204 new PrintWriter( 205 new FileOutputStream(xmlSchema)), 206 new OutputFormat(Method.XML, null, true)); 207 xmlSerializer.serialize(doc); 208 } 209 catch (Exception e) 210 { 211 throw new BuildException(e); 212 } 213 log("Torque - JDBCToXMLSchema finished"); 214 } 215 216 /*** 217 * Generates an XML database schema from JDBC metadata. 218 * 219 * @throws Exception a generic exception. 220 */ 221 public void generateXML() throws Exception 222 { 223 // Load the Interbase Driver. 224 Class.forName(dbDriver); 225 log("DB driver sucessfuly instantiated"); 226 227 // Attemtp to connect to a database. 228 Connection con = DriverManager.getConnection(dbUrl, dbUser, dbPassword); 229 log("DB connection established"); 230 231 // Get the database Metadata. 232 DatabaseMetaData dbMetaData = con.getMetaData(); 233 234 // The database map. 235 List tableList = getTableNames(dbMetaData); 236 237 databaseNode = doc.createElement("database"); 238 239 // Build a database-wide column -> table map. 240 columnTableMap = new Hashtable(); 241 242 log("Building column/table map..."); 243 for (int i = 0; i < tableList.size(); i++) 244 { 245 String curTable = (String) tableList.get(i); 246 List columns = getColumns(dbMetaData, curTable); 247 248 for (int j = 0; j < columns.size(); j++) 249 { 250 List col = (List) columns.get(j); 251 String name = (String) col.get(0); 252 253 columnTableMap.put(name, curTable); 254 } 255 } 256 257 for (int i = 0; i < tableList.size(); i++) 258 { 259 // Add Table. 260 String curTable = (String) tableList.get(i); 261 // dbMap.addTable(curTable); 262 log("Processing table: " + curTable); 263 264 Element table = doc.createElement("table"); 265 table.setAttribute("name", curTable); 266 if (isSameJavaName()) 267 { 268 table.setAttribute("javaName", curTable); 269 } 270 271 // Add Columns. 272 // TableMap tblMap = dbMap.getTable(curTable); 273 274 List columns = getColumns(dbMetaData, curTable); 275 List primKeys = getPrimaryKeys(dbMetaData, curTable); 276 Collection forgnKeys = getForeignKeys(dbMetaData, curTable); 277 278 // Set the primary keys. 279 primaryKeys = new Hashtable(); 280 281 for (int k = 0; k < primKeys.size(); k++) 282 { 283 String curPrimaryKey = (String) primKeys.get(k); 284 primaryKeys.put(curPrimaryKey, curPrimaryKey); 285 } 286 287 for (int j = 0; j < columns.size(); j++) 288 { 289 List col = (List) columns.get(j); 290 String name = (String) col.get(0); 291 Integer type = ((Integer) col.get(1)); 292 int size = ((Integer) col.get(2)).intValue(); 293 294 // From DatabaseMetaData.java 295 // 296 // Indicates column might not allow NULL values. Huh? 297 // Might? Boy, that's a definitive answer. 298 /* int columnNoNulls = 0; */ 299 300 // Indicates column definitely allows NULL values. 301 /* int columnNullable = 1; */ 302 303 // Indicates NULLABILITY of column is unknown. 304 /* int columnNullableUnknown = 2; */ 305 306 Integer nullType = (Integer) col.get(3); 307 String defValue = (String) col.get(4); 308 309 Element column = doc.createElement("column"); 310 column.setAttribute("name", name); 311 if (isSameJavaName()) 312 { 313 column.setAttribute("javaName", name); 314 } 315 column.setAttribute("type", TypeMap.getTorqueType(type)); 316 317 if (size > 0 && (type.intValue() == Types.CHAR 318 || type.intValue() == Types.VARCHAR 319 || type.intValue() == Types.LONGVARCHAR 320 || type.intValue() == Types.DECIMAL 321 || type.intValue() == Types.NUMERIC)) 322 { 323 column.setAttribute("size", String.valueOf(size)); 324 } 325 326 if (nullType.intValue() == 0) 327 { 328 column.setAttribute("required", "true"); 329 } 330 331 if (primaryKeys.containsKey(name)) 332 { 333 column.setAttribute("primaryKey", "true"); 334 } 335 336 if (defValue != null) 337 { 338 // trim out parens & quotes out of def value. 339 // makes sense for MSSQL. not sure about others. 340 if (defValue.startsWith("(") && defValue.endsWith(")")) 341 { 342 defValue = defValue.substring(1, defValue.length() - 1); 343 } 344 345 if (defValue.startsWith("'") && defValue.endsWith("'")) 346 { 347 defValue = defValue.substring(1, defValue.length() - 1); 348 } 349 350 column.setAttribute("default", defValue); 351 } 352 table.appendChild(column); 353 } 354 355 // Foreign keys for this table. 356 for (Iterator l = forgnKeys.iterator(); l.hasNext();) 357 { 358 Object[] forKey = (Object[]) l.next(); 359 String foreignKeyTable = (String) forKey[0]; 360 List refs = (List) forKey[1]; 361 Element fk = doc.createElement("foreign-key"); 362 fk.setAttribute("foreignTable", foreignKeyTable); 363 for (int m = 0; m < refs.size(); m++) 364 { 365 Element ref = doc.createElement("reference"); 366 String[] refData = (String[]) refs.get(m); 367 ref.setAttribute("local", refData[0]); 368 ref.setAttribute("foreign", refData[1]); 369 fk.appendChild(ref); 370 } 371 table.appendChild(fk); 372 } 373 databaseNode.appendChild(table); 374 } 375 doc.appendChild(databaseNode); 376 } 377 378 /*** 379 * Get all the table names in the current database that are not 380 * system tables. 381 * 382 * @param dbMeta JDBC database metadata. 383 * @return The list of all the tables in a database. 384 * @throws SQLException 385 */ 386 public List getTableNames(DatabaseMetaData dbMeta) 387 throws SQLException 388 { 389 log("Getting table list..."); 390 List tables = new ArrayList(); 391 ResultSet tableNames = null; 392 // these are the entity types we want from the database 393 String[] types = {"TABLE", "VIEW"}; 394 try 395 { 396 tableNames = dbMeta.getTables(null, dbSchema, "%", types); 397 while (tableNames.next()) 398 { 399 String name = tableNames.getString(3); 400 String type = tableNames.getString(4); 401 tables.add(name); 402 } 403 } 404 finally 405 { 406 if (tableNames != null) 407 { 408 tableNames.close(); 409 } 410 } 411 return tables; 412 } 413 414 /*** 415 * Retrieves all the column names and types for a given table from 416 * JDBC metadata. It returns a List of Lists. Each element 417 * of the returned List is a List with: 418 * 419 * element 0 => a String object for the column name. 420 * element 1 => an Integer object for the column type. 421 * element 2 => size of the column. 422 * element 3 => null type. 423 * 424 * @param dbMeta JDBC metadata. 425 * @param tableName Table from which to retrieve column information. 426 * @return The list of columns in <code>tableName</code>. 427 * @throws SQLException 428 */ 429 public List getColumns(DatabaseMetaData dbMeta, String tableName) 430 throws SQLException 431 { 432 List columns = new ArrayList(); 433 ResultSet columnSet = null; 434 try 435 { 436 columnSet = dbMeta.getColumns(null, dbSchema, tableName, null); 437 while (columnSet.next()) 438 { 439 String name = columnSet.getString(4); 440 Integer sqlType = new Integer(columnSet.getString(5)); 441 Integer size = new Integer(columnSet.getInt(7)); 442 Integer nullType = new Integer(columnSet.getInt(11)); 443 String defValue = columnSet.getString(13); 444 445 List col = new ArrayList(5); 446 col.add(name); 447 col.add(sqlType); 448 col.add(size); 449 col.add(nullType); 450 col.add(defValue); 451 columns.add(col); 452 } 453 } 454 finally 455 { 456 if (columnSet != null) 457 { 458 columnSet.close(); 459 } 460 } 461 return columns; 462 } 463 464 /*** 465 * Retrieves a list of the columns composing the primary key for a given 466 * table. 467 * 468 * @param dbMeta JDBC metadata. 469 * @param tableName Table from which to retrieve PK information. 470 * @return A list of the primary key parts for <code>tableName</code>. 471 * @throws SQLException 472 */ 473 public List getPrimaryKeys(DatabaseMetaData dbMeta, String tableName) 474 throws SQLException 475 { 476 List pk = new ArrayList(); 477 ResultSet parts = null; 478 try 479 { 480 parts = dbMeta.getPrimaryKeys(null, dbSchema, tableName); 481 while (parts.next()) 482 { 483 pk.add(parts.getString(4)); 484 } 485 } 486 finally 487 { 488 if (parts != null) 489 { 490 parts.close(); 491 } 492 } 493 return pk; 494 } 495 496 /*** 497 * Retrieves a list of foreign key columns for a given table. 498 * 499 * @param dbMeta JDBC metadata. 500 * @param tableName Table from which to retrieve FK information. 501 * @return A list of foreign keys in <code>tableName</code>. 502 * @throws SQLException 503 */ 504 public Collection getForeignKeys(DatabaseMetaData dbMeta, String tableName) 505 throws SQLException 506 { 507 Hashtable fks = new Hashtable(); 508 ResultSet foreignKeys = null; 509 try 510 { 511 foreignKeys = dbMeta.getImportedKeys(null, dbSchema, tableName); 512 while (foreignKeys.next()) 513 { 514 String refTableName = foreignKeys.getString(3); 515 String fkName = foreignKeys.getString(12); 516 // if FK has no name - make it up (use tablename instead) 517 if (fkName == null) 518 { 519 fkName = refTableName; 520 } 521 Object[] fk = (Object[]) fks.get(fkName); 522 List refs; 523 if (fk == null) 524 { 525 fk = new Object[2]; 526 fk[0] = refTableName; //referenced table name 527 refs = new ArrayList(); 528 fk[1] = refs; 529 fks.put(fkName, fk); 530 } 531 else 532 { 533 refs = (ArrayList) fk[1]; 534 } 535 String[] ref = new String[2]; 536 ref[0] = foreignKeys.getString(8); //local column 537 ref[1] = foreignKeys.getString(4); //foreign column 538 refs.add(ref); 539 } 540 } 541 finally 542 { 543 if (foreignKeys != null) 544 { 545 foreignKeys.close(); 546 } 547 } 548 return fks.values(); 549 } 550 }

This page was automatically generated by Maven