Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » schema » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.jdbc.schema;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.io.PrintWriter;
   24   import java.io.Writer;
   25   import java.sql.Connection;
   26   import java.sql.DatabaseMetaData;
   27   import java.sql.SQLException;
   28   import java.sql.Statement;
   29   import java.util.Arrays;
   30   import java.util.Collection;
   31   import java.util.HashSet;
   32   import java.util.Iterator;
   33   import java.util.LinkedHashSet;
   34   import java.util.LinkedList;
   35   import java.util.Set;
   36   import javax.sql.DataSource;
   37   
   38   import org.apache.commons.lang.StringUtils;
   39   import org.apache.openjpa.conf.OpenJPAConfiguration;
   40   import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
   41   import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
   42   import org.apache.openjpa.jdbc.sql.DBDictionary;
   43   import org.apache.openjpa.jdbc.sql.SQLExceptions;
   44   import org.apache.openjpa.lib.conf.Configurations;
   45   import org.apache.openjpa.lib.log.Log;
   46   import org.apache.openjpa.lib.util.Files;
   47   import org.apache.openjpa.lib.util.Localizer;
   48   import org.apache.openjpa.lib.util.Options;
   49   import org.apache.openjpa.util.InvalidStateException;
   50   
   51   /**
   52    * The SchemaTool is used to manage the database schema. Note that the
   53    * tool never adds or drops unique constraints from existing tables, because
   54    * JDBC {@link DatabaseMetaData} does not include information on these
   55    * constraints.
   56    *
   57    * @author Abe White
   58    * @author Patrick Linskey
   59    */
   60   public class SchemaTool {
   61   
   62       public static final String ACTION_ADD = "add";
   63       public static final String ACTION_DROP = "drop";
   64       public static final String ACTION_RETAIN = "retain";
   65       public static final String ACTION_REFRESH = "refresh";
   66       public static final String ACTION_BUILD = "build";
   67       public static final String ACTION_REFLECT = "reflect";
   68       public static final String ACTION_CREATEDB = "createDB";
   69       public static final String ACTION_DROPDB = "dropDB";
   70       public static final String ACTION_IMPORT = "import";
   71       public static final String ACTION_EXPORT = "export";
   72       public static final String ACTION_DELETE_TABLE_CONTENTS = 
   73           "deleteTableContents";
   74   
   75       public static final String[] ACTIONS = new String[]{
   76           ACTION_ADD,
   77           ACTION_DROP,
   78           ACTION_RETAIN,
   79           ACTION_REFRESH,
   80           ACTION_BUILD,
   81           ACTION_REFLECT,
   82           ACTION_CREATEDB,
   83           ACTION_DROPDB,
   84           ACTION_IMPORT,
   85           ACTION_EXPORT,
   86           ACTION_DELETE_TABLE_CONTENTS,
   87       };
   88   
   89       private static final Localizer _loc = Localizer.forPackage
   90           (SchemaTool.class);
   91   
   92       private final JDBCConfiguration _conf;
   93       private final DataSource _ds;
   94       private final Log _log;
   95       private final DBDictionary _dict;
   96       private final String _action;
   97       private boolean _ignoreErrs = false;
   98       private boolean _openjpaTables = false;
   99       private boolean _dropTables = true;
  100       private boolean _dropSeqs = true;
  101       private boolean _pks = true;
  102       private boolean _fks = true;
  103       private boolean _indexes = true;
  104       private boolean _seqs = true;
  105       private PrintWriter _writer = null;
  106       private SchemaGroup _group = null;
  107       private SchemaGroup _db = null;
  108       private boolean _fullDB = false;
  109   
  110       /**
  111        * Default constructor. Tools constructed this way will not have an
  112        * action, so the {@link #run()} method will be a no-op.
  113        */
  114       public SchemaTool(JDBCConfiguration conf) {
  115           this(conf, null);
  116       }
  117   
  118       /**
  119        * Construct a tool to perform the given action.
  120        */
  121       public SchemaTool(JDBCConfiguration conf, String action) {
  122           if (action != null && !Arrays.asList(ACTIONS).contains(action))
  123               throw new IllegalArgumentException("action == " + action);
  124   
  125           _conf = conf;
  126           _action = action;
  127           _ds = conf.getDataSource2(null);
  128           _log = conf.getLog(JDBCConfiguration.LOG_SCHEMA);
  129   
  130           // initialize this up-front; otherwise the dbdictionaryfactory might
  131           // try to take a connection to initialize when we've already got one:
  132           // bad news if the max pool is 1
  133           _dict = _conf.getDBDictionaryInstance();
  134       }
  135   
  136       /**
  137        * The action supplied on construction.
  138        */
  139       public String getAction() {
  140           return _action;
  141       }
  142   
  143       /**
  144        * If true, SQLExceptions thrown during schema manipulation will be
  145        * printed but ignored.
  146        */
  147       public boolean getIgnoreErrors() {
  148           return _ignoreErrs;
  149       }
  150   
  151       /**
  152        * If true, SQLExceptions thrown during schema manipulation will be
  153        * printed but ignored.
  154        */
  155       public void setIgnoreErrors(boolean ignoreErrs) {
  156           _ignoreErrs = ignoreErrs;
  157       }
  158   
  159       /**
  160        * Whether to act on special tables used by OpenJPA components
  161        * for bookkeeping.
  162        */
  163       public boolean getOpenJPATables() {
  164           return _openjpaTables;
  165       }
  166   
  167       /**
  168        * Whether to act on special tables used by OpenJPA components
  169        * for bookkeeping.
  170        */
  171       public void setOpenJPATables(boolean openjpaTables) {
  172           _openjpaTables = openjpaTables;
  173       }
  174   
  175       /**
  176        * If true, tables that appear to be unused will be dropped. Defaults to
  177        * true.
  178        */
  179       public boolean getDropTables() {
  180           return _dropTables;
  181       }
  182   
  183       /**
  184        * If true, tables that appear to be unused will be dropped. Defaults to
  185        * true.
  186        */
  187       public void setDropTables(boolean dropTables) {
  188           _dropTables = dropTables;
  189       }
  190   
  191       /**
  192        * If true, sequences that appear to be unused will be dropped. Defaults
  193        * to true.
  194        */
  195       public boolean getDropSequences() {
  196           return _dropSeqs;
  197       }
  198   
  199       /**
  200        * If true, sequences that appear to be unused will be dropped. Defaults
  201        * to true.
  202        */
  203       public void setDropSequences(boolean dropSeqs) {
  204           _dropSeqs = dropSeqs;
  205           if (dropSeqs)
  206               setSequences(true);
  207       }
  208   
  209       /**
  210        * Whether sequences should be manipulated. Defaults to true.
  211        */
  212       public boolean getSequences() {
  213           return _seqs;
  214       }
  215   
  216       /**
  217        * Whether sequences should be manipulated. Defaults to true.
  218        */
  219       public void setSequences(boolean seqs) {
  220           _seqs = seqs;
  221       }
  222   
  223       /**
  224        * Whether indexes on existing tables should be manipulated.
  225        * Defaults to true.
  226        */
  227       public boolean getIndexes() {
  228           return _indexes;
  229       }
  230   
  231       /**
  232        * Whether indexes on existing tables should be manipulated.
  233        * Defaults to true.
  234        */
  235       public void setIndexes(boolean indexes) {
  236           _indexes = indexes;
  237       }
  238   
  239       /**
  240        * Whether foreign keys on existing tables should be manipulated.
  241        * Defaults to true.
  242        */
  243       public boolean getForeignKeys() {
  244           return _fks;
  245       }
  246   
  247       /**
  248        * Whether foreign keys on existing tables should be manipulated.
  249        * Defaults to true.
  250        */
  251       public void setForeignKeys(boolean fks) {
  252           _fks = fks;
  253       }
  254   
  255       /**
  256        * Whether primary keys on existing tables should be manipulated.
  257        * Defaults to true.
  258        */
  259       public boolean getPrimaryKeys() {
  260           return _pks;
  261       }
  262   
  263       /**
  264        * Whether primary keys on existing tables should be manipulated.
  265        * Defaults to true.
  266        */
  267       public void setPrimaryKeys(boolean pks) {
  268           _pks = pks;
  269       }
  270   
  271       /**
  272        * The stream to write to for the creation of SQL scripts. If the
  273        * stream is non-null, all SQL will be written to this stream rather than
  274        * executed against the database.
  275        */
  276       public Writer getWriter() {
  277           return _writer;
  278       }
  279   
  280       /**
  281        * The stream to write to for the creation of SQL scripts. If the
  282        * stream is non-null, all SQL will be written to this stream rather than
  283        * executed against the database.
  284        */
  285       public void setWriter(Writer writer) {
  286           if (writer == null)
  287               _writer = null;
  288           else if (writer instanceof PrintWriter)
  289               _writer = (PrintWriter) writer;
  290           else
  291               _writer = new PrintWriter(writer);
  292       }
  293   
  294       /**
  295        * Return the schema group the tool will act on.
  296        */
  297       public SchemaGroup getSchemaGroup() {
  298           return _group;
  299       }
  300   
  301       /**
  302        * Set the schema group the tool will act on.
  303        */
  304       public void setSchemaGroup(SchemaGroup group) {
  305           _group = group;
  306       }
  307   
  308       ///////////
  309       // Actions
  310       ///////////
  311   
  312       /**
  313        * Run the tool action.
  314        */
  315       public void run()
  316           throws SQLException {
  317           if (_action == null)
  318               return;
  319   
  320           if (ACTION_ADD.equals(_action))
  321               add();
  322           else if (ACTION_DROP.equals(_action))
  323               drop();
  324           else if (ACTION_RETAIN.equals(_action))
  325               retain();
  326           else if (ACTION_REFRESH.equals(_action))
  327               refresh();
  328           else if (ACTION_BUILD.equals(_action))
  329               build();
  330           else if (ACTION_CREATEDB.equals(_action))
  331               createDB();
  332           else if (ACTION_DROPDB.equals(_action))
  333               dropDB();
  334           else if (ACTION_DELETE_TABLE_CONTENTS.equals(_action))
  335               deleteTableContents();
  336       }
  337   
  338       /**
  339        * Adds any components present in the schema repository but absent from
  340        * the database. Package-private for testing.
  341        */
  342       void add()
  343           throws SQLException {
  344           add(getDBSchemaGroup(false), assertSchemaGroup());
  345       }
  346   
  347       /**
  348        * Drops all schema components in the schema repository that also exist
  349        * in the database. Package-private for testing.
  350        */
  351       void drop()
  352           throws SQLException {
  353           drop(getDBSchemaGroup(false), assertSchemaGroup());
  354       }
  355   
  356       /**
  357        * Drops database components that are not mentioned in the schema
  358        * repository. Package-private for testing.
  359        */
  360       void retain()
  361           throws SQLException {
  362           retain(getDBSchemaGroup(true), assertSchemaGroup(),
  363               getDropTables(), getDropSequences());
  364       }
  365   
  366       /**
  367        * Adds any components present in the schema repository but absent from
  368        * the database, and drops unused database components.
  369        * Package-private for testing.
  370        */
  371       void refresh()
  372           throws SQLException {
  373           SchemaGroup local = assertSchemaGroup();
  374           SchemaGroup db = getDBSchemaGroup(true);
  375           retain(db, local, getDropTables(), getDropSequences());
  376           add(db, local);
  377       }
  378   
  379       /**
  380        * Re-execute all SQL used for the creation of the current database;
  381        * this action is usually used when creating SQL scripts.
  382        * Package-private for testing.
  383        */
  384       void createDB()
  385           throws SQLException {
  386           SchemaGroup group = new SchemaGroup();
  387           group.addSchema();
  388           add(group, getDBSchemaGroup(true));
  389       }
  390   
  391       /**
  392        * Re-execute all SQL used for the creation of the current database;
  393        * this action is usually used when creating SQL scripts.
  394        * Package-private for testing.
  395        */
  396       void build()
  397           throws SQLException {
  398           SchemaGroup group = new SchemaGroup();
  399           group.addSchema();
  400           add(group, assertSchemaGroup());
  401       }
  402   
  403       /**
  404        * Drop the current database. Package-private for testing.
  405        */
  406       void dropDB()
  407           throws SQLException {
  408           retain(getDBSchemaGroup(true), new SchemaGroup(), true, true);
  409       }
  410   
  411       /**
  412        * Issue DELETE statement against all known tables.
  413        */
  414       private void deleteTableContents() 
  415           throws SQLException {
  416           SchemaGroup group = getSchemaGroup();
  417           Schema[] schemas = group.getSchemas();
  418           Collection tables = new LinkedHashSet();
  419           for (int i = 0; i < schemas.length; i++) {
  420               Table[] ts = schemas[i].getTables();
  421               for (int j = 0; j < ts.length; j++)
  422                   tables.add(ts[j]);
  423           }
  424           Table[] tableArray = (Table[]) tables.toArray(new Table[tables.size()]);
  425           String[] sql = _conf.getDBDictionaryInstance()
  426               .getDeleteTableContentsSQL(tableArray);
  427           if (!executeSQL(sql))
  428               _log.warn(_loc.get("delete-table-contents"));
  429       }
  430   
  431       /**
  432        * Record the changes made to the DB in the current {@link SchemaFactory}.
  433        */
  434       public void record() {
  435           if (_db != null && _writer == null)
  436               _conf.getSchemaFactoryInstance().storeSchema(_db);
  437       }
  438   
  439       /**
  440        * Adds all database components in the repository schema that are not
  441        * present in the given database schema to the database.
  442        */
  443       private void add(SchemaGroup db, SchemaGroup repos)
  444           throws SQLException {
  445           // add sequences
  446           Schema[] schemas = repos.getSchemas();
  447           Schema schema;
  448           if (_seqs) {
  449               Sequence[] seqs;
  450               for (int i = 0; i < schemas.length; i++) {
  451                   seqs = schemas[i].getSequences();
  452                   for (int j = 0; j < seqs.length; j++) {
  453                       if (db.findSequence(schemas[i], seqs[j].getFullName()) != null)
  454                           continue;
  455   
  456                       if (createSequence(seqs[j])) {
  457                           schema = db.getSchema(seqs[j].getSchemaName());
  458                           if (schema == null)
  459                               schema = db.addSchema(seqs[j].getSchemaName());
  460                           schema.importSequence(seqs[j]);
  461                       } else
  462                           _log.warn(_loc.get("add-seq", seqs[j]));
  463                   }
  464               }
  465           }
  466   
  467           // order is important in this method; start with columns
  468           Table[] tabs;
  469           Table dbTable;
  470           Column[] cols;
  471           Column col;
  472           for (int i = 0; i < schemas.length; i++) {
  473               tabs = schemas[i].getTables();
  474               for (int j = 0; j < tabs.length; j++) {
  475                   cols = tabs[j].getColumns();
  476                   dbTable = db.findTable(schemas[i], tabs[j].getFullName());
  477                   for (int k = 0; k < cols.length; k++) {
  478                       if (dbTable != null) {
  479                           col = dbTable.getColumn(cols[k].getName());
  480                           if (col == null) {
  481                               if (addColumn(cols[k]))
  482                                   dbTable.importColumn(cols[k]);
  483                               else
  484                                   _log.warn(_loc.get("add-col", cols[k],
  485                                       tabs[j]));
  486                           } else if (!cols[k].equalsColumn(col)) {
  487                               _log.warn(_loc.get("bad-col", new Object[]{
  488                                   col, dbTable, col.getDescription(),
  489                                   cols[k].getDescription() }));
  490                           }
  491                       }
  492                   }
  493               }
  494           }
  495   
  496           // primary keys
  497           if (_pks) {
  498               PrimaryKey pk;
  499               for (int i = 0; i < schemas.length; i++) {
  500                   tabs = schemas[i].getTables();
  501                   for (int j = 0; j < tabs.length; j++) {
  502                       pk = tabs[j].getPrimaryKey();
  503                       dbTable = db.findTable(schemas[i], tabs[j].getFullName());
  504                       if (pk != null && !pk.isLogical() && dbTable != null) {
  505                           if (dbTable.getPrimaryKey() == null
  506                               && addPrimaryKey(pk))
  507                               dbTable.importPrimaryKey(pk);
  508                           else if (dbTable.getPrimaryKey() == null)
  509                               _log.warn(_loc.get("add-pk", pk, tabs[j]));
  510                           else if (!pk.equalsPrimaryKey(dbTable.getPrimaryKey()))
  511                               _log.warn(_loc.get("bad-pk",
  512                                   dbTable.getPrimaryKey(), dbTable));
  513                       }
  514                   }
  515               }
  516           }
  517   
  518           // tables
  519           Set newTables = new HashSet();
  520           for (int i = 0; i < schemas.length; i++) {
  521               tabs = schemas[i].getTables();
  522               for (int j = 0; j < tabs.length; j++) {
  523                   if (db.findTable(schemas[i], tabs[j].getFullName()) != null)
  524                       continue;
  525   
  526                   if (createTable(tabs[j])) {
  527                       newTables.add(tabs[j]);
  528                       schema = db.getSchema(tabs[j].getSchemaName());
  529                       if (schema == null)
  530                           schema = db.addSchema(tabs[j].getSchemaName());
  531                       schema.importTable(tabs[j]);
  532                   } else
  533                       _log.warn(_loc.get("add-table", tabs[j]));
  534               }
  535           }
  536   
  537           // indexes
  538           Index[] idxs;
  539           Index idx;
  540           for (int i = 0; i < schemas.length; i++) {
  541               tabs = schemas[i].getTables();
  542               for (int j = 0; j < tabs.length; j++) {
  543                   // create indexes on new tables even if indexes
  544                   // have been turned off
  545                   if (!_indexes && !newTables.contains(tabs[j]))
  546                       continue;
  547   
  548                   idxs = tabs[j].getIndexes();
  549                   dbTable = db.findTable(schemas[i], tabs[j].getFullName());
  550                   for (int k = 0; k < idxs.length; k++) {
  551                       if (dbTable != null) {
  552                           idx = findIndex(dbTable, idxs[k]);
  553                           if (idx == null) {
  554                               if (createIndex(idxs[k], dbTable))
  555                                   dbTable.importIndex(idxs[k]);
  556                               else
  557                                   _log.warn(_loc.get("add-index", idxs[k],
  558                                       tabs[j]));
  559                           } else if (!idxs[k].equalsIndex(idx))
  560                               _log.warn(_loc.get("bad-index", idx, dbTable));
  561                       }
  562                   }
  563               }
  564           }
  565   
  566           // Unique Constraints on group of columns
  567           Unique[] uniques;
  568           for (int i = 0; i < schemas.length; i++) {
  569               tabs = schemas[i].getTables();
  570               for (int j = 0; j < tabs.length; j++) {
  571                   // create unique constraints only on new tables 
  572                   if (!newTables.contains(tabs[j]))
  573                       continue;
  574   
  575                   uniques = tabs[j].getUniques();
  576                   if (uniques == null || uniques.length == 0)
  577                       continue;
  578                   dbTable = db.findTable(tabs[j]);
  579                   if (dbTable == null)
  580                       continue;
  581                   for (int k = 0; k < uniques.length; k++) {
  582                       dbTable.importUnique(uniques[k]);
  583                   }
  584               }
  585           }
  586           
  587           // foreign keys
  588           ForeignKey[] fks;
  589           ForeignKey fk;
  590           for (int i = 0; i < schemas.length; i++) {
  591               tabs = schemas[i].getTables();
  592               for (int j = 0; j < tabs.length; j++) {
  593                   // create foreign keys on new tables even if fks
  594                   // have been turned off
  595                   if (!_fks && !newTables.contains(tabs[j]))
  596                       continue;
  597   
  598                   fks = tabs[j].getForeignKeys();
  599                   dbTable = db.findTable(schemas[i],tabs[j].getFullName());
  600                   for (int k = 0; k < fks.length; k++) {
  601                       if (!fks[k].isLogical() && dbTable != null) {
  602                           fk = findForeignKey(dbTable, fks[k]);
  603                           if (fk == null) {
  604                               if (addForeignKey(fks[k]))
  605                                   dbTable.importForeignKey(fks[k]);
  606                               else
  607                                   _log.warn(_loc.get("add-fk",
  608                                       fks[k], tabs[j]));
  609                           } else if (!fks[k].equalsForeignKey(fk))
  610                               _log.warn(_loc.get("bad-fk", fk, dbTable));
  611                       }
  612                   }
  613               }
  614           }
  615       }
  616   
  617       /**
  618        * Drops all database components that are in the given database schema
  619        * but not in the repository schema.
  620        */
  621       private void retain(SchemaGroup db, SchemaGroup repos, boolean tables,
  622           boolean sequences)
  623           throws SQLException {
  624           Schema[] schemas = db.getSchemas();
  625           if (_seqs && sequences) {
  626               Sequence[] seqs;
  627               for (int i = 0; i < schemas.length; i++) {
  628                   seqs = schemas[i].getSequences();
  629                   for (int j = 0; j < seqs.length; j++) {
  630                       if (!isDroppable(seqs[j]))
  631                           continue;
  632                       if (repos.findSequence(seqs[j]) == null) {
  633                           if (dropSequence(seqs[j]))
  634                               schemas[i].removeSequence(seqs[j]);
  635                           else
  636                               _log.warn(_loc.get("drop-seq", seqs[j]));
  637                       }
  638                   }
  639               }
  640           }
  641   
  642           // order is important in this method; start with foreign keys
  643           Table[] tabs;
  644           Table reposTable;
  645           if (_fks) {
  646               ForeignKey[] fks;
  647               ForeignKey fk;
  648               for (int i = 0; i < schemas.length; i++) {
  649                   tabs = schemas[i].getTables();
  650                   for (int j = 0; j < tabs.length; j++) {
  651                       if (!isDroppable(tabs[j]))
  652                           continue;
  653                       fks = tabs[j].getForeignKeys();
  654                       reposTable = repos.findTable(tabs[j]);
  655                       if (!tables && reposTable == null)
  656                           continue;
  657   
  658                       for (int k = 0; k < fks.length; k++) {
  659                           if (fks[k].isLogical())
  660                               continue;
  661   
  662                           fk = null;
  663                           if (reposTable != null)
  664                               fk = findForeignKey(reposTable, fks[k]);
  665                           if (reposTable == null || fk == null
  666                               || !fks[k].equalsForeignKey(fk)) {
  667                               if (dropForeignKey(fks[k]))
  668                                   tabs[j].removeForeignKey(fks[k]);
  669                               else
  670                                   _log.warn(_loc.get("drop-fk", fks[k],
  671                                       tabs[j]));
  672                           }
  673                       }
  674                   }
  675               }
  676           }
  677   
  678           // primary keys
  679           if (_pks) {
  680               PrimaryKey pk;
  681               for (int i = 0; i < schemas.length; i++) {
  682                   tabs = schemas[i].getTables();
  683                   for (int j = 0; j < tabs.length; j++) {
  684                       if (!isDroppable(tabs[j]))
  685                           continue;
  686                       pk = tabs[j].getPrimaryKey();
  687                       if (pk != null && pk.isLogical())
  688                           continue;
  689   
  690                       reposTable = repos.findTable(tabs[j]);
  691                       if (pk != null && reposTable != null
  692                           && (reposTable.getPrimaryKey() == null
  693                           || !pk.equalsPrimaryKey(reposTable.getPrimaryKey()))) {
  694                           if (dropPrimaryKey(pk))
  695                               tabs[j].removePrimaryKey();
  696                           else
  697                               _log.warn(_loc.get("drop-pk", pk, tabs[j]));
  698                       }
  699                   }
  700               }
  701           }
  702   
  703           // columns
  704           Column[] cols;
  705           Column col;
  706           Collection drops = new LinkedList();
  707           for (int i = 0; i < schemas.length; i++) {
  708               tabs = schemas[i].getTables();
  709               for (int j = 0; j < tabs.length; j++) {
  710                   if (!isDroppable(tabs[j]))
  711                       continue;
  712                   cols = tabs[j].getColumns();
  713                   reposTable = repos.findTable(tabs[j]);
  714                   if (reposTable != null) {
  715                       for (int k = 0; k < cols.length; k++) {
  716                           col = reposTable.getColumn(cols[k].getName());
  717                           if (col == null || !cols[k].equalsColumn(col)) {
  718                               if (tabs[j].getColumns().length == 1)
  719                                   drops.add(tabs[j]);
  720                               else if (dropColumn(cols[k]))
  721                                   tabs[j].removeColumn(cols[k]);
  722                               else
  723                                   _log.warn(_loc.get("drop-col", cols[k],
  724                                       tabs[j]));
  725                           }
  726                       }
  727                   }
  728               }
  729           }
  730   
  731           // now tables
  732           if (tables) {
  733               for (int i = 0; i < schemas.length; i++) {
  734                   tabs = schemas[i].getTables();
  735                   for (int j = 0; j < tabs.length; j++)
  736                       if (!!isDroppable(tabs[j])
  737                           && repos.findTable(tabs[j]) == null)
  738                           drops.add(tabs[j]);
  739               }
  740           }
  741           dropTables(drops, db);
  742       }
  743   
  744       /**
  745        * Drops all database components in the given repository schema.
  746        */
  747       private void drop(SchemaGroup db, SchemaGroup repos)
  748           throws SQLException {
  749           // drop sequences
  750           Schema[] schemas = repos.getSchemas();
  751           if (_seqs) {
  752               Sequence[] seqs;
  753               Sequence dbSeq;
  754               for (int i = 0; i < schemas.length; i++) {
  755                   seqs = schemas[i].getSequences();
  756                   for (int j = 0; j < seqs.length; j++) {
  757                       if (!isDroppable(seqs[j]))
  758                           continue;
  759                       dbSeq = db.findSequence(seqs[j]);
  760                       if (dbSeq != null) {
  761                           if (dropSequence(seqs[j]))
  762                               dbSeq.getSchema().removeSequence(dbSeq);
  763                           else
  764                               _log.warn(_loc.get("drop-seq", seqs[j]));
  765                       }
  766                   }
  767               }
  768           }
  769   
  770           // calculate tables to drop; we can only drop tables if we're sure
  771           // the user listed the entire table definition in the stuff they want
  772           // dropped; else they might just want to drop a few columns
  773           Collection drops = new LinkedList();
  774           Table[] tabs;
  775           Table dbTable;
  776           Column[] dbCols;
  777           for (int i = 0; i < schemas.length; i++) {
  778               tabs = schemas[i].getTables();
  779               tables:
  780               for (int j = 0; j < tabs.length; j++) {
  781                   if (!isDroppable(tabs[j]))
  782                       continue;
  783                   dbTable = db.findTable(tabs[j]);
  784                   if (dbTable == null)
  785                       continue;
  786   
  787                   dbCols = dbTable.getColumns();
  788                   for (int k = 0; k < dbCols.length; k++)
  789                       if (tabs[j].getColumn(dbCols[k].getName()) == null)
  790                           continue tables;
  791   
  792                   drops.add(tabs[j]);
  793               }
  794           }
  795   
  796           // order is important in this method; start with foreign keys mentioned
  797           // in the drop schema
  798           if (_fks) {
  799               ForeignKey[] fks;
  800               ForeignKey fk;
  801               for (int i = 0; i < schemas.length; i++) {
  802                   tabs = schemas[i].getTables();
  803                   for (int j = 0; j < tabs.length; j++) {
  804                       if (!isDroppable(tabs[j]))
  805                           continue;
  806                       fks = tabs[j].getForeignKeys();
  807                       dbTable = db.findTable(tabs[j]);
  808                       for (int k = 0; k < fks.length; k++) {
  809                           if (fks[k].isLogical())
  810                               continue;
  811   
  812                           fk = null;
  813                           if (dbTable != null)
  814                               fk = findForeignKey(dbTable, fks[k]);
  815                           if (dbTable == null || fk == null)
  816                               continue;
  817   
  818                           if (dropForeignKey(fks[k]))
  819                               if (dbTable != null)
  820                                   dbTable.removeForeignKey(fk);
  821                               else
  822                                   _log.warn(_loc.get("drop-fk", fks[k], tabs[j]));
  823                       }
  824                   }
  825               }
  826   
  827               // also drop imported foreign keys for tables that will be dropped
  828               Table tab;
  829               for (Iterator itr = drops.iterator(); itr.hasNext();) {
  830                   tab = (Table) itr.next();
  831                   dbTable = db.findTable(tab);
  832                   if (dbTable == null)
  833                       continue;
  834   
  835                   fks = db.findExportedForeignKeys(dbTable.getPrimaryKey());
  836                   for (int i = 0; i < fks.length; i++) {
  837                       if (dropForeignKey(fks[i]))
  838                           dbTable.removeForeignKey(fks[i]);
  839                       else
  840                           _log.warn(_loc.get("drop-fk", fks[i], dbTable));
  841                   }
  842               }
  843           }
  844   
  845           // drop the tables we calculated above
  846           dropTables(drops, db);
  847   
  848           // columns
  849           Column[] cols;
  850           Column col;
  851           for (int i = 0; i < schemas.length; i++) {
  852               tabs = schemas[i].getTables();
  853               for (int j = 0; j < tabs.length; j++) {
  854                   if (!isDroppable(tabs[j]))
  855                       continue;
  856                   cols = tabs[j].getColumns();
  857                   dbTable = db.findTable(tabs[j]);
  858                   for (int k = 0; k < cols.length; k++) {
  859                       col = null;
  860                       if (dbTable != null)
  861                           col = dbTable.getColumn(cols[k].getName());
  862                       if (dbTable == null || col == null)
  863                           continue;
  864   
  865                       if (dropColumn(cols[k])) {
  866                           if (dbTable != null)
  867                               dbTable.removeColumn(col);
  868                           else
  869                               _log.warn(_loc.get("drop-col", cols[k], tabs[j]));
  870                       }
  871                   }
  872               }
  873           }
  874       }
  875   
  876       /**
  877        * Return true if the table is droppable.
  878        */
  879       private boolean isDroppable(Table table) {
  880           return _openjpaTables
  881               || (!table.getName().toUpperCase().startsWith("OPENJPA_")
  882               && !table.getName().toUpperCase().startsWith("JDO_")); // legacy
  883       }
  884   
  885       /**
  886        * Return true if the sequence is droppable.
  887        */
  888       private boolean isDroppable(Sequence seq) {
  889           return _openjpaTables
  890               || (!seq.getName().toUpperCase().startsWith("OPENJPA_")
  891               && !seq.getName().toUpperCase().startsWith("JDO_")); // legacy
  892       }
  893   
  894       /**
  895        * Find an index in the given table that matches the given one.
  896        */
  897       private Index findIndex(Table dbTable, Index idx) {
  898           Index[] idxs = dbTable.getIndexes();
  899           for (int i = 0; i < idxs.length; i++)
  900               if (idx.columnsMatch(idxs[i].getColumns()))
  901                   return idxs[i];
  902           return null;
  903       }
  904   
  905       /**
  906        * Find a foreign key in the given table that matches the given one.
  907        */
  908       private ForeignKey findForeignKey(Table dbTable, ForeignKey fk) {
  909           if (fk.getConstantColumns().length > 0
  910               || fk.getConstantPrimaryKeyColumns().length > 0)
  911               return null;
  912           ForeignKey[] fks = dbTable.getForeignKeys();
  913           for (int i = 0; i < fks.length; i++)
  914               if (fk.columnsMatch(fks[i].getColumns(),
  915                   fks[i].getPrimaryKeyColumns()))
  916                   return fks[i];
  917           return null;
  918       }
  919   
  920       /**
  921        * Remove the given collection of tables from the database schema. Orders
  922        * the removals according to foreign key constraints on the tables.
  923        */
  924       private void dropTables(Collection tables, SchemaGroup change)
  925           throws SQLException {
  926           if (tables.isEmpty())
  927               return;
  928   
  929           Table table;
  930           Table changeTable;
  931           for (Iterator itr = tables.iterator(); itr.hasNext();) {
  932               table = (Table) itr.next();
  933               if (dropTable(table)) {
  934                   changeTable = change.findTable(table);
  935                   if (changeTable != null)
  936                       changeTable.getSchema().removeTable(changeTable);
  937               } else
  938                   _log.warn(_loc.get("drop-table", table));
  939           }
  940       }
  941   
  942       /**
  943        * Add the given table to the database schema.
  944        *
  945        * @return true if the operation was successful, false otherwise
  946        */
  947       public boolean createTable(Table table)
  948           throws SQLException {
  949           return executeSQL(_dict.getCreateTableSQL(table));
  950       }
  951   
  952       /**
  953        * Drop the given table from the database schema.
  954        *
  955        * @return true if the operation was successful, false otherwise
  956        */
  957       public boolean dropTable(Table table)
  958           throws SQLException {
  959           return executeSQL(_dict.getDropTableSQL(table));
  960       }
  961   
  962       /**
  963        * Add the given sequence to the database schema.
  964        *
  965        * @return true if the operation was successful, false otherwise
  966        */
  967       public boolean createSequence(Sequence seq)
  968           throws SQLException {
  969           return executeSQL(_dict.getCreateSequenceSQL(seq));
  970       }
  971   
  972       /**
  973        * Drop the given sequence from the database schema.
  974        *
  975        * @return true if the operation was successful, false otherwise
  976        */
  977       public boolean dropSequence(Sequence seq)
  978           throws SQLException {
  979           return executeSQL(_dict.getDropSequenceSQL(seq));
  980       }
  981   
  982       /**
  983        * Add the given index to the database schema.
  984        *
  985        * @return true if the operation was successful, false otherwise
  986        */
  987       public boolean createIndex(Index idx, Table table)
  988           throws SQLException {
  989           int max = _dict.maxIndexesPerTable;
  990   
  991           int len = table.getIndexes().length;
  992           if (table.getPrimaryKey() != null)
  993               len += table.getPrimaryKey().getColumns().length;
  994   
  995           if (len >= max) {
  996               _log.warn(_loc.get("too-many-indexes", idx, table, max + ""));
  997               return false;
  998           }
  999   
 1000           return executeSQL(_dict.getCreateIndexSQL(idx));
 1001       }
 1002   
 1003       /**
 1004        * Drop the given index from the database schema.
 1005        *
 1006        * @return true if the operation was successful, false otherwise
 1007        */
 1008       public boolean dropIndex(Index idx)
 1009           throws SQLException {
 1010           return executeSQL(_dict.getDropIndexSQL(idx));
 1011       }
 1012   
 1013       /**
 1014        * Add the given column to the database schema.
 1015        *
 1016        * @return true if the operation was successful, false otherwise
 1017        */
 1018       public boolean addColumn(Column col)
 1019           throws SQLException {
 1020           return executeSQL(_dict.getAddColumnSQL(col));
 1021       }
 1022   
 1023       /**
 1024        * Drop the given column from the database schema.
 1025        *
 1026        * @return true if the operation was successful, false otherwise
 1027        */
 1028       public boolean dropColumn(Column col)
 1029           throws SQLException {
 1030           return executeSQL(_dict.getDropColumnSQL(col));
 1031       }
 1032   
 1033       /**
 1034        * Add the given primary key to the database schema.
 1035        *
 1036        * @return true if the operation was successful, false otherwise
 1037        */
 1038       public boolean addPrimaryKey(PrimaryKey pk)
 1039           throws SQLException {
 1040           return executeSQL(_dict.getAddPrimaryKeySQL(pk));
 1041       }
 1042   
 1043       /**
 1044        * Drop the given primary key from the database schema.
 1045        *
 1046        * @return true if the operation was successful, false otherwise
 1047        */
 1048       public boolean dropPrimaryKey(PrimaryKey pk)
 1049           throws SQLException {
 1050           return executeSQL(_dict.getDropPrimaryKeySQL(pk));
 1051       }
 1052   
 1053       /**
 1054        * Add the given foreign key to the database schema.
 1055        *
 1056        * @return true if the operation was successful, false otherwise
 1057        */
 1058       public boolean addForeignKey(ForeignKey fk)
 1059           throws SQLException {
 1060           return executeSQL(_dict.getAddForeignKeySQL(fk));
 1061       }
 1062   
 1063       /**
 1064        * Drop the given foreign key from the database schema.
 1065        *
 1066        * @return true if the operation was successful, false otherwise
 1067        */
 1068       public boolean dropForeignKey(ForeignKey fk)
 1069           throws SQLException {
 1070           return executeSQL(_dict.getDropForeignKeySQL(fk));
 1071       }
 1072   
 1073       /**
 1074        * Return the database schema.
 1075        */
 1076       public SchemaGroup getDBSchemaGroup() {
 1077           try {
 1078               return getDBSchemaGroup(true);
 1079           } catch (SQLException se) {
 1080               throw SQLExceptions.getStore(se, _dict);
 1081           }
 1082       }
 1083   
 1084       /**
 1085        * Set the database schema.
 1086        */
 1087       public void setDBSchemaGroup(SchemaGroup db) {
 1088           _db = db;
 1089           if (db != null)
 1090               _fullDB = true;
 1091       }
 1092   
 1093       /**
 1094        * Return the database schema.
 1095        *
 1096        * @param full if false, only the tables named in the set schema
 1097        * repository will be generated
 1098        */
 1099       private SchemaGroup getDBSchemaGroup(boolean full)
 1100           throws SQLException {
 1101           if (_db == null || (full && !_fullDB)) {
 1102               SchemaGenerator gen = new SchemaGenerator(_conf);
 1103               gen.setPrimaryKeys(_pks);
 1104               gen.setForeignKeys(_fks);
 1105               gen.setIndexes(_indexes);
 1106               if (full)
 1107                   gen.generateSchemas();
 1108               else {
 1109                   // generate only the tables in the given repository
 1110                   // group; some may not exist yet, which is OK; we just need
 1111                   // to make sure we can detect the changes to the ones that
 1112                   // do exist
 1113                   Collection tables = new LinkedList();
 1114                   SchemaGroup group = assertSchemaGroup();
 1115                   Schema[] schemas = group.getSchemas();
 1116                   Table[] tabs;
 1117                   for (int i = 0; i < schemas.length; i++) {
 1118                       tabs = schemas[i].getTables();
 1119                       for (int j = 0; j < tabs.length; j++) {
 1120                           if (tabs[j].getSchemaName() == null)
 1121                               tables.add("." + tabs[j].getName());
 1122                           else
 1123                               tables.add(tabs[j].getFullName());
 1124                       }
 1125                   }
 1126                   if (!tables.isEmpty())
 1127                       gen.generateSchemas((String[]) tables.toArray
 1128                           (new String[tables.size()]));
 1129               }
 1130               _db = gen.getSchemaGroup();
 1131           }
 1132           return _db;
 1133       }
 1134   
 1135       private SchemaGroup assertSchemaGroup() {
 1136           SchemaGroup local = getSchemaGroup();
 1137           if (local == null)
 1138               throw new InvalidStateException(_loc.get("tool-norepos"));
 1139           return local;
 1140       }
 1141   
 1142       /////////////
 1143       // Utilities
 1144       /////////////
 1145   
 1146       /**
 1147        * Executes the given array of non-selecting SQL statements, correctly
 1148        * logging the SQL calls and optionally ignoring errors.
 1149        *
 1150        * @return true if there was SQL to execute and the calls were
 1151        * successful, false otherwise
 1152        */
 1153       private boolean executeSQL(String[] sql)
 1154           throws SQLException {
 1155           // if no sql, probably b/c dictionary doesn't support operation
 1156           if (sql.length == 0)
 1157               return false;
 1158   
 1159           boolean err = false;
 1160           if (_writer == null) {
 1161               // this is outside the try-catch because a failure here is
 1162               // really bad, and should not be ignored.
 1163               Connection conn = _ds.getConnection();
 1164               Statement statement = null;
 1165               boolean wasAuto = true;
 1166               try {
 1167                   wasAuto = conn.getAutoCommit();
 1168                   if (!wasAuto)
 1169                       conn.setAutoCommit(true);
 1170                   for (int i = 0; i < sql.length; i++) {
 1171                       try {
 1172                           // some connections require that rollback be
 1173                           // called on the connection before any DDL statements
 1174                           // can be run on it, even when autocommit is on.
 1175                           // This is sometimes because the connection does not
 1176                           // allow DDL statements when there are multiple
 1177                           // commands issued on the connection, and the
 1178                           // connection pool may have issued some validation SQL.
 1179                           try {
 1180                               conn.rollback();
 1181                           } catch (Exception e) {
 1182                           }
 1183   
 1184                           statement = conn.createStatement();
 1185                           statement.executeUpdate(sql[i]);
 1186   
 1187                           // some connections seem to require an explicit
 1188                           // commit for DDL statements, even when autocommit
 1189                           // is on. The DataDirect drivers seem to suffer from
 1190                           // this limitation.
 1191                           try {
 1192                               conn.commit();
 1193                           } catch (Exception e) {
 1194                           }
 1195                       }
 1196                       catch (SQLException se) {
 1197                           err = true;
 1198                           handleException(se);
 1199                       } finally {
 1200                           if (statement != null)
 1201                               try {
 1202                                   statement.close();
 1203                               } catch (SQLException se) {
 1204                               }
 1205                       }
 1206                   }
 1207               }
 1208               finally {
 1209                   if (!wasAuto)
 1210                       conn.setAutoCommit(false);
 1211                   try {
 1212                       conn.close();
 1213                   } catch (SQLException se) {
 1214                   }
 1215               }
 1216           } else {
 1217               for (int i = 0; i < sql.length; i++)
 1218                   _writer.println(sql[i] + ";");
 1219               _writer.flush();
 1220           }
 1221   
 1222           return !err;
 1223       }
 1224   
 1225       /**
 1226        * Handle the given exception, logging it and optionally ignoring it,
 1227        * depending on the flags this SchemaTool was created with.
 1228        */
 1229       private void handleException(SQLException sql)
 1230           throws SQLException {
 1231           if (!_ignoreErrs)
 1232               throw sql;
 1233           _log.warn(sql.getMessage(), sql);
 1234       }
 1235   
 1236       ////////
 1237       // Main
 1238       ////////
 1239   
 1240       /**
 1241        * Usage: java org.apache.openjpa.jdbc.schema.SchemaTool [option]*
 1242        * [-action/-a &lt;add | retain | drop | refresh | createDB | dropDB
 1243        * | build | reflect | import | export&gt;]
 1244        * &lt;.schema file or resource&gt;*
 1245        *  Where the following options are recognized.
 1246        * <ul>
 1247        * <li><i>-properties/-p &lt;properties file or resource&gt;</i>: The
 1248        * path or resource name of a OpenJPA properties file containing
 1249        * information such as the license key	and connection data as
 1250        * outlined in {@link JDBCConfiguration}. Optional.</li>
 1251        * <li><i>-&lt;property name&gt; &lt;property value&gt;</i>: All bean
 1252        * properties of the OpenJPA {@link JDBCConfiguration} can be set by
 1253        * using their	names and supplying a value. For example:
 1254        * <code>-licenseKey adslfja83r3lkadf</code></li>
 1255        * <li><i>-ignoreErrors/-i &lt;true/t | false/f&gt;</i>: If false, an
 1256        * exception will will be thrown if the tool encounters any database
 1257        * exceptions; defaults to <code>false</code>.</li>
 1258        * <li><i>-file/-f &lt;stdout | output file or resource&gt;</i>: Use this
 1259        * option to write a SQL script for the planned schema modifications,
 1260        * rather than committing them to the database. This option also
 1261        * applies to the <code>export</code> and <code>reflect</code> actions.</li>
 1262        * <li><i>-openjpaTables/-kt &lt;true/t | false/f&gt;</i>: Under the
 1263        * <code>reflect</code> action, whether to reflect on tables with
 1264        * the name <code>OPENJPA_*</code>. Under other actions, whether to
 1265        * drop such tables. Defaults to <code>false</code>.</li>
 1266        * <li><i>-dropTables/-dt &lt;true/t | false/f&gt;</i>: Set this option to
 1267        * true to drop tables that appear to be unused during
 1268        * <code>retain</code>	and <code>refresh</code> actions. Defaults to
 1269        * <code>true</code>.</li>
 1270        * <li><i>-dropSequences/-dsq &lt;true/t | false/f&gt;</i>: Set this option
 1271        * to true to drop sequences that appear to be unused during
 1272        * <code>retain</code>	and <code>refresh</code> actions. Defaults to
 1273        * <code>true</code>.</li>
 1274        * <li><i>-primaryKeys/-pk &lt;true/t | false/f&gt;</i>: Whether primary
 1275        * keys on existing tables are manipulated. Defaults to true.</li>
 1276        * <li><i>-foreignKeys/-fk &lt;true/t | false/f&gt;</i>: Whether foreign
 1277        * keys on existing tables are manipulated. Defaults to true.</li>
 1278        * <li><i>-indexes/-ix &lt;true/t | false/f&gt;</i>: Whether indexes
 1279        * on existing tables are manipulated. Defaults to true.</li>
 1280        * <li><i>-sequences/-sq &lt;true/t | false/f&gt;</i>: Whether to
 1281        * manipulate sequences. Defaults to true.</li>
 1282        * <li><i>-record/-r &lt;true/t | false/f&gt;</i>: Set this option to
 1283        * <code>false</code> to prevent writing the schema changes to the
 1284        * current {@link SchemaFactory}.</li>
 1285        * </ul>
 1286        *  Actions can be composed in a comma-separated list. The various actions 
 1287        *  are as follows.
 1288        * <ul>
 1289        * <li><i>add</i>: Bring the schema up-to-date with the latest
 1290        * changes to the schema XML data by adding tables, columns,
 1291        * indexes, etc. This action never drops any data. This is the
 1292        * default action.</li>
 1293        * <li><i>retain</i>: Keep all schema components in the schema XML, but
 1294        * drop the rest from the database. This action never adds any data.</li>
 1295        * <li><i>drop</i>: Drop all the schema components in the schema XML.</li>
 1296        * <li><i>refresh</i>: Equivalent to retain, then add.</li>
 1297        * <li><i>createDB</i>: Execute SQL to re-create the current database.
 1298        * This action is typically used in conjuction with the
 1299        * <code>file</code> option.</li>
 1300        * <li><i>build</i>: Execute SQL to build the schema defined in the XML.
 1301        * Because it doesn't take the current database schema into account,
 1302        * this action is typically used in conjuction with the
 1303        * <code>file</code> option.</li>
 1304        * <li><i>reflect</i>: Reflect on the current database schema. Write the
 1305        * schema's XML representation to the file specified with the
 1306        * <code>file</code> option, or to stdout if no file is given.</li>
 1307        * <li><i>dropDB</i>: Execute SQL to drop the current database. This
 1308        * action implies <code>dropTables</code>.</li>
 1309        * <li><i>deleteTableContents</i>: Execute SQL to delete all rows from 
 1310        * all tables that OpenJPA knows about.</li>
 1311        * <li><i>import</i>: Import the given XML schema definition into the
 1312        * current {@link SchemaFactory}.</li>
 1313        * <li><i>export</i>: Export the current {@link SchemaFactory}'s recorded
 1314        * schema to an XML schema definition file.</li>
 1315        * </ul>
 1316        *  Examples:
 1317        * <ul>
 1318        * <li>Write a script to stdout to re-create the current database
 1319        * schema:<br />
 1320        * <code>java org.apache.openjpa.jdbc.schema.SchemaTool -f stdout 
 1321        * -a createDB</code></li>
 1322        * <li>Drop the current database schema:<br />
 1323        * <code>java org.apache.openjpa.jdbc.schema.SchemaTool 
 1324        * -a dropDB</code></li>
 1325        * <li>Refresh the schema and delete all records in all tables:<br />
 1326        * <code>java org.apache.openjpa.jdbc.schema.SchemaTool 
 1327        * -a refresh,deleteTableContents</code></li>
 1328        * <li>Create a schema based on an XML schema definition file:<br />
 1329        * <code>java org.apache.openjpa.jdbc.schema.SchemaTool 
 1330        * myschema.xml</code></li>
 1331        * </ul>
 1332        */
 1333       public static void main(String[] args)
 1334           throws IOException, SQLException {
 1335           Options opts = new Options();
 1336           final String[] arguments = opts.setFromCmdLine(args);
 1337           boolean ret = Configurations.runAgainstAllAnchors(opts,
 1338               new Configurations.Runnable() {
 1339               public boolean run(Options opts) throws Exception {
 1340                   JDBCConfiguration conf = new JDBCConfigurationImpl();
 1341                   try {
 1342                       return SchemaTool.run(conf, arguments, opts);
 1343                   } finally {
 1344                       conf.close();
 1345                   }
 1346               }
 1347           });
 1348           if (!ret)
 1349               System.out.println(_loc.get("tool-usage"));
 1350       }
 1351   
 1352       /**
 1353        * Run the tool. Returns false if any invalid options were given.
 1354        *
 1355        * @see #main
 1356        */
 1357       public static boolean run(JDBCConfiguration conf, String[] args,
 1358           Options opts)
 1359           throws IOException, SQLException {
 1360           Flags flags = new Flags();
 1361           flags.dropTables = opts.removeBooleanProperty
 1362               ("dropTables", "dt", flags.dropTables);
 1363           flags.dropSequences = opts.removeBooleanProperty
 1364               ("dropSequences", "dsq", flags.dropSequences);
 1365           flags.ignoreErrors = opts.removeBooleanProperty
 1366               ("ignoreErrors", "i", flags.ignoreErrors);
 1367           flags.openjpaTables = opts.removeBooleanProperty
 1368               ("openjpaTables", "ot", flags.openjpaTables);
 1369           flags.primaryKeys = opts.removeBooleanProperty
 1370               ("primaryKeys", "pk", flags.primaryKeys);
 1371           flags.foreignKeys = opts.removeBooleanProperty
 1372               ("foreignKeys", "fks", flags.foreignKeys);
 1373           flags.indexes = opts.removeBooleanProperty
 1374               ("indexes", "ix", flags.indexes);
 1375           flags.sequences = opts.removeBooleanProperty
 1376               ("sequences", "sq", flags.sequences);
 1377           flags.record = opts.removeBooleanProperty("record", "r", flags.record);
 1378           String fileName = opts.removeProperty("file", "f", null);
 1379           String schemas = opts.removeProperty("s");
 1380           if (schemas != null)
 1381               opts.setProperty("schemas", schemas);
 1382   
 1383           String[] actions = opts.removeProperty("action", "a", flags.action)
 1384               .split(",");
 1385           
 1386           // setup a configuration instance with cmd-line info
 1387           Configurations.populateConfiguration(conf, opts);
 1388   
 1389           // create script writer
 1390           ClassLoader loader = conf.getClassResolverInstance().
 1391               getClassLoader(SchemaTool.class, null);
 1392           flags.writer = Files.getWriter(fileName, loader);
 1393   
 1394           boolean returnValue = true;
 1395           for (int i = 0; i < actions.length; i++) {
 1396               flags.action = actions[i];
 1397               returnValue &= run(conf, args, flags, loader);
 1398           }
 1399           
 1400           return returnValue;
 1401       }
 1402   
 1403       /**
 1404        * Run the tool. Return false if invalid options were given.
 1405        */
 1406       public static boolean run(JDBCConfiguration conf, String[] args,
 1407           Flags flags, ClassLoader loader)
 1408           throws IOException, SQLException {
 1409           Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
 1410           if (ACTION_REFLECT.equals(flags.action)) {
 1411               if (args.length > 0)
 1412                   return false;
 1413               if (flags.writer == null)
 1414                   flags.writer = new PrintWriter(System.out);
 1415   
 1416               SchemaGenerator gen = new SchemaGenerator(conf);
 1417               gen.setPrimaryKeys(flags.primaryKeys);
 1418               gen.setIndexes(flags.indexes);
 1419               gen.setForeignKeys(flags.foreignKeys);
 1420               gen.setSequences(flags.sequences);
 1421               gen.setOpenJPATables(flags.openjpaTables);
 1422   
 1423               String schemas = conf.getSchemas();
 1424               if (StringUtils.isEmpty(schemas))
 1425                   schemas = "all";
 1426               log.info(_loc.get("sch-reflect", schemas));
 1427               gen.generateSchemas();
 1428   
 1429               // record the schema
 1430               log.info(_loc.get("sch-reflect-write"));
 1431               SchemaSerializer ser = new XMLSchemaSerializer(conf);
 1432               ser.addAll(gen.getSchemaGroup());
 1433               ser.serialize(flags.writer, ser.PRETTY);
 1434               return true;
 1435           }
 1436   
 1437           if (args.length == 0
 1438               && !ACTION_CREATEDB.equals(flags.action)
 1439               && !ACTION_DROPDB.equals(flags.action)
 1440               && !ACTION_EXPORT.equals(flags.action)
 1441               && !ACTION_DELETE_TABLE_CONTENTS.equals(flags.action))
 1442               return false;
 1443   
 1444           // parse in the arguments
 1445           SchemaParser parser = new XMLSchemaParser(conf);
 1446           parser.setDelayConstraintResolve(true);
 1447           File file;
 1448           for (int i = 0; i < args.length; i++) {
 1449               file = Files.getFile(args[i], loader);
 1450               log.info(_loc.get("tool-running", file));
 1451               parser.parse(file);
 1452           }
 1453           parser.resolveConstraints();
 1454   
 1455           if (ACTION_IMPORT.equals(flags.action)) {
 1456               log.info(_loc.get("tool-import-store"));
 1457               SchemaGroup schema = parser.getSchemaGroup();
 1458               conf.getSchemaFactoryInstance().storeSchema(schema);
 1459               return true;
 1460           }
 1461           if (ACTION_EXPORT.equals(flags.action)) {
 1462               if (flags.writer == null)
 1463                   flags.writer = new PrintWriter(System.out);
 1464   
 1465               log.info(_loc.get("tool-export-gen"));
 1466               SchemaGroup schema = conf.getSchemaFactoryInstance().readSchema();
 1467   
 1468               log.info(_loc.get("tool-export-write"));
 1469               SchemaSerializer ser = new XMLSchemaSerializer(conf);
 1470               ser.addAll(schema);
 1471               ser.serialize(flags.writer, ser.PRETTY);
 1472               return true;
 1473           }
 1474   
 1475           SchemaTool tool = new SchemaTool(conf, flags.action);
 1476           tool.setIgnoreErrors(flags.ignoreErrors);
 1477           tool.setDropTables(flags.dropTables);
 1478           tool.setSequences(flags.sequences); // set before dropseqs
 1479           tool.setDropSequences(flags.dropSequences);
 1480           tool.setPrimaryKeys(flags.primaryKeys);
 1481           tool.setForeignKeys(flags.foreignKeys);
 1482           tool.setIndexes(flags.indexes);
 1483           tool.setOpenJPATables(flags.openjpaTables);
 1484           if (args.length > 0)
 1485               tool.setSchemaGroup(parser.getSchemaGroup());
 1486           if (flags.writer != null)
 1487               tool.setWriter(flags.writer);
 1488   
 1489           log.info(_loc.get("tool-action", flags.action));
 1490           try {
 1491               tool.run();
 1492           } finally {
 1493               if (flags.record) {
 1494                   log.info(_loc.get("tool-record"));
 1495                   tool.record();
 1496               }
 1497           }
 1498           if (flags.writer != null)
 1499               flags.writer.flush();
 1500   
 1501           return true;
 1502       }
 1503   
 1504       /**
 1505        * Run flags.
 1506        */
 1507       public static class Flags {
 1508   
 1509           public String action = ACTION_ADD;
 1510           public Writer writer = null;
 1511           public boolean dropTables = true;
 1512           public boolean dropSequences = true;
 1513           public boolean ignoreErrors = false;
 1514           public boolean openjpaTables = false;
 1515           public boolean primaryKeys = true;
 1516           public boolean foreignKeys = true;
 1517           public boolean indexes = true;
 1518           public boolean sequences = true;
 1519           public boolean record = true;
 1520       }
 1521   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » schema » [javadoc | source]