0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.openjpa.jdbc.schema;
0020:
0021: import java.io.File;
0022: import java.io.IOException;
0023: import java.io.PrintWriter;
0024: import java.io.Writer;
0025: import java.sql.Connection;
0026: import java.sql.DatabaseMetaData;
0027: import java.sql.SQLException;
0028: import java.sql.Statement;
0029: import java.util.Arrays;
0030: import java.util.Collection;
0031: import java.util.HashSet;
0032: import java.util.Iterator;
0033: import java.util.LinkedHashSet;
0034: import java.util.LinkedList;
0035: import java.util.Set;
0036: import javax.sql.DataSource;
0037:
0038: import org.apache.commons.lang.StringUtils;
0039: import org.apache.openjpa.conf.OpenJPAConfiguration;
0040: import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
0041: import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
0042: import org.apache.openjpa.jdbc.sql.DBDictionary;
0043: import org.apache.openjpa.jdbc.sql.SQLExceptions;
0044: import org.apache.openjpa.lib.conf.Configurations;
0045: import org.apache.openjpa.lib.log.Log;
0046: import org.apache.openjpa.lib.util.Files;
0047: import org.apache.openjpa.lib.util.Localizer;
0048: import org.apache.openjpa.lib.util.Options;
0049: import org.apache.openjpa.util.InvalidStateException;
0050:
0051: /**
0052: * The SchemaTool is used to manage the database schema. Note that the
0053: * tool never adds or drops unique constraints from existing tables, because
0054: * JDBC {@link DatabaseMetaData} does not include information on these
0055: * constraints.
0056: *
0057: * @author Abe White
0058: * @author Patrick Linskey
0059: */
0060: public class SchemaTool {
0061:
0062: public static final String ACTION_ADD = "add";
0063: public static final String ACTION_DROP = "drop";
0064: public static final String ACTION_RETAIN = "retain";
0065: public static final String ACTION_REFRESH = "refresh";
0066: public static final String ACTION_BUILD = "build";
0067: public static final String ACTION_REFLECT = "reflect";
0068: public static final String ACTION_CREATEDB = "createDB";
0069: public static final String ACTION_DROPDB = "dropDB";
0070: public static final String ACTION_IMPORT = "import";
0071: public static final String ACTION_EXPORT = "export";
0072: public static final String ACTION_DELETE_TABLE_CONTENTS = "deleteTableContents";
0073:
0074: public static final String[] ACTIONS = new String[] { ACTION_ADD,
0075: ACTION_DROP, ACTION_RETAIN, ACTION_REFRESH, ACTION_BUILD,
0076: ACTION_REFLECT, ACTION_CREATEDB, ACTION_DROPDB,
0077: ACTION_IMPORT, ACTION_EXPORT, ACTION_DELETE_TABLE_CONTENTS, };
0078:
0079: private static final Localizer _loc = Localizer
0080: .forPackage(SchemaTool.class);
0081:
0082: private final JDBCConfiguration _conf;
0083: private final DataSource _ds;
0084: private final Log _log;
0085: private final DBDictionary _dict;
0086: private final String _action;
0087: private boolean _ignoreErrs = false;
0088: private boolean _openjpaTables = false;
0089: private boolean _dropTables = true;
0090: private boolean _dropSeqs = true;
0091: private boolean _pks = true;
0092: private boolean _fks = true;
0093: private boolean _indexes = true;
0094: private boolean _seqs = true;
0095: private PrintWriter _writer = null;
0096: private SchemaGroup _group = null;
0097: private SchemaGroup _db = null;
0098: private boolean _fullDB = false;
0099:
0100: /**
0101: * Default constructor. Tools constructed this way will not have an
0102: * action, so the {@link #run()} method will be a no-op.
0103: */
0104: public SchemaTool(JDBCConfiguration conf) {
0105: this (conf, null);
0106: }
0107:
0108: /**
0109: * Construct a tool to perform the given action.
0110: */
0111: public SchemaTool(JDBCConfiguration conf, String action) {
0112: if (action != null && !Arrays.asList(ACTIONS).contains(action))
0113: throw new IllegalArgumentException("action == " + action);
0114:
0115: _conf = conf;
0116: _action = action;
0117: _ds = conf.getDataSource2(null);
0118: _log = conf.getLog(JDBCConfiguration.LOG_SCHEMA);
0119:
0120: // initialize this up-front; otherwise the dbdictionaryfactory might
0121: // try to take a connection to initialize when we've already got one:
0122: // bad news if the max pool is 1
0123: _dict = _conf.getDBDictionaryInstance();
0124: }
0125:
0126: /**
0127: * The action supplied on construction.
0128: */
0129: public String getAction() {
0130: return _action;
0131: }
0132:
0133: /**
0134: * If true, SQLExceptions thrown during schema manipulation will be
0135: * printed but ignored.
0136: */
0137: public boolean getIgnoreErrors() {
0138: return _ignoreErrs;
0139: }
0140:
0141: /**
0142: * If true, SQLExceptions thrown during schema manipulation will be
0143: * printed but ignored.
0144: */
0145: public void setIgnoreErrors(boolean ignoreErrs) {
0146: _ignoreErrs = ignoreErrs;
0147: }
0148:
0149: /**
0150: * Whether to act on special tables used by OpenJPA components
0151: * for bookkeeping.
0152: */
0153: public boolean getOpenJPATables() {
0154: return _openjpaTables;
0155: }
0156:
0157: /**
0158: * Whether to act on special tables used by OpenJPA components
0159: * for bookkeeping.
0160: */
0161: public void setOpenJPATables(boolean openjpaTables) {
0162: _openjpaTables = openjpaTables;
0163: }
0164:
0165: /**
0166: * If true, tables that appear to be unused will be dropped. Defaults to
0167: * true.
0168: */
0169: public boolean getDropTables() {
0170: return _dropTables;
0171: }
0172:
0173: /**
0174: * If true, tables that appear to be unused will be dropped. Defaults to
0175: * true.
0176: */
0177: public void setDropTables(boolean dropTables) {
0178: _dropTables = dropTables;
0179: }
0180:
0181: /**
0182: * If true, sequences that appear to be unused will be dropped. Defaults
0183: * to true.
0184: */
0185: public boolean getDropSequences() {
0186: return _dropSeqs;
0187: }
0188:
0189: /**
0190: * If true, sequences that appear to be unused will be dropped. Defaults
0191: * to true.
0192: */
0193: public void setDropSequences(boolean dropSeqs) {
0194: _dropSeqs = dropSeqs;
0195: if (dropSeqs)
0196: setSequences(true);
0197: }
0198:
0199: /**
0200: * Whether sequences should be manipulated. Defaults to true.
0201: */
0202: public boolean getSequences() {
0203: return _seqs;
0204: }
0205:
0206: /**
0207: * Whether sequences should be manipulated. Defaults to true.
0208: */
0209: public void setSequences(boolean seqs) {
0210: _seqs = seqs;
0211: }
0212:
0213: /**
0214: * Whether indexes on existing tables should be manipulated.
0215: * Defaults to true.
0216: */
0217: public boolean getIndexes() {
0218: return _indexes;
0219: }
0220:
0221: /**
0222: * Whether indexes on existing tables should be manipulated.
0223: * Defaults to true.
0224: */
0225: public void setIndexes(boolean indexes) {
0226: _indexes = indexes;
0227: }
0228:
0229: /**
0230: * Whether foreign keys on existing tables should be manipulated.
0231: * Defaults to true.
0232: */
0233: public boolean getForeignKeys() {
0234: return _fks;
0235: }
0236:
0237: /**
0238: * Whether foreign keys on existing tables should be manipulated.
0239: * Defaults to true.
0240: */
0241: public void setForeignKeys(boolean fks) {
0242: _fks = fks;
0243: }
0244:
0245: /**
0246: * Whether primary keys on existing tables should be manipulated.
0247: * Defaults to true.
0248: */
0249: public boolean getPrimaryKeys() {
0250: return _pks;
0251: }
0252:
0253: /**
0254: * Whether primary keys on existing tables should be manipulated.
0255: * Defaults to true.
0256: */
0257: public void setPrimaryKeys(boolean pks) {
0258: _pks = pks;
0259: }
0260:
0261: /**
0262: * The stream to write to for the creation of SQL scripts. If the
0263: * stream is non-null, all SQL will be written to this stream rather than
0264: * executed against the database.
0265: */
0266: public Writer getWriter() {
0267: return _writer;
0268: }
0269:
0270: /**
0271: * The stream to write to for the creation of SQL scripts. If the
0272: * stream is non-null, all SQL will be written to this stream rather than
0273: * executed against the database.
0274: */
0275: public void setWriter(Writer writer) {
0276: if (writer == null)
0277: _writer = null;
0278: else if (writer instanceof PrintWriter)
0279: _writer = (PrintWriter) writer;
0280: else
0281: _writer = new PrintWriter(writer);
0282: }
0283:
0284: /**
0285: * Return the schema group the tool will act on.
0286: */
0287: public SchemaGroup getSchemaGroup() {
0288: return _group;
0289: }
0290:
0291: /**
0292: * Set the schema group the tool will act on.
0293: */
0294: public void setSchemaGroup(SchemaGroup group) {
0295: _group = group;
0296: }
0297:
0298: ///////////
0299: // Actions
0300: ///////////
0301:
0302: /**
0303: * Run the tool action.
0304: */
0305: public void run() throws SQLException {
0306: if (_action == null)
0307: return;
0308:
0309: if (ACTION_ADD.equals(_action))
0310: add();
0311: else if (ACTION_DROP.equals(_action))
0312: drop();
0313: else if (ACTION_RETAIN.equals(_action))
0314: retain();
0315: else if (ACTION_REFRESH.equals(_action))
0316: refresh();
0317: else if (ACTION_BUILD.equals(_action))
0318: build();
0319: else if (ACTION_CREATEDB.equals(_action))
0320: createDB();
0321: else if (ACTION_DROPDB.equals(_action))
0322: dropDB();
0323: else if (ACTION_DELETE_TABLE_CONTENTS.equals(_action))
0324: deleteTableContents();
0325: }
0326:
0327: /**
0328: * Adds any components present in the schema repository but absent from
0329: * the database. Package-private for testing.
0330: */
0331: void add() throws SQLException {
0332: add(getDBSchemaGroup(false), assertSchemaGroup());
0333: }
0334:
0335: /**
0336: * Drops all schema components in the schema repository that also exist
0337: * in the database. Package-private for testing.
0338: */
0339: void drop() throws SQLException {
0340: drop(getDBSchemaGroup(false), assertSchemaGroup());
0341: }
0342:
0343: /**
0344: * Drops database components that are not mentioned in the schema
0345: * repository. Package-private for testing.
0346: */
0347: void retain() throws SQLException {
0348: retain(getDBSchemaGroup(true), assertSchemaGroup(),
0349: getDropTables(), getDropSequences());
0350: }
0351:
0352: /**
0353: * Adds any components present in the schema repository but absent from
0354: * the database, and drops unused database components.
0355: * Package-private for testing.
0356: */
0357: void refresh() throws SQLException {
0358: SchemaGroup local = assertSchemaGroup();
0359: SchemaGroup db = getDBSchemaGroup(true);
0360: retain(db, local, getDropTables(), getDropSequences());
0361: add(db, local);
0362: }
0363:
0364: /**
0365: * Re-execute all SQL used for the creation of the current database;
0366: * this action is usually used when creating SQL scripts.
0367: * Package-private for testing.
0368: */
0369: void createDB() throws SQLException {
0370: SchemaGroup group = new SchemaGroup();
0371: group.addSchema();
0372: add(group, getDBSchemaGroup(true));
0373: }
0374:
0375: /**
0376: * Re-execute all SQL used for the creation of the current database;
0377: * this action is usually used when creating SQL scripts.
0378: * Package-private for testing.
0379: */
0380: void build() throws SQLException {
0381: SchemaGroup group = new SchemaGroup();
0382: group.addSchema();
0383: add(group, assertSchemaGroup());
0384: }
0385:
0386: /**
0387: * Drop the current database. Package-private for testing.
0388: */
0389: void dropDB() throws SQLException {
0390: retain(getDBSchemaGroup(true), new SchemaGroup(), true, true);
0391: }
0392:
0393: /**
0394: * Issue DELETE statement against all known tables.
0395: */
0396: private void deleteTableContents() throws SQLException {
0397: SchemaGroup group = getSchemaGroup();
0398: Schema[] schemas = group.getSchemas();
0399: Collection tables = new LinkedHashSet();
0400: for (int i = 0; i < schemas.length; i++) {
0401: Table[] ts = schemas[i].getTables();
0402: for (int j = 0; j < ts.length; j++)
0403: tables.add(ts[j]);
0404: }
0405: Table[] tableArray = (Table[]) tables.toArray(new Table[tables
0406: .size()]);
0407: String[] sql = _conf.getDBDictionaryInstance()
0408: .getDeleteTableContentsSQL(tableArray);
0409: if (!executeSQL(sql))
0410: _log.warn(_loc.get("delete-table-contents"));
0411: }
0412:
0413: /**
0414: * Record the changes made to the DB in the current {@link SchemaFactory}.
0415: */
0416: public void record() {
0417: if (_db != null && _writer == null)
0418: _conf.getSchemaFactoryInstance().storeSchema(_db);
0419: }
0420:
0421: /**
0422: * Adds all database components in the repository schema that are not
0423: * present in the given database schema to the database.
0424: */
0425: private void add(SchemaGroup db, SchemaGroup repos)
0426: throws SQLException {
0427: // add sequences
0428: Schema[] schemas = repos.getSchemas();
0429: Schema schema;
0430: if (_seqs) {
0431: Sequence[] seqs;
0432: for (int i = 0; i < schemas.length; i++) {
0433: seqs = schemas[i].getSequences();
0434: for (int j = 0; j < seqs.length; j++) {
0435: if (db.findSequence(schemas[i], seqs[j]
0436: .getFullName()) != null)
0437: continue;
0438:
0439: if (createSequence(seqs[j])) {
0440: schema = db.getSchema(seqs[j].getSchemaName());
0441: if (schema == null)
0442: schema = db.addSchema(seqs[j]
0443: .getSchemaName());
0444: schema.importSequence(seqs[j]);
0445: } else
0446: _log.warn(_loc.get("add-seq", seqs[j]));
0447: }
0448: }
0449: }
0450:
0451: // order is important in this method; start with columns
0452: Table[] tabs;
0453: Table dbTable;
0454: Column[] cols;
0455: Column col;
0456: for (int i = 0; i < schemas.length; i++) {
0457: tabs = schemas[i].getTables();
0458: for (int j = 0; j < tabs.length; j++) {
0459: cols = tabs[j].getColumns();
0460: dbTable = db.findTable(schemas[i], tabs[j]
0461: .getFullName());
0462: for (int k = 0; k < cols.length; k++) {
0463: if (dbTable != null) {
0464: col = dbTable.getColumn(cols[k].getName());
0465: if (col == null) {
0466: if (addColumn(cols[k]))
0467: dbTable.importColumn(cols[k]);
0468: else
0469: _log.warn(_loc.get("add-col", cols[k],
0470: tabs[j]));
0471: } else if (!cols[k].equalsColumn(col)) {
0472: _log.warn(_loc.get("bad-col", new Object[] {
0473: col, dbTable, col.getDescription(),
0474: cols[k].getDescription() }));
0475: }
0476: }
0477: }
0478: }
0479: }
0480:
0481: // primary keys
0482: if (_pks) {
0483: PrimaryKey pk;
0484: for (int i = 0; i < schemas.length; i++) {
0485: tabs = schemas[i].getTables();
0486: for (int j = 0; j < tabs.length; j++) {
0487: pk = tabs[j].getPrimaryKey();
0488: dbTable = db.findTable(schemas[i], tabs[j]
0489: .getFullName());
0490: if (pk != null && !pk.isLogical()
0491: && dbTable != null) {
0492: if (dbTable.getPrimaryKey() == null
0493: && addPrimaryKey(pk))
0494: dbTable.importPrimaryKey(pk);
0495: else if (dbTable.getPrimaryKey() == null)
0496: _log.warn(_loc.get("add-pk", pk, tabs[j]));
0497: else if (!pk.equalsPrimaryKey(dbTable
0498: .getPrimaryKey()))
0499: _log.warn(_loc.get("bad-pk", dbTable
0500: .getPrimaryKey(), dbTable));
0501: }
0502: }
0503: }
0504: }
0505:
0506: // tables
0507: Set newTables = new HashSet();
0508: for (int i = 0; i < schemas.length; i++) {
0509: tabs = schemas[i].getTables();
0510: for (int j = 0; j < tabs.length; j++) {
0511: if (db.findTable(schemas[i], tabs[j].getFullName()) != null)
0512: continue;
0513:
0514: if (createTable(tabs[j])) {
0515: newTables.add(tabs[j]);
0516: schema = db.getSchema(tabs[j].getSchemaName());
0517: if (schema == null)
0518: schema = db.addSchema(tabs[j].getSchemaName());
0519: schema.importTable(tabs[j]);
0520: } else
0521: _log.warn(_loc.get("add-table", tabs[j]));
0522: }
0523: }
0524:
0525: // indexes
0526: Index[] idxs;
0527: Index idx;
0528: for (int i = 0; i < schemas.length; i++) {
0529: tabs = schemas[i].getTables();
0530: for (int j = 0; j < tabs.length; j++) {
0531: // create indexes on new tables even if indexes
0532: // have been turned off
0533: if (!_indexes && !newTables.contains(tabs[j]))
0534: continue;
0535:
0536: idxs = tabs[j].getIndexes();
0537: dbTable = db.findTable(schemas[i], tabs[j]
0538: .getFullName());
0539: for (int k = 0; k < idxs.length; k++) {
0540: if (dbTable != null) {
0541: idx = findIndex(dbTable, idxs[k]);
0542: if (idx == null) {
0543: if (createIndex(idxs[k], dbTable))
0544: dbTable.importIndex(idxs[k]);
0545: else
0546: _log.warn(_loc.get("add-index",
0547: idxs[k], tabs[j]));
0548: } else if (!idxs[k].equalsIndex(idx))
0549: _log.warn(_loc.get("bad-index", idx,
0550: dbTable));
0551: }
0552: }
0553: }
0554: }
0555:
0556: // Unique Constraints on group of columns
0557: Unique[] uniques;
0558: for (int i = 0; i < schemas.length; i++) {
0559: tabs = schemas[i].getTables();
0560: for (int j = 0; j < tabs.length; j++) {
0561: // create unique constraints only on new tables
0562: if (!newTables.contains(tabs[j]))
0563: continue;
0564:
0565: uniques = tabs[j].getUniques();
0566: if (uniques == null || uniques.length == 0)
0567: continue;
0568: dbTable = db.findTable(tabs[j]);
0569: if (dbTable == null)
0570: continue;
0571: for (int k = 0; k < uniques.length; k++) {
0572: dbTable.importUnique(uniques[k]);
0573: }
0574: }
0575: }
0576:
0577: // foreign keys
0578: ForeignKey[] fks;
0579: ForeignKey fk;
0580: for (int i = 0; i < schemas.length; i++) {
0581: tabs = schemas[i].getTables();
0582: for (int j = 0; j < tabs.length; j++) {
0583: // create foreign keys on new tables even if fks
0584: // have been turned off
0585: if (!_fks && !newTables.contains(tabs[j]))
0586: continue;
0587:
0588: fks = tabs[j].getForeignKeys();
0589: dbTable = db.findTable(schemas[i], tabs[j]
0590: .getFullName());
0591: for (int k = 0; k < fks.length; k++) {
0592: if (!fks[k].isLogical() && dbTable != null) {
0593: fk = findForeignKey(dbTable, fks[k]);
0594: if (fk == null) {
0595: if (addForeignKey(fks[k]))
0596: dbTable.importForeignKey(fks[k]);
0597: else
0598: _log.warn(_loc.get("add-fk", fks[k],
0599: tabs[j]));
0600: } else if (!fks[k].equalsForeignKey(fk))
0601: _log.warn(_loc.get("bad-fk", fk, dbTable));
0602: }
0603: }
0604: }
0605: }
0606: }
0607:
0608: /**
0609: * Drops all database components that are in the given database schema
0610: * but not in the repository schema.
0611: */
0612: private void retain(SchemaGroup db, SchemaGroup repos,
0613: boolean tables, boolean sequences) throws SQLException {
0614: Schema[] schemas = db.getSchemas();
0615: if (_seqs && sequences) {
0616: Sequence[] seqs;
0617: for (int i = 0; i < schemas.length; i++) {
0618: seqs = schemas[i].getSequences();
0619: for (int j = 0; j < seqs.length; j++) {
0620: if (!isDroppable(seqs[j]))
0621: continue;
0622: if (repos.findSequence(seqs[j]) == null) {
0623: if (dropSequence(seqs[j]))
0624: schemas[i].removeSequence(seqs[j]);
0625: else
0626: _log.warn(_loc.get("drop-seq", seqs[j]));
0627: }
0628: }
0629: }
0630: }
0631:
0632: // order is important in this method; start with foreign keys
0633: Table[] tabs;
0634: Table reposTable;
0635: if (_fks) {
0636: ForeignKey[] fks;
0637: ForeignKey fk;
0638: for (int i = 0; i < schemas.length; i++) {
0639: tabs = schemas[i].getTables();
0640: for (int j = 0; j < tabs.length; j++) {
0641: if (!isDroppable(tabs[j]))
0642: continue;
0643: fks = tabs[j].getForeignKeys();
0644: reposTable = repos.findTable(tabs[j]);
0645: if (!tables && reposTable == null)
0646: continue;
0647:
0648: for (int k = 0; k < fks.length; k++) {
0649: if (fks[k].isLogical())
0650: continue;
0651:
0652: fk = null;
0653: if (reposTable != null)
0654: fk = findForeignKey(reposTable, fks[k]);
0655: if (reposTable == null || fk == null
0656: || !fks[k].equalsForeignKey(fk)) {
0657: if (dropForeignKey(fks[k]))
0658: tabs[j].removeForeignKey(fks[k]);
0659: else
0660: _log.warn(_loc.get("drop-fk", fks[k],
0661: tabs[j]));
0662: }
0663: }
0664: }
0665: }
0666: }
0667:
0668: // primary keys
0669: if (_pks) {
0670: PrimaryKey pk;
0671: for (int i = 0; i < schemas.length; i++) {
0672: tabs = schemas[i].getTables();
0673: for (int j = 0; j < tabs.length; j++) {
0674: if (!isDroppable(tabs[j]))
0675: continue;
0676: pk = tabs[j].getPrimaryKey();
0677: if (pk != null && pk.isLogical())
0678: continue;
0679:
0680: reposTable = repos.findTable(tabs[j]);
0681: if (pk != null
0682: && reposTable != null
0683: && (reposTable.getPrimaryKey() == null || !pk
0684: .equalsPrimaryKey(reposTable
0685: .getPrimaryKey()))) {
0686: if (dropPrimaryKey(pk))
0687: tabs[j].removePrimaryKey();
0688: else
0689: _log.warn(_loc.get("drop-pk", pk, tabs[j]));
0690: }
0691: }
0692: }
0693: }
0694:
0695: // columns
0696: Column[] cols;
0697: Column col;
0698: Collection drops = new LinkedList();
0699: for (int i = 0; i < schemas.length; i++) {
0700: tabs = schemas[i].getTables();
0701: for (int j = 0; j < tabs.length; j++) {
0702: if (!isDroppable(tabs[j]))
0703: continue;
0704: cols = tabs[j].getColumns();
0705: reposTable = repos.findTable(tabs[j]);
0706: if (reposTable != null) {
0707: for (int k = 0; k < cols.length; k++) {
0708: col = reposTable.getColumn(cols[k].getName());
0709: if (col == null || !cols[k].equalsColumn(col)) {
0710: if (tabs[j].getColumns().length == 1)
0711: drops.add(tabs[j]);
0712: else if (dropColumn(cols[k]))
0713: tabs[j].removeColumn(cols[k]);
0714: else
0715: _log.warn(_loc.get("drop-col", cols[k],
0716: tabs[j]));
0717: }
0718: }
0719: }
0720: }
0721: }
0722:
0723: // now tables
0724: if (tables) {
0725: for (int i = 0; i < schemas.length; i++) {
0726: tabs = schemas[i].getTables();
0727: for (int j = 0; j < tabs.length; j++)
0728: if (!!isDroppable(tabs[j])
0729: && repos.findTable(tabs[j]) == null)
0730: drops.add(tabs[j]);
0731: }
0732: }
0733: dropTables(drops, db);
0734: }
0735:
0736: /**
0737: * Drops all database components in the given repository schema.
0738: */
0739: private void drop(SchemaGroup db, SchemaGroup repos)
0740: throws SQLException {
0741: // drop sequences
0742: Schema[] schemas = repos.getSchemas();
0743: if (_seqs) {
0744: Sequence[] seqs;
0745: Sequence dbSeq;
0746: for (int i = 0; i < schemas.length; i++) {
0747: seqs = schemas[i].getSequences();
0748: for (int j = 0; j < seqs.length; j++) {
0749: if (!isDroppable(seqs[j]))
0750: continue;
0751: dbSeq = db.findSequence(seqs[j]);
0752: if (dbSeq != null) {
0753: if (dropSequence(seqs[j]))
0754: dbSeq.getSchema().removeSequence(dbSeq);
0755: else
0756: _log.warn(_loc.get("drop-seq", seqs[j]));
0757: }
0758: }
0759: }
0760: }
0761:
0762: // calculate tables to drop; we can only drop tables if we're sure
0763: // the user listed the entire table definition in the stuff they want
0764: // dropped; else they might just want to drop a few columns
0765: Collection drops = new LinkedList();
0766: Table[] tabs;
0767: Table dbTable;
0768: Column[] dbCols;
0769: for (int i = 0; i < schemas.length; i++) {
0770: tabs = schemas[i].getTables();
0771: tables: for (int j = 0; j < tabs.length; j++) {
0772: if (!isDroppable(tabs[j]))
0773: continue;
0774: dbTable = db.findTable(tabs[j]);
0775: if (dbTable == null)
0776: continue;
0777:
0778: dbCols = dbTable.getColumns();
0779: for (int k = 0; k < dbCols.length; k++)
0780: if (tabs[j].getColumn(dbCols[k].getName()) == null)
0781: continue tables;
0782:
0783: drops.add(tabs[j]);
0784: }
0785: }
0786:
0787: // order is important in this method; start with foreign keys mentioned
0788: // in the drop schema
0789: if (_fks) {
0790: ForeignKey[] fks;
0791: ForeignKey fk;
0792: for (int i = 0; i < schemas.length; i++) {
0793: tabs = schemas[i].getTables();
0794: for (int j = 0; j < tabs.length; j++) {
0795: if (!isDroppable(tabs[j]))
0796: continue;
0797: fks = tabs[j].getForeignKeys();
0798: dbTable = db.findTable(tabs[j]);
0799: for (int k = 0; k < fks.length; k++) {
0800: if (fks[k].isLogical())
0801: continue;
0802:
0803: fk = null;
0804: if (dbTable != null)
0805: fk = findForeignKey(dbTable, fks[k]);
0806: if (dbTable == null || fk == null)
0807: continue;
0808:
0809: if (dropForeignKey(fks[k]))
0810: if (dbTable != null)
0811: dbTable.removeForeignKey(fk);
0812: else
0813: _log.warn(_loc.get("drop-fk", fks[k],
0814: tabs[j]));
0815: }
0816: }
0817: }
0818:
0819: // also drop imported foreign keys for tables that will be dropped
0820: Table tab;
0821: for (Iterator itr = drops.iterator(); itr.hasNext();) {
0822: tab = (Table) itr.next();
0823: dbTable = db.findTable(tab);
0824: if (dbTable == null)
0825: continue;
0826:
0827: fks = db.findExportedForeignKeys(dbTable
0828: .getPrimaryKey());
0829: for (int i = 0; i < fks.length; i++) {
0830: if (dropForeignKey(fks[i]))
0831: dbTable.removeForeignKey(fks[i]);
0832: else
0833: _log.warn(_loc.get("drop-fk", fks[i], dbTable));
0834: }
0835: }
0836: }
0837:
0838: // drop the tables we calculated above
0839: dropTables(drops, db);
0840:
0841: // columns
0842: Column[] cols;
0843: Column col;
0844: for (int i = 0; i < schemas.length; i++) {
0845: tabs = schemas[i].getTables();
0846: for (int j = 0; j < tabs.length; j++) {
0847: if (!isDroppable(tabs[j]))
0848: continue;
0849: cols = tabs[j].getColumns();
0850: dbTable = db.findTable(tabs[j]);
0851: for (int k = 0; k < cols.length; k++) {
0852: col = null;
0853: if (dbTable != null)
0854: col = dbTable.getColumn(cols[k].getName());
0855: if (dbTable == null || col == null)
0856: continue;
0857:
0858: if (dropColumn(cols[k])) {
0859: if (dbTable != null)
0860: dbTable.removeColumn(col);
0861: else
0862: _log.warn(_loc.get("drop-col", cols[k],
0863: tabs[j]));
0864: }
0865: }
0866: }
0867: }
0868: }
0869:
0870: /**
0871: * Return true if the table is droppable.
0872: */
0873: private boolean isDroppable(Table table) {
0874: return _openjpaTables
0875: || (!table.getName().toUpperCase().startsWith(
0876: "OPENJPA_") && !table.getName().toUpperCase()
0877: .startsWith("JDO_")); // legacy
0878: }
0879:
0880: /**
0881: * Return true if the sequence is droppable.
0882: */
0883: private boolean isDroppable(Sequence seq) {
0884: return _openjpaTables
0885: || (!seq.getName().toUpperCase().startsWith("OPENJPA_") && !seq
0886: .getName().toUpperCase().startsWith("JDO_")); // legacy
0887: }
0888:
0889: /**
0890: * Find an index in the given table that matches the given one.
0891: */
0892: private Index findIndex(Table dbTable, Index idx) {
0893: Index[] idxs = dbTable.getIndexes();
0894: for (int i = 0; i < idxs.length; i++)
0895: if (idx.columnsMatch(idxs[i].getColumns()))
0896: return idxs[i];
0897: return null;
0898: }
0899:
0900: /**
0901: * Find a foreign key in the given table that matches the given one.
0902: */
0903: private ForeignKey findForeignKey(Table dbTable, ForeignKey fk) {
0904: if (fk.getConstantColumns().length > 0
0905: || fk.getConstantPrimaryKeyColumns().length > 0)
0906: return null;
0907: ForeignKey[] fks = dbTable.getForeignKeys();
0908: for (int i = 0; i < fks.length; i++)
0909: if (fk.columnsMatch(fks[i].getColumns(), fks[i]
0910: .getPrimaryKeyColumns()))
0911: return fks[i];
0912: return null;
0913: }
0914:
0915: /**
0916: * Remove the given collection of tables from the database schema. Orders
0917: * the removals according to foreign key constraints on the tables.
0918: */
0919: private void dropTables(Collection tables, SchemaGroup change)
0920: throws SQLException {
0921: if (tables.isEmpty())
0922: return;
0923:
0924: Table table;
0925: Table changeTable;
0926: for (Iterator itr = tables.iterator(); itr.hasNext();) {
0927: table = (Table) itr.next();
0928: if (dropTable(table)) {
0929: changeTable = change.findTable(table);
0930: if (changeTable != null)
0931: changeTable.getSchema().removeTable(changeTable);
0932: } else
0933: _log.warn(_loc.get("drop-table", table));
0934: }
0935: }
0936:
0937: /**
0938: * Add the given table to the database schema.
0939: *
0940: * @return true if the operation was successful, false otherwise
0941: */
0942: public boolean createTable(Table table) throws SQLException {
0943: return executeSQL(_dict.getCreateTableSQL(table));
0944: }
0945:
0946: /**
0947: * Drop the given table from the database schema.
0948: *
0949: * @return true if the operation was successful, false otherwise
0950: */
0951: public boolean dropTable(Table table) throws SQLException {
0952: return executeSQL(_dict.getDropTableSQL(table));
0953: }
0954:
0955: /**
0956: * Add the given sequence to the database schema.
0957: *
0958: * @return true if the operation was successful, false otherwise
0959: */
0960: public boolean createSequence(Sequence seq) throws SQLException {
0961: return executeSQL(_dict.getCreateSequenceSQL(seq));
0962: }
0963:
0964: /**
0965: * Drop the given sequence from the database schema.
0966: *
0967: * @return true if the operation was successful, false otherwise
0968: */
0969: public boolean dropSequence(Sequence seq) throws SQLException {
0970: return executeSQL(_dict.getDropSequenceSQL(seq));
0971: }
0972:
0973: /**
0974: * Add the given index to the database schema.
0975: *
0976: * @return true if the operation was successful, false otherwise
0977: */
0978: public boolean createIndex(Index idx, Table table)
0979: throws SQLException {
0980: int max = _dict.maxIndexesPerTable;
0981:
0982: int len = table.getIndexes().length;
0983: if (table.getPrimaryKey() != null)
0984: len += table.getPrimaryKey().getColumns().length;
0985:
0986: if (len >= max) {
0987: _log.warn(_loc
0988: .get("too-many-indexes", idx, table, max + ""));
0989: return false;
0990: }
0991:
0992: return executeSQL(_dict.getCreateIndexSQL(idx));
0993: }
0994:
0995: /**
0996: * Drop the given index from the database schema.
0997: *
0998: * @return true if the operation was successful, false otherwise
0999: */
1000: public boolean dropIndex(Index idx) throws SQLException {
1001: return executeSQL(_dict.getDropIndexSQL(idx));
1002: }
1003:
1004: /**
1005: * Add the given column to the database schema.
1006: *
1007: * @return true if the operation was successful, false otherwise
1008: */
1009: public boolean addColumn(Column col) throws SQLException {
1010: return executeSQL(_dict.getAddColumnSQL(col));
1011: }
1012:
1013: /**
1014: * Drop the given column from the database schema.
1015: *
1016: * @return true if the operation was successful, false otherwise
1017: */
1018: public boolean dropColumn(Column col) throws SQLException {
1019: return executeSQL(_dict.getDropColumnSQL(col));
1020: }
1021:
1022: /**
1023: * Add the given primary key to the database schema.
1024: *
1025: * @return true if the operation was successful, false otherwise
1026: */
1027: public boolean addPrimaryKey(PrimaryKey pk) throws SQLException {
1028: return executeSQL(_dict.getAddPrimaryKeySQL(pk));
1029: }
1030:
1031: /**
1032: * Drop the given primary key from the database schema.
1033: *
1034: * @return true if the operation was successful, false otherwise
1035: */
1036: public boolean dropPrimaryKey(PrimaryKey pk) throws SQLException {
1037: return executeSQL(_dict.getDropPrimaryKeySQL(pk));
1038: }
1039:
1040: /**
1041: * Add the given foreign key to the database schema.
1042: *
1043: * @return true if the operation was successful, false otherwise
1044: */
1045: public boolean addForeignKey(ForeignKey fk) throws SQLException {
1046: return executeSQL(_dict.getAddForeignKeySQL(fk));
1047: }
1048:
1049: /**
1050: * Drop the given foreign key from the database schema.
1051: *
1052: * @return true if the operation was successful, false otherwise
1053: */
1054: public boolean dropForeignKey(ForeignKey fk) throws SQLException {
1055: return executeSQL(_dict.getDropForeignKeySQL(fk));
1056: }
1057:
1058: /**
1059: * Return the database schema.
1060: */
1061: public SchemaGroup getDBSchemaGroup() {
1062: try {
1063: return getDBSchemaGroup(true);
1064: } catch (SQLException se) {
1065: throw SQLExceptions.getStore(se, _dict);
1066: }
1067: }
1068:
1069: /**
1070: * Set the database schema.
1071: */
1072: public void setDBSchemaGroup(SchemaGroup db) {
1073: _db = db;
1074: if (db != null)
1075: _fullDB = true;
1076: }
1077:
1078: /**
1079: * Return the database schema.
1080: *
1081: * @param full if false, only the tables named in the set schema
1082: * repository will be generated
1083: */
1084: private SchemaGroup getDBSchemaGroup(boolean full)
1085: throws SQLException {
1086: if (_db == null || (full && !_fullDB)) {
1087: SchemaGenerator gen = new SchemaGenerator(_conf);
1088: gen.setPrimaryKeys(_pks);
1089: gen.setForeignKeys(_fks);
1090: gen.setIndexes(_indexes);
1091: if (full)
1092: gen.generateSchemas();
1093: else {
1094: // generate only the tables in the given repository
1095: // group; some may not exist yet, which is OK; we just need
1096: // to make sure we can detect the changes to the ones that
1097: // do exist
1098: Collection tables = new LinkedList();
1099: SchemaGroup group = assertSchemaGroup();
1100: Schema[] schemas = group.getSchemas();
1101: Table[] tabs;
1102: for (int i = 0; i < schemas.length; i++) {
1103: tabs = schemas[i].getTables();
1104: for (int j = 0; j < tabs.length; j++) {
1105: if (tabs[j].getSchemaName() == null)
1106: tables.add("." + tabs[j].getName());
1107: else
1108: tables.add(tabs[j].getFullName());
1109: }
1110: }
1111: if (!tables.isEmpty())
1112: gen.generateSchemas((String[]) tables
1113: .toArray(new String[tables.size()]));
1114: }
1115: _db = gen.getSchemaGroup();
1116: }
1117: return _db;
1118: }
1119:
1120: private SchemaGroup assertSchemaGroup() {
1121: SchemaGroup local = getSchemaGroup();
1122: if (local == null)
1123: throw new InvalidStateException(_loc.get("tool-norepos"));
1124: return local;
1125: }
1126:
1127: /////////////
1128: // Utilities
1129: /////////////
1130:
1131: /**
1132: * Executes the given array of non-selecting SQL statements, correctly
1133: * logging the SQL calls and optionally ignoring errors.
1134: *
1135: * @return true if there was SQL to execute and the calls were
1136: * successful, false otherwise
1137: */
1138: private boolean executeSQL(String[] sql) throws SQLException {
1139: // if no sql, probably b/c dictionary doesn't support operation
1140: if (sql.length == 0)
1141: return false;
1142:
1143: boolean err = false;
1144: if (_writer == null) {
1145: // this is outside the try-catch because a failure here is
1146: // really bad, and should not be ignored.
1147: Connection conn = _ds.getConnection();
1148: Statement statement = null;
1149: boolean wasAuto = true;
1150: try {
1151: wasAuto = conn.getAutoCommit();
1152: if (!wasAuto)
1153: conn.setAutoCommit(true);
1154: for (int i = 0; i < sql.length; i++) {
1155: try {
1156: // some connections require that rollback be
1157: // called on the connection before any DDL statements
1158: // can be run on it, even when autocommit is on.
1159: // This is sometimes because the connection does not
1160: // allow DDL statements when there are multiple
1161: // commands issued on the connection, and the
1162: // connection pool may have issued some validation SQL.
1163: try {
1164: conn.rollback();
1165: } catch (Exception e) {
1166: }
1167:
1168: statement = conn.createStatement();
1169: statement.executeUpdate(sql[i]);
1170:
1171: // some connections seem to require an explicit
1172: // commit for DDL statements, even when autocommit
1173: // is on. The DataDirect drivers seem to suffer from
1174: // this limitation.
1175: try {
1176: conn.commit();
1177: } catch (Exception e) {
1178: }
1179: } catch (SQLException se) {
1180: err = true;
1181: handleException(se);
1182: } finally {
1183: if (statement != null)
1184: try {
1185: statement.close();
1186: } catch (SQLException se) {
1187: }
1188: }
1189: }
1190: } finally {
1191: if (!wasAuto)
1192: conn.setAutoCommit(false);
1193: try {
1194: conn.close();
1195: } catch (SQLException se) {
1196: }
1197: }
1198: } else {
1199: for (int i = 0; i < sql.length; i++)
1200: _writer.println(sql[i] + ";");
1201: _writer.flush();
1202: }
1203:
1204: return !err;
1205: }
1206:
1207: /**
1208: * Handle the given exception, logging it and optionally ignoring it,
1209: * depending on the flags this SchemaTool was created with.
1210: */
1211: private void handleException(SQLException sql) throws SQLException {
1212: if (!_ignoreErrs)
1213: throw sql;
1214: _log.warn(sql.getMessage(), sql);
1215: }
1216:
1217: ////////
1218: // Main
1219: ////////
1220:
1221: /**
1222: * Usage: java org.apache.openjpa.jdbc.schema.SchemaTool [option]*
1223: * [-action/-a <add | retain | drop | refresh | createDB | dropDB
1224: * | build | reflect | import | export>]
1225: * <.schema file or resource>*
1226: * Where the following options are recognized.
1227: * <ul>
1228: * <li><i>-properties/-p <properties file or resource></i>: The
1229: * path or resource name of a OpenJPA properties file containing
1230: * information such as the license key and connection data as
1231: * outlined in {@link JDBCConfiguration}. Optional.</li>
1232: * <li><i>-<property name> <property value></i>: All bean
1233: * properties of the OpenJPA {@link JDBCConfiguration} can be set by
1234: * using their names and supplying a value. For example:
1235: * <code>-licenseKey adslfja83r3lkadf</code></li>
1236: * <li><i>-ignoreErrors/-i <true/t | false/f></i>: If false, an
1237: * exception will will be thrown if the tool encounters any database
1238: * exceptions; defaults to <code>false</code>.</li>
1239: * <li><i>-file/-f <stdout | output file or resource></i>: Use this
1240: * option to write a SQL script for the planned schema modifications,
1241: * rather than committing them to the database. This option also
1242: * applies to the <code>export</code> and <code>reflect</code> actions.</li>
1243: * <li><i>-openjpaTables/-kt <true/t | false/f></i>: Under the
1244: * <code>reflect</code> action, whether to reflect on tables with
1245: * the name <code>OPENJPA_*</code>. Under other actions, whether to
1246: * drop such tables. Defaults to <code>false</code>.</li>
1247: * <li><i>-dropTables/-dt <true/t | false/f></i>: Set this option to
1248: * true to drop tables that appear to be unused during
1249: * <code>retain</code> and <code>refresh</code> actions. Defaults to
1250: * <code>true</code>.</li>
1251: * <li><i>-dropSequences/-dsq <true/t | false/f></i>: Set this option
1252: * to true to drop sequences that appear to be unused during
1253: * <code>retain</code> and <code>refresh</code> actions. Defaults to
1254: * <code>true</code>.</li>
1255: * <li><i>-primaryKeys/-pk <true/t | false/f></i>: Whether primary
1256: * keys on existing tables are manipulated. Defaults to true.</li>
1257: * <li><i>-foreignKeys/-fk <true/t | false/f></i>: Whether foreign
1258: * keys on existing tables are manipulated. Defaults to true.</li>
1259: * <li><i>-indexes/-ix <true/t | false/f></i>: Whether indexes
1260: * on existing tables are manipulated. Defaults to true.</li>
1261: * <li><i>-sequences/-sq <true/t | false/f></i>: Whether to
1262: * manipulate sequences. Defaults to true.</li>
1263: * <li><i>-record/-r <true/t | false/f></i>: Set this option to
1264: * <code>false</code> to prevent writing the schema changes to the
1265: * current {@link SchemaFactory}.</li>
1266: * </ul>
1267: * Actions can be composed in a comma-separated list. The various actions
1268: * are as follows.
1269: * <ul>
1270: * <li><i>add</i>: Bring the schema up-to-date with the latest
1271: * changes to the schema XML data by adding tables, columns,
1272: * indexes, etc. This action never drops any data. This is the
1273: * default action.</li>
1274: * <li><i>retain</i>: Keep all schema components in the schema XML, but
1275: * drop the rest from the database. This action never adds any data.</li>
1276: * <li><i>drop</i>: Drop all the schema components in the schema XML.</li>
1277: * <li><i>refresh</i>: Equivalent to retain, then add.</li>
1278: * <li><i>createDB</i>: Execute SQL to re-create the current database.
1279: * This action is typically used in conjuction with the
1280: * <code>file</code> option.</li>
1281: * <li><i>build</i>: Execute SQL to build the schema defined in the XML.
1282: * Because it doesn't take the current database schema into account,
1283: * this action is typically used in conjuction with the
1284: * <code>file</code> option.</li>
1285: * <li><i>reflect</i>: Reflect on the current database schema. Write the
1286: * schema's XML representation to the file specified with the
1287: * <code>file</code> option, or to stdout if no file is given.</li>
1288: * <li><i>dropDB</i>: Execute SQL to drop the current database. This
1289: * action implies <code>dropTables</code>.</li>
1290: * <li><i>deleteTableContents</i>: Execute SQL to delete all rows from
1291: * all tables that OpenJPA knows about.</li>
1292: * <li><i>import</i>: Import the given XML schema definition into the
1293: * current {@link SchemaFactory}.</li>
1294: * <li><i>export</i>: Export the current {@link SchemaFactory}'s recorded
1295: * schema to an XML schema definition file.</li>
1296: * </ul>
1297: * Examples:
1298: * <ul>
1299: * <li>Write a script to stdout to re-create the current database
1300: * schema:<br />
1301: * <code>java org.apache.openjpa.jdbc.schema.SchemaTool -f stdout
1302: * -a createDB</code></li>
1303: * <li>Drop the current database schema:<br />
1304: * <code>java org.apache.openjpa.jdbc.schema.SchemaTool
1305: * -a dropDB</code></li>
1306: * <li>Refresh the schema and delete all records in all tables:<br />
1307: * <code>java org.apache.openjpa.jdbc.schema.SchemaTool
1308: * -a refresh,deleteTableContents</code></li>
1309: * <li>Create a schema based on an XML schema definition file:<br />
1310: * <code>java org.apache.openjpa.jdbc.schema.SchemaTool
1311: * myschema.xml</code></li>
1312: * </ul>
1313: */
1314: public static void main(String[] args) throws IOException,
1315: SQLException {
1316: Options opts = new Options();
1317: final String[] arguments = opts.setFromCmdLine(args);
1318: boolean ret = Configurations.runAgainstAllAnchors(opts,
1319: new Configurations.Runnable() {
1320: public boolean run(Options opts) throws Exception {
1321: JDBCConfiguration conf = new JDBCConfigurationImpl();
1322: try {
1323: return SchemaTool
1324: .run(conf, arguments, opts);
1325: } finally {
1326: conf.close();
1327: }
1328: }
1329: });
1330: if (!ret)
1331: System.out.println(_loc.get("tool-usage"));
1332: }
1333:
1334: /**
1335: * Run the tool. Returns false if any invalid options were given.
1336: *
1337: * @see #main
1338: */
1339: public static boolean run(JDBCConfiguration conf, String[] args,
1340: Options opts) throws IOException, SQLException {
1341: Flags flags = new Flags();
1342: flags.dropTables = opts.removeBooleanProperty("dropTables",
1343: "dt", flags.dropTables);
1344: flags.dropSequences = opts.removeBooleanProperty(
1345: "dropSequences", "dsq", flags.dropSequences);
1346: flags.ignoreErrors = opts.removeBooleanProperty("ignoreErrors",
1347: "i", flags.ignoreErrors);
1348: flags.openjpaTables = opts.removeBooleanProperty(
1349: "openjpaTables", "ot", flags.openjpaTables);
1350: flags.primaryKeys = opts.removeBooleanProperty("primaryKeys",
1351: "pk", flags.primaryKeys);
1352: flags.foreignKeys = opts.removeBooleanProperty("foreignKeys",
1353: "fks", flags.foreignKeys);
1354: flags.indexes = opts.removeBooleanProperty("indexes", "ix",
1355: flags.indexes);
1356: flags.sequences = opts.removeBooleanProperty("sequences", "sq",
1357: flags.sequences);
1358: flags.record = opts.removeBooleanProperty("record", "r",
1359: flags.record);
1360: String fileName = opts.removeProperty("file", "f", null);
1361: String schemas = opts.removeProperty("s");
1362: if (schemas != null)
1363: opts.setProperty("schemas", schemas);
1364:
1365: String[] actions = opts.removeProperty("action", "a",
1366: flags.action).split(",");
1367:
1368: // setup a configuration instance with cmd-line info
1369: Configurations.populateConfiguration(conf, opts);
1370:
1371: // create script writer
1372: ClassLoader loader = conf.getClassResolverInstance()
1373: .getClassLoader(SchemaTool.class, null);
1374: flags.writer = Files.getWriter(fileName, loader);
1375:
1376: boolean returnValue = true;
1377: for (int i = 0; i < actions.length; i++) {
1378: flags.action = actions[i];
1379: returnValue &= run(conf, args, flags, loader);
1380: }
1381:
1382: return returnValue;
1383: }
1384:
1385: /**
1386: * Run the tool. Return false if invalid options were given.
1387: */
1388: public static boolean run(JDBCConfiguration conf, String[] args,
1389: Flags flags, ClassLoader loader) throws IOException,
1390: SQLException {
1391: Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
1392: if (ACTION_REFLECT.equals(flags.action)) {
1393: if (args.length > 0)
1394: return false;
1395: if (flags.writer == null)
1396: flags.writer = new PrintWriter(System.out);
1397:
1398: SchemaGenerator gen = new SchemaGenerator(conf);
1399: gen.setPrimaryKeys(flags.primaryKeys);
1400: gen.setIndexes(flags.indexes);
1401: gen.setForeignKeys(flags.foreignKeys);
1402: gen.setSequences(flags.sequences);
1403: gen.setOpenJPATables(flags.openjpaTables);
1404:
1405: String schemas = conf.getSchemas();
1406: if (StringUtils.isEmpty(schemas))
1407: schemas = "all";
1408: log.info(_loc.get("sch-reflect", schemas));
1409: gen.generateSchemas();
1410:
1411: // record the schema
1412: log.info(_loc.get("sch-reflect-write"));
1413: SchemaSerializer ser = new XMLSchemaSerializer(conf);
1414: ser.addAll(gen.getSchemaGroup());
1415: ser.serialize(flags.writer, ser.PRETTY);
1416: return true;
1417: }
1418:
1419: if (args.length == 0 && !ACTION_CREATEDB.equals(flags.action)
1420: && !ACTION_DROPDB.equals(flags.action)
1421: && !ACTION_EXPORT.equals(flags.action)
1422: && !ACTION_DELETE_TABLE_CONTENTS.equals(flags.action))
1423: return false;
1424:
1425: // parse in the arguments
1426: SchemaParser parser = new XMLSchemaParser(conf);
1427: parser.setDelayConstraintResolve(true);
1428: File file;
1429: for (int i = 0; i < args.length; i++) {
1430: file = Files.getFile(args[i], loader);
1431: log.info(_loc.get("tool-running", file));
1432: parser.parse(file);
1433: }
1434: parser.resolveConstraints();
1435:
1436: if (ACTION_IMPORT.equals(flags.action)) {
1437: log.info(_loc.get("tool-import-store"));
1438: SchemaGroup schema = parser.getSchemaGroup();
1439: conf.getSchemaFactoryInstance().storeSchema(schema);
1440: return true;
1441: }
1442: if (ACTION_EXPORT.equals(flags.action)) {
1443: if (flags.writer == null)
1444: flags.writer = new PrintWriter(System.out);
1445:
1446: log.info(_loc.get("tool-export-gen"));
1447: SchemaGroup schema = conf.getSchemaFactoryInstance()
1448: .readSchema();
1449:
1450: log.info(_loc.get("tool-export-write"));
1451: SchemaSerializer ser = new XMLSchemaSerializer(conf);
1452: ser.addAll(schema);
1453: ser.serialize(flags.writer, ser.PRETTY);
1454: return true;
1455: }
1456:
1457: SchemaTool tool = new SchemaTool(conf, flags.action);
1458: tool.setIgnoreErrors(flags.ignoreErrors);
1459: tool.setDropTables(flags.dropTables);
1460: tool.setSequences(flags.sequences); // set before dropseqs
1461: tool.setDropSequences(flags.dropSequences);
1462: tool.setPrimaryKeys(flags.primaryKeys);
1463: tool.setForeignKeys(flags.foreignKeys);
1464: tool.setIndexes(flags.indexes);
1465: tool.setOpenJPATables(flags.openjpaTables);
1466: if (args.length > 0)
1467: tool.setSchemaGroup(parser.getSchemaGroup());
1468: if (flags.writer != null)
1469: tool.setWriter(flags.writer);
1470:
1471: log.info(_loc.get("tool-action", flags.action));
1472: try {
1473: tool.run();
1474: } finally {
1475: if (flags.record) {
1476: log.info(_loc.get("tool-record"));
1477: tool.record();
1478: }
1479: }
1480: if (flags.writer != null)
1481: flags.writer.flush();
1482:
1483: return true;
1484: }
1485:
1486: /**
1487: * Run flags.
1488: */
1489: public static class Flags {
1490:
1491: public String action = ACTION_ADD;
1492: public Writer writer = null;
1493: public boolean dropTables = true;
1494: public boolean dropSequences = true;
1495: public boolean ignoreErrors = false;
1496: public boolean openjpaTables = false;
1497: public boolean primaryKeys = true;
1498: public boolean foreignKeys = true;
1499: public boolean indexes = true;
1500: public boolean sequences = true;
1501: public boolean record = true;
1502: }
1503: }
|