001: package com.quadcap.sql.tools;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.BufferedWriter;
042: import java.io.FileInputStream;
043: import java.io.FileOutputStream;
044: import java.io.IOException;
045: import java.io.InputStream;
046: import java.io.OutputStream;
047: import java.io.OutputStreamWriter;
048: import java.io.Reader;
049: import java.io.Writer;
050:
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.HashMap;
054: import java.util.Map;
055:
056: import java.util.ArrayList;
057: import java.util.Hashtable;
058: import java.util.Properties;
059: import java.util.Vector;
060:
061: import java.sql.Blob;
062: import java.sql.Clob;
063: import java.sql.Connection;
064: import java.sql.DatabaseMetaData;
065: import java.sql.ResultSet;
066: import java.sql.ResultSetMetaData;
067: import java.sql.Statement;
068:
069: import java.sql.SQLException;
070: import java.sql.Types;
071:
072: import java.util.zip.GZIPOutputStream;
073:
074: import com.quadcap.sql.Backup;
075:
076: import com.quadcap.sql.types.Type;
077:
078: import com.quadcap.util.Config;
079: import com.quadcap.util.Debug;
080:
081: import com.quadcap.util.collections.DiGraph;
082:
083: /**
084: * Dump an SQL database in an XML representation. The XML document
085: * has an outer tag of <code><database></code>. Each child element
086: * then contains one table row, where the tag name specifies the table name;
087: * each child element of this tag contains one column value where the
088: * tag name specifies the column name. Most object are represented in
089: * their standard Java <code>toString</code> style representation, except
090: * binary objects, which are dumped in hexadecimal. (Base-64 would probably
091: * be better...)
092: *
093: * @author Stan Bailes
094: */
095:
096: public class XmlDump implements Backup {
097: Connection conn;
098: int indentLevel = 0;
099: String lineSep;
100: boolean wasBegin = false;
101: boolean wasEnd = false;
102:
103: /**
104: * No-argument constructor. The object so constructed needs a
105: * <code>Connection</code> in order to do anything useful.
106: */
107: public XmlDump() {
108: lineSep = System.getProperty("line.separator");
109: }
110:
111: /**
112: * Construct an XmlDump object bound to the specified connection.
113: *
114: * @param conn the database connection
115: */
116: public XmlDump(Connection conn) {
117: this .conn = conn;
118: lineSep = System.getProperty("line.separator");
119: }
120:
121: /**
122: * Construct an XmlDump object bound to the specified connection,
123: * and with a specified initial indent level.
124: *
125: * @param conn the database connection
126: * @param indentLevel the initial indent level (in units -- currently
127: * indent units are simple spaces
128: */
129: public XmlDump(Connection conn, int indentLevel) {
130: this .indentLevel = indentLevel;
131: this .conn = conn;
132: lineSep = System.getProperty("line.separator");
133: }
134:
135: /**
136: * Set the dumper's database connection
137: *
138: * @param conn the new database connection
139: */
140: public void setConnection(Connection conn) {
141: this .conn = conn;
142: }
143:
144: /**
145: * Get the dumper's database connection
146: *
147: * @return the current database connection
148: */
149: public Connection getConnection() {
150: return conn;
151: }
152:
153: final void beginTag(Writer w, String tag) throws IOException {
154: if (wasBegin)
155: w.write(lineSep);
156: for (int i = 0; i < indentLevel; i++)
157: w.write(' ');
158: indentLevel++;
159: w.write('<');
160: w.write(tag);
161: w.write('>');
162: wasBegin = true;
163: wasEnd = false;
164: }
165:
166: final void endTag(Writer w, String tag) throws IOException {
167: indentLevel--;
168: if (wasEnd) {
169: for (int i = 0; i < indentLevel; i++)
170: w.write(' ');
171: }
172: w.write('<');
173: w.write('/');
174: w.write(tag);
175: w.write('>');
176: w.write(lineSep);
177: wasBegin = false;
178: wasEnd = true;
179: }
180:
181: static final String charsLt = "<";
182: static final String charsGt = ">";
183: static final String charsAmp = "&";
184:
185: final void writeString(Writer w, String str) throws IOException {
186: for (int i = 0; i < str.length(); i++) {
187: char c = str.charAt(i);
188: switch (c) {
189: case '<':
190: w.write(charsLt);
191: break;
192: case '>':
193: w.write(charsGt);
194: break;
195: case '&':
196: w.write(charsAmp);
197: break;
198: default:
199: w.write(c);
200: }
201: }
202: wasBegin = false;
203: wasEnd = false;
204: }
205:
206: static final char[] hexBytes = { '0', '1', '2', '3', '4', '5', '6',
207: '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
208:
209: final void writeBinaryStream(Writer w, InputStream is)
210: throws IOException {
211: int c;
212: while ((c = is.read()) >= 0) {
213: w.write(hexBytes[(c >> 4) & 0xf]);
214: w.write(hexBytes[c & 0xf]);
215: }
216: }
217:
218: final void writeCharacterStream(Writer w, Reader r)
219: throws IOException {
220: int ci;
221: while ((ci = r.read()) >= 0) {
222: char c = (char) ci;
223: switch (c) {
224: case '<':
225: w.write(charsLt);
226: break;
227: case '>':
228: w.write(charsGt);
229: break;
230: case '&':
231: w.write(charsAmp);
232: break;
233: default:
234: w.write(c);
235: }
236: }
237: }
238:
239: final void writeBinary(Writer w, Object obj) throws IOException {
240: if (obj instanceof Boolean) {
241: w.write((((Boolean) obj).booleanValue()) ? '1' : '0');
242: } else {
243: byte[] buf = (byte[]) obj;
244: for (int i = 0; i < buf.length; i++) {
245: byte c = buf[i];
246: w.write(hexBytes[(c >> 4) & 0xf]);
247: w.write(hexBytes[c & 0xf]);
248: }
249: }
250: }
251:
252: final void writeValue(Writer w, Object obj, int jdbcType)
253: throws SQLException, IOException {
254: switch (jdbcType) {
255: case Types.BIT:
256: case Types.TINYINT:
257: case Types.SMALLINT:
258: case Types.INTEGER:
259: case Types.BIGINT:
260: case Types.FLOAT:
261: case Types.REAL:
262: case Types.DOUBLE:
263: case Types.NUMERIC:
264: case Types.DECIMAL:
265: case Types.CHAR:
266: case Types.VARCHAR:
267: case Types.LONGVARCHAR:
268: case Types.DATE:
269: case Types.TIME:
270: case Types.TIMESTAMP:
271: case Types.OTHER:
272: writeString(w, obj.toString());
273: break;
274: //- //#ifdef JDK11
275: //- case Type.CLOB:
276: //#else
277: case Types.CLOB:
278: writeCharacterStream(w, ((Clob) obj).getCharacterStream());
279: //#endif
280: break;
281: //- //#ifdef JDK11
282: //- case Type.BLOB:
283: //#else
284: case Types.BLOB:
285: //#endif
286: writeBinaryStream(w, ((Blob) obj).getBinaryStream());
287: break;
288: case Types.BINARY:
289: case Types.VARBINARY:
290: case Types.LONGVARBINARY:
291: writeBinary(w, obj);
292: break;
293: case Types.NULL:
294: //#ifndef JDK11
295: case Types.JAVA_OBJECT:
296: case Types.DISTINCT:
297: case Types.STRUCT:
298: case Types.ARRAY:
299: case Types.REF:
300: //#endif
301: throw new IOException("Not supported, jdbc type: "
302: + jdbcType + ", object = "
303: + obj.getClass().getName() + ": " + obj);
304: }
305: }
306:
307: List parseViewDef(String s) {
308: List ret = new ArrayList();
309: StringBuffer sb = new StringBuffer();
310: int idx = s.indexOf(" AS ");
311: if (idx >= 0) {
312: s = s.substring(idx + 4);
313: }
314: boolean l = false;
315: for (int i = 0; i < s.length(); i++) {
316: char c = s.charAt(i);
317: boolean d = Character.isLetterOrDigit(c) || c == '.';
318: if (d ^ l) {
319: if (sb.length() > 0) {
320: ret.add(sb.toString());
321: sb.setLength(0);
322: }
323: }
324: if (d) {
325: sb.append(c);
326: }
327: l = d;
328: }
329: if (sb.length() > 0)
330: ret.add(sb.toString());
331: return ret;
332: }
333:
334: void dumpViewDefinitions(Writer w) throws SQLException, IOException {
335: DatabaseMetaData dbMeta = (DatabaseMetaData) conn.getMetaData();
336: ResultSet rs = (ResultSet) dbMeta.getTables(null, "%", "%",
337: null);
338: Map map = new HashMap();
339: DiGraph graph = new DiGraph();
340: try {
341: while (rs.next()) {
342: String type = rs.getString(4);
343: if (type.toLowerCase().indexOf("view") >= 0) {
344: String schema = rs.getString(2);
345: String table = rs.getString(3);
346: if (schema != null && schema.length() > 0) {
347: table = schema + "." + table;
348: }
349: String view = rs.getString(5);
350: if (view != null) {
351: Iterator it = parseViewDef(view).iterator();
352: while (it.hasNext()) {
353: String base = it.next().toString();
354: graph.addArc(base, table);
355: //graph.addArc(table, base);
356: }
357: map.put(table, view);
358: }
359: }
360: }
361: Iterator iter = graph.levelize(true);
362: while (iter.hasNext()) {
363: String s = iter.next().toString();
364: Object x = map.get(s);
365: if (x != null) {
366: beginTag(w, "ddl");
367: writeString(w, "CREATE " + x.toString());
368: endTag(w, "ddl");
369: }
370: }
371: } finally {
372: rs.close();
373: }
374: }
375:
376: void dumpIndexDefinitions(Writer w, String tableName)
377: throws SQLException, IOException {
378: String schemaP = null;
379: String tableP = tableName;
380: int idx = tableName.indexOf('.');
381: if (idx > 0) {
382: schemaP = tableName.substring(0, idx);
383: tableP = tableName.substring(idx + 1);
384: }
385: DatabaseMetaData dbMeta = (DatabaseMetaData) conn.getMetaData();
386: ResultSet rs = (ResultSet) dbMeta.getIndexInfo(null, schemaP,
387: tableP, false, true);
388: try {
389: StringBuffer columns = new StringBuffer();
390: boolean unique = false;
391: String table = null;
392: String index = null;
393: while (rs.next()) {
394: if (rs.getObject(5) != null) {
395: String sch = rs.getString(2);
396: String tab = rs.getString(3);
397: if (sch != null && sch.length() > 0) {
398: tab = sch + "." + tab;
399: }
400: if (index != null && !index.equals(rs.getString(6))) {
401: beginTag(w, "ddl");
402: writeString(w, "CREATE "
403: + (unique ? "UNIQUE " : "") + " INDEX "
404: + index + " ON " + table + "("
405: + columns + ")");
406: endTag(w, "ddl");
407: columns.setLength(0);
408: }
409: String column = rs.getString(9);
410: unique = !rs.getBoolean(4);
411: index = rs.getString(6);
412: table = tab;
413: if (columns.length() > 0)
414: columns.append(", ");
415: columns.append(column);
416: }
417: }
418: if (index != null) {
419: beginTag(w, "ddl");
420: writeString(w, "CREATE " + (unique ? "UNIQUE " : "")
421: + " INDEX " + index + " ON " + table + "("
422: + columns + ")");
423: endTag(w, "ddl");
424: }
425: } finally {
426: rs.close();
427: }
428: }
429:
430: final Iterator orderTables() throws SQLException {
431: ArrayList t = new ArrayList();
432: DatabaseMetaData dbMeta = (DatabaseMetaData) conn.getMetaData();
433: ResultSet rs = (ResultSet) dbMeta.getTables(null, "%", "%",
434: null);
435: try {
436: while (rs.next()) {
437: String type = rs.getString(4);
438: if (type.toLowerCase().indexOf("table") >= 0) {
439: String schema = rs.getString(2);
440: String table = rs.getString(3);
441: if (schema != null && schema.length() > 0) {
442: table = schema + "." + table;
443: }
444: t.add(table);
445: }
446: }
447: } finally {
448: rs.close();
449: }
450: return t.iterator();
451: }
452:
453: class DbType {
454: int type;
455: String prefix;
456: String suffix;
457: String createParams;
458:
459: DbType(int type, String prefix, String suffix,
460: String createParams) {
461: this .type = type;
462: this .prefix = prefix == null ? "" : prefix;
463: this .suffix = suffix == null ? "" : suffix;
464: this .createParams = createParams == null ? ""
465: : createParams;
466: }
467:
468: String getTypePrefix() {
469: return prefix;
470: }
471:
472: String getTypeSuffix() {
473: return suffix;
474: }
475:
476: String getCreateParams(ResultSet rs) throws SQLException {
477: StringBuffer sb = new StringBuffer("");
478: String cp = createParams;
479: while (cp.length() > 0) {
480: String param = cp;
481: int idx = cp.indexOf(',');
482: if (idx > 0) {
483: param = cp.substring(0, idx).trim();
484: cp = cp.substring(idx + 1).trim();
485: } else {
486: cp = "";
487: }
488: if (param.equalsIgnoreCase("length")
489: || param.equalsIgnoreCase("precision")) {
490: if (sb.length() == 0) {
491: sb.append('(');
492: } else {
493: sb.append(',');
494: }
495: sb.append(rs.getString(7));
496: } else if (param.equalsIgnoreCase("scale")) {
497: if (sb.length() == 0) {
498: sb.append('(');
499: } else {
500: sb.append(',');
501: }
502: sb.append(rs.getString(9));
503: }
504: }
505: if (sb.length() > 0) {
506: sb.append(')');
507: }
508: return sb.toString();
509: }
510: };
511:
512: Hashtable types = null;
513:
514: Hashtable getTypes() throws SQLException {
515: if (types == null) {
516: types = new Hashtable();
517: DatabaseMetaData dbMeta = (DatabaseMetaData) conn
518: .getMetaData();
519: ResultSet rs = (ResultSet) dbMeta.getTypeInfo();
520: while (rs.next()) {
521: int type = rs.getInt(2);
522: String prefix = rs.getString(4);
523: String suffix = rs.getString(5);
524: String createParams = rs.getString(6);
525: DbType t = new DbType(type, prefix, suffix,
526: createParams);
527: types.put(new Integer(type), t);
528: }
529: }
530: return types;
531: }
532:
533: DbType getType(int type) throws SQLException {
534: DbType t = null;
535: getTypes();
536: t = (DbType) types.get(new Integer(type));
537: if (t == null) {
538: throw new RuntimeException("no type: " + type);
539: }
540: return t;
541: }
542:
543: /**
544: * Dump the SQL foreign key constraints for the specified table
545: */
546: public void dumpTableForeignKeys(Writer w, String tableName)
547: throws SQLException, IOException {
548: String schema = null;
549: String table = tableName;
550: int idx = tableName.indexOf('.');
551: if (idx > 0) {
552: schema = tableName.substring(0, idx);
553: table = tableName.substring(idx + 1);
554: }
555: DatabaseMetaData dbMeta = (DatabaseMetaData) conn.getMetaData();
556: ResultSet rs = (ResultSet) dbMeta.getImportedKeys(null, schema,
557: table);
558: String cname = "";
559: Vector pkeys = null;
560: String pkTable = null;
561: StringBuffer sb = new StringBuffer("");
562: while (rs.next()) {
563: String constraintName = rs.getString(12);
564: if (!constraintName.equals(cname)) {
565: if (cname.length() > 0) {
566: sb.append(") references ");
567: sb.append(pkTable);
568: sb.append("(");
569: for (int i = 0; i < pkeys.size(); i++) {
570: if (i > 0)
571: sb.append(", ");
572: sb.append(pkeys.elementAt(i).toString());
573: }
574: sb.append(")\n");
575: beginTag(w, "ddl");
576: writeString(w, sb.toString());
577: endTag(w, "ddl");
578: }
579: String pkSchema = rs.getString(2);
580: if (pkSchema != null && pkSchema.length() > 0) {
581: pkTable = pkSchema + "." + rs.getString(3);
582: } else {
583: pkTable = rs.getString(3);
584: }
585: cname = constraintName;
586: sb.setLength(0);
587: sb.append("\nalter table ");
588: sb.append(tableName);
589: sb.append(" add constraint ");
590: sb.append(cname);
591: sb.append(" foreign key(");
592: pkeys = new Vector();
593: } else {
594: sb.append(", ");
595: }
596: sb.append(rs.getString(8));
597: pkeys.addElement(rs.getString(4));
598: }
599: if (cname.length() > 0) {
600: sb.append(") references ");
601: sb.append(pkTable);
602: sb.append("(");
603: for (int i = 0; i < pkeys.size(); i++) {
604: if (i > 0)
605: sb.append(", ");
606: sb.append(pkeys.elementAt(i).toString());
607: }
608: sb.append(")");
609: beginTag(w, "ddl");
610: writeString(w, sb.toString());
611: endTag(w, "ddl");
612: }
613: }
614:
615: /**
616: * Dump the SQL table definition for the specified table
617: */
618: public void dumpTableDefinition(Writer w, String tableName)
619: throws SQLException, IOException {
620: Hashtable constraints = new Hashtable();
621: String schema = null;
622: String table = tableName;
623: int idx = tableName.indexOf('.');
624: if (idx > 0) {
625: schema = tableName.substring(0, idx);
626: table = tableName.substring(idx + 1);
627: }
628: DatabaseMetaData dbMeta = (DatabaseMetaData) conn.getMetaData();
629: ResultSet rs = (ResultSet) dbMeta.getColumns(null, schema,
630: table, "%");
631:
632: ResultSet rs2 = conn.createStatement().executeQuery(
633: "select * from " + tableName);
634: ResultSetMetaData rsMeta = rs2.getMetaData();
635:
636: StringBuffer sb = new StringBuffer("\ncreate table ");
637: sb.append(tableName);
638: sb.append('(');
639: boolean first = true;
640: while (rs.next()) {
641: sb.append("\n\t");
642: if (!first)
643: sb.append(", ");
644: first = false;
645: int type = rs.getInt(5);
646: DbType t = getType(type);
647: sb.append(rs.getString(4)); // column name
648: sb.append(' ');
649: sb.append(rs.getString(6)); // type name
650: sb.append(t.getCreateParams(rs));
651: if (rs.getInt(11) == ResultSetMetaData.columnNoNulls) {
652: sb.append(" NOT NULL");
653: }
654: String dflt = rs.getString(13);
655: if (dflt != null) {
656: sb.append(" DEFAULT ");
657: sb.append(t.getTypePrefix());
658: sb.append(dflt);
659: sb.append(t.getTypeSuffix());
660: }
661: if (rsMeta.isAutoIncrement(rs.getInt(17))) {
662: sb.append(" WITH IDENTITY");
663: }
664: }
665: rs.close();
666: rs2.close();
667:
668: rs = (ResultSet) dbMeta.getPrimaryKeys(null, schema, table);
669: first = true;
670: while (rs.next()) {
671: if (first) {
672: constraints.put(rs.getString(6), "");
673: sb.append("\n\t, constraint ");
674: sb.append(rs.getString(6)); // constraint name
675: sb.append(" primary key (");
676: first = false;
677: } else {
678: sb.append(", ");
679: }
680: sb.append(rs.getString(4)); // column name
681: }
682: if (!first)
683: sb.append(')');
684: rs.close();
685:
686: String iname = "";
687: rs = (ResultSet) dbMeta.getIndexInfo(null, schema, table, true,
688: false);
689: first = true;
690: while (rs.next()) {
691: String indexName = rs.getString(6);
692: if (constraints.get(indexName) != null)
693: continue;
694: if (rs.getObject(5) != null)
695: continue;
696: if (indexName.equals(iname)) {
697: sb.append(", ");
698: } else {
699: if (iname.length() > 0) {
700: sb.append(")");
701: }
702: sb.append("\n\t, constraint ");
703: sb.append(indexName);
704: sb.append(" unique(");
705: iname = indexName;
706: }
707: sb.append(rs.getString(9)); // column name
708: }
709: if (iname.length() > 0) {
710: sb.append(')');
711: }
712: rs.close();
713:
714: sb.append(")\n");
715: beginTag(w, "ddl");
716: writeString(w, sb.toString());
717: endTag(w, "ddl");
718: }
719:
720: /**
721: * Dump the rows of the specified table to the output stream in XML
722: * format. Each row is dumped as an XML element with the tag name
723: * equal to the table name. Each non-null column is dumped as a
724: * sub-element with the tag name equal to the column name.
725: *
726: * @param w the output stream
727: * @param tableName the name of the database table to dump.
728: *
729: * @exception IOException may be thrown
730: * @exception SQLException may be thrown
731: */
732: public void dumpTable(Writer w, String tableName)
733: throws IOException, SQLException {
734: tableName = tableName.toUpperCase();
735: Statement s = (Statement) conn.createStatement();
736: try {
737: ResultSet rs = (ResultSet) s.executeQuery("select * from "
738: + tableName);
739: try {
740: ResultSetMetaData rm = (ResultSetMetaData) rs
741: .getMetaData();
742: int cols = rm.getColumnCount();
743: while (rs.next()) {
744: beginTag(w, tableName);
745: for (int i = 1; i <= cols; i++) {
746: Object obj = rs.getObject(i);
747: if (!rs.wasNull()) {
748: beginTag(w, rm.getColumnName(i));
749: try {
750: writeValue(w, obj, rm.getColumnType(i));
751: } catch (IOException e) {
752: Debug.println("Table: " + tableName
753: + ", column = " + i + ": "
754: + rm.getColumnName(i));
755: throw e;
756: }
757: endTag(w, rm.getColumnName(i));
758: }
759: }
760: endTag(w, tableName);
761: }
762: } finally {
763: rs.close();
764: }
765: } finally {
766: s.close();
767: }
768: }
769:
770: /**
771: * Dump all of the tables in the database which is referenced by the
772: * current connection object to the specified output stream. This
773: * routine writes a well-formed XML document, with a document element
774: * named <code><database></code>. Sub-elements correspond to
775: * indidivual table rows.<p>
776: *
777: * This routine first constructs a graph of the foreign table reference
778: * constraints, and attempts to output base tables before dependant
779: * tables, so that the resulting file can be imported without causing
780: * foreign key integrity violations.<p>
781: *
782: * @param w the output stream
783: *
784: * @exception IOException may be thrown
785: * @exception SQLException may be thrown
786: */
787: public void dumpTables(Writer w) throws IOException, SQLException {
788: w.write("<?xml version=\"1.0\"?>\n");
789: beginTag(w, "database");
790: Iterator iter = orderTables();
791: while (iter.hasNext()) {
792: String table = iter.next().toString();
793: dumpTableDefinition(w, table);
794: }
795: dumpViewDefinitions(w);
796:
797: iter = orderTables();
798: beginTag(w, "dml");
799: while (iter.hasNext()) {
800: String table = iter.next().toString();
801: dumpTable(w, table);
802: }
803: endTag(w, "dml");
804:
805: iter = orderTables();
806: while (iter.hasNext()) {
807: String table = iter.next().toString();
808: dumpIndexDefinitions(w, table);
809: }
810:
811: iter = orderTables();
812: while (iter.hasNext()) {
813: String table = iter.next().toString();
814: dumpTableForeignKeys(w, table);
815: }
816: endTag(w, "database");
817: }
818:
819: /**
820: * A convenience function that sets the current connection and dumps
821: * the database in one go.
822: *
823: * @param conn the database connection
824: * @param w the output stream
825: *
826: * @exception IOException may be thrown
827: * @exception SQLException may be thrown
828: */
829: public void backup(java.sql.Connection conn, Writer w)
830: throws IOException, SQLException {
831: this .conn = (Connection) conn;
832: dumpTables(w);
833: }
834:
835: /**
836: * Establish a database connection based on the settings of system
837: * properties.
838: *
839: * <p>The following system properties are used to establish the
840: * database connection:
841: * <table border=1>
842: * <tr><th align=left>jdbc.driver</th>
843: * <td>The name of the JDBC driver class (default
844: * <code>com.quadcap.jdbc.JdbcDriver</code>)</td></tr>
845: * <tr><th align=left>jdbc.url</th>
846: * <td>The JDBC URL used to establish the database connection.
847: * </td></tr>
848: * <tr><th align=left>jdbc.props</th>
849: * <td>The name of a properties file used to establish the connection.
850: * If this property is specified, <code>jdbc.user</code> and
851: * <code>jdbc.password</code> aren't used.
852: * If this property has the value <code>"system"</code>, then
853: * the system properties are passed to the
854: * <code>DriverManager.getConnection()</code> method.
855: * </td></tr>
856: * <tr><th align=left>jdbc.user</th>
857: * <td>The user name used to establish the
858: * database connection. If neither <code>jdbc.props</code>
859: * nor <code>jdbc.user</code> is specified, the
860: * <code>DriverManager.getConnection(String url)</code>
861: * method is used to establish the connection.</td></tr>
862: * <tr><th align=left>jdbc.password</th>
863: * <td>The password used to establish the
864: * database connection.</td></tr>
865: * </table>
866: * </p>
867: *
868: * @exception Exception may be thrown if there's a problem connecting
869: * to the database.
870: */
871: public static Connection makeConnection() throws Exception {
872: Config.reset();
873: String driver = Config.getProperty("jdbc.driver",
874: "com.quadcap.jdbc.JdbcDriver");
875: String url = Config.getProperty("jdbc.url");
876: String user = Config.getProperty("jdbc.user");
877: String pass = Config.getProperty("jdbc.password");
878: String propsfile = Config.getProperty("jdbc.props");
879: Connection xconn = null;
880: Class.forName(driver);
881: if (propsfile != null) {
882: Properties props;
883: if (propsfile.equals("system")) {
884: props = System.getProperties();
885: } else {
886: props = new Properties();
887: FileInputStream pfile = new FileInputStream(propsfile);
888: props.load(pfile);
889: }
890: xconn = (Connection) java.sql.DriverManager.getConnection(
891: url, props);
892: } else if (user != null) {
893: xconn = (Connection) java.sql.DriverManager.getConnection(
894: url, user, pass);
895: } else {
896: xconn = (Connection) java.sql.DriverManager
897: .getConnection(url);
898: }
899: return xconn;
900: }
901:
902: /**
903: * A main program which allows this function to be run as a command-line
904: * application for doing an off-line dump. The name of the output file is
905: * specified as the argument. If the name of the output file
906: * ends with <code>".gz"</code>, the output is compressed using the
907: * <code>gzip</code> compression mechanism.
908: *
909: * @see makeConnection for a description of the System properties used
910: * to establish the database connection.
911: * @param args <code>com.quadcap.sql.tools.XmlDump</code>
912: * <i>output file</i>
913: */
914: public static void main(String[] args) {
915: try {
916: Connection xconn = makeConnection();
917: try {
918: XmlDump dump = new XmlDump(xconn);
919: String outfile = args[0];
920: if (outfile.endsWith(".xml")) {
921: FileOutputStream fos = new FileOutputStream(outfile);
922: OutputStreamWriter ow = new OutputStreamWriter(fos);
923: BufferedWriter bw = new BufferedWriter(ow);
924: dump.dumpTables(bw);
925: bw.close();
926: } else if (outfile.endsWith(".xml.gz")) {
927: FileOutputStream fos = new FileOutputStream(outfile);
928: GZIPOutputStream gos = new GZIPOutputStream(fos);
929: OutputStreamWriter ow = new OutputStreamWriter(gos);
930: BufferedWriter bw = new BufferedWriter(ow);
931: dump.dumpTables(bw);
932: bw.flush();
933: bw.close();
934: } else {
935: throw new Exception(
936: "Unrecognized output type for file: "
937: + outfile);
938: }
939: } finally {
940: xconn.close();
941: }
942: } catch (Exception e) {
943: com.quadcap.util.Debug.print(e);
944: }
945: }
946: }
|