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