Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » meta » [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.meta;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.io.PrintWriter;
   24   import java.io.Writer;
   25   import java.security.AccessController;
   26   import java.security.PrivilegedActionException;
   27   import java.sql.SQLException;
   28   import java.util.Arrays;
   29   import java.util.Collection;
   30   import java.util.HashMap;
   31   import java.util.HashSet;
   32   import java.util.Iterator;
   33   import java.util.Map;
   34   import java.util.Set;
   35   
   36   import org.apache.openjpa.conf.OpenJPAConfiguration;
   37   import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
   38   import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
   39   import org.apache.openjpa.jdbc.kernel.JDBCSeq;
   40   import org.apache.openjpa.jdbc.schema.Column;
   41   import org.apache.openjpa.jdbc.schema.DynamicSchemaFactory;
   42   import org.apache.openjpa.jdbc.schema.LazySchemaFactory;
   43   import org.apache.openjpa.jdbc.schema.Schema;
   44   import org.apache.openjpa.jdbc.schema.SchemaGenerator;
   45   import org.apache.openjpa.jdbc.schema.SchemaGroup;
   46   import org.apache.openjpa.jdbc.schema.SchemaSerializer;
   47   import org.apache.openjpa.jdbc.schema.SchemaTool;
   48   import org.apache.openjpa.jdbc.schema.Table;
   49   import org.apache.openjpa.jdbc.schema.XMLSchemaSerializer;
   50   import org.apache.openjpa.jdbc.sql.DBDictionary;
   51   import org.apache.openjpa.kernel.Seq;
   52   import org.apache.openjpa.lib.conf.Configurations;
   53   import org.apache.openjpa.lib.log.Log;
   54   import org.apache.openjpa.lib.meta.ClassArgParser;
   55   import org.apache.openjpa.lib.util.Files;
   56   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   57   import org.apache.openjpa.lib.util.Localizer;
   58   import org.apache.openjpa.lib.util.Options;
   59   import org.apache.openjpa.lib.util.Services;
   60   import org.apache.openjpa.meta.ClassMetaData;
   61   import org.apache.openjpa.meta.FieldMetaData;
   62   import org.apache.openjpa.meta.JavaTypes;
   63   import org.apache.openjpa.meta.MetaDataFactory;
   64   import org.apache.openjpa.meta.MetaDataModes;
   65   import org.apache.openjpa.meta.QueryMetaData;
   66   import org.apache.openjpa.meta.SequenceMetaData;
   67   import org.apache.openjpa.meta.ValueStrategies;
   68   import org.apache.openjpa.util.GeneralException;
   69   import org.apache.openjpa.util.InternalException;
   70   import org.apache.openjpa.util.MetaDataException;
   71   
   72   /**
   73    * Tool for manipulating class mappings and associated schema.
   74    *
   75    * @author Abe White
   76    */
   77   public class MappingTool
   78       implements MetaDataModes {
   79   
   80       public static final String SCHEMA_ACTION_NONE = "none";
   81   
   82       public static final String ACTION_ADD = "add";
   83       public static final String ACTION_REFRESH = "refresh";
   84       public static final String ACTION_BUILD_SCHEMA = "buildSchema";
   85       public static final String ACTION_DROP = "drop";
   86       public static final String ACTION_VALIDATE = "validate";
   87       public static final String ACTION_EXPORT = "export";
   88       public static final String ACTION_IMPORT = "import";
   89   
   90       public static final String[] ACTIONS = new String[]{
   91           ACTION_ADD,
   92           ACTION_REFRESH,
   93           ACTION_BUILD_SCHEMA,
   94           ACTION_DROP,
   95           ACTION_VALIDATE,
   96           ACTION_EXPORT,
   97           ACTION_IMPORT,
   98       };
   99   
  100       private static final Localizer _loc =
  101           Localizer.forPackage(MappingTool.class);
  102   
  103       private final JDBCConfiguration _conf;
  104       private final Log _log;
  105       private final String _action;
  106       private final boolean _meta;
  107       private final int _mode;
  108       private final DBDictionary _dict;
  109   
  110       private MappingRepository _repos = null;
  111       private SchemaGroup _schema = null;
  112       private SchemaTool _schemaTool = null;
  113       private String _schemaActions = SchemaTool.ACTION_ADD;
  114       private boolean _readSchema = false;
  115       private boolean _pks = false;
  116       private boolean _fks = false;
  117       private boolean _indexes = false;
  118       private boolean _seqs = true;
  119       private boolean _dropUnused = true;
  120       private boolean _ignoreErrors = false;
  121       private File _file = null;
  122       private Writer _mappingWriter = null;
  123       private Writer _schemaWriter = null;
  124   
  125       // buffer metadatas to be dropped
  126       private Set _dropCls = null;
  127       private Set _dropMap = null;
  128       private boolean _flush = false;
  129       private boolean _flushSchema = false;
  130   
  131       /**
  132        * Constructor. Supply configuration and action.
  133        */
  134       public MappingTool(JDBCConfiguration conf, String action, boolean meta) {
  135           _conf = conf;
  136           _log = conf.getLog(JDBCConfiguration.LOG_METADATA);
  137           _meta = meta;
  138   
  139           if (action == null)
  140               _action = ACTION_REFRESH;
  141           else if (!Arrays.asList(ACTIONS).contains(action))
  142               throw new IllegalArgumentException("action == " + action);
  143           else
  144               _action = action;
  145   
  146           if (meta && ACTION_ADD.equals(_action))
  147               _mode = MODE_META;
  148           else if (meta && ACTION_DROP.equals(_action))
  149               _mode = MODE_META | MODE_MAPPING | MODE_QUERY;
  150           else
  151               _mode = MODE_MAPPING;
  152   
  153           _dict = _conf.getDBDictionaryInstance();
  154       }
  155   
  156       /**
  157        * The action supplied on construction.
  158        */
  159       public String getAction() {
  160           return _action;
  161       }
  162   
  163       /**
  164        * Whether the action works on metadata as well as mappings.
  165        */
  166       public boolean isMetaDataAction() {
  167           return _meta;
  168       }
  169   
  170       /**
  171        * The schema modification policy, or <code>none</code>. See the
  172        * ACTION constants in {@link SchemaTool}. May be a comma-separated
  173        * list of values. Defaults to {@link SchemaTool#ACTION_ADD}.
  174        */
  175       public String getSchemaAction() {
  176           return _schemaActions;
  177       }
  178   
  179       /**
  180        * The schema modification policy, or <code>none</code>. See the
  181        * ACTION constants in {@link SchemaTool}. May be a comma-separated
  182        * list of values. Defaults to {@link SchemaTool#ACTION_ADD}.
  183        */
  184       public void setSchemaAction(String schemaAction) {
  185           _schemaActions = schemaAction;
  186       }
  187   
  188       /**
  189        * Set to true to read the entire schema before mapping.
  190        * Leaving this option false saves time, but is dangerous when adding
  191        * new mappings, because without full knowledge of the existing schema the
  192        * mapping tool might create tables or indexes that conflict with
  193        * existing components.
  194        */
  195       public boolean getReadSchema() {
  196           return _readSchema;
  197       }
  198   
  199       /**
  200        * Set to true to read the entire schema before mapping.
  201        * Leaving this option false saves time, but is dangerous when adding
  202        * new mappings, because without full knowledge of the existing schema the
  203        * mapping tool might create tables or indexes that conflict with
  204        * existing components.
  205        */
  206       public void setReadSchema(boolean readSchema) {
  207           _readSchema = readSchema;
  208       }
  209   
  210       /**
  211        * Whether to manipulate sequences. Defaults to true.
  212        */
  213       public boolean getSequences() {
  214           return _seqs;
  215       }
  216   
  217       /**
  218        * Whether to manipulate sequences. Defaults to true.
  219        */
  220       public void setSequences(boolean seqs) {
  221           _seqs = seqs;
  222       }
  223   
  224       /**
  225        * Whether indexes on existing tables should be manipulated.
  226        * Defaults to false.
  227        */
  228       public boolean getIndexes() {
  229           return _indexes;
  230       }
  231   
  232       /**
  233        * Whether indexes on existing tables should be manipulated.
  234        * Defaults to false.
  235        */
  236       public void setIndexes(boolean indexes) {
  237           _indexes = indexes;
  238       }
  239   
  240       /**
  241        * Whether foreign keys on existing tables should be manipulated.
  242        * Defaults to false.
  243        */
  244       public boolean getForeignKeys() {
  245           return _fks;
  246       }
  247   
  248       /**
  249        * Whether foreign keys on existing tables should be manipulated.
  250        * Defaults to false.
  251        */
  252       public void setForeignKeys(boolean fks) {
  253           _fks = fks;
  254       }
  255   
  256       /**
  257        * Whether primary keys on existing tables should be manipulated.
  258        * Defaults to false.
  259        */
  260       public boolean getPrimaryKeys() {
  261           return _pks;
  262       }
  263   
  264       /**
  265        * Whether primary keys on existing tables should be manipulated.
  266        * Defaults to false.
  267        */
  268       public void setPrimaryKeys(boolean pks) {
  269           _pks = pks;
  270       }
  271   
  272       /**
  273        * Whether schema components that are unused by any mapping will be
  274        * dropped from this tool's {@link SchemaGroup}, and, depending on
  275        * the schema action, from the database. Defaults to true.
  276        */
  277       public boolean getDropUnusedComponents() {
  278           return _dropUnused;
  279       }
  280   
  281       /**
  282        * Whether schema components that are unused by any mapping will be
  283        * dropped from this tool's {@link SchemaGroup}, and, depending on
  284        * the schema action, from the database. Defaults to true.
  285        */
  286       public void setDropUnusedComponents(boolean dropUnused) {
  287           _dropUnused = dropUnused;
  288       }
  289   
  290       /**
  291        * Whether and SQL errors should cause a failure or just issue a warning.
  292        */
  293       public void setIgnoreErrors(boolean ignoreErrors) {
  294           _ignoreErrors = ignoreErrors;
  295       }
  296   
  297       /**
  298        * Whether and SQL errors should cause a failure or just issue a warning.
  299        */
  300       public boolean getIgnoreErrors() {
  301           return _ignoreErrors;
  302       }
  303   
  304       /**
  305        * Return the schema tool to use for schema modification.
  306        */
  307       private SchemaTool newSchemaTool(String action) {
  308           if (SCHEMA_ACTION_NONE.equals(action))
  309               action = null;
  310           SchemaTool tool = new SchemaTool(_conf, action);
  311           tool.setIgnoreErrors(getIgnoreErrors());
  312           tool.setPrimaryKeys(getPrimaryKeys());
  313           tool.setForeignKeys(getForeignKeys());
  314           tool.setIndexes(getIndexes());
  315           tool.setSequences(getSequences());
  316           return tool;
  317       }
  318   
  319       /**
  320        * Set the schema tool to use for schema modification.
  321        */
  322       public void setSchemaTool(SchemaTool tool) {
  323           _schemaTool = tool;
  324       }
  325   
  326       /**
  327        * The stream to export the planned schema to as an XML document.
  328        * If non-null, then the database schema will not be altered.
  329        */
  330       public Writer getSchemaWriter() {
  331           return _schemaWriter;
  332       }
  333   
  334       /**
  335        * The stream to export the planned schema to as an XML document.
  336        * If non-null, then the database schema will not be altered.
  337        */
  338       public void setSchemaWriter(Writer schemaWriter) {
  339           _schemaWriter = schemaWriter;
  340       }
  341   
  342       /**
  343        * The stream to export the planned mappings to as an XML document.
  344        * If non-null, then the mapping repository will not be altered.
  345        */
  346       public Writer getMappingWriter() {
  347           return _mappingWriter;
  348       }
  349   
  350       /**
  351        * The stream to export the planned mappings to as an XML document.
  352        * If non-null, then the mapping repository will not be altered.
  353        */
  354       public void setMappingWriter(Writer mappingWriter) {
  355           _mappingWriter = mappingWriter;
  356       }
  357   
  358       /**
  359        * If adding metadata, the metadata file to add to.
  360        */
  361       public File getMetaDataFile() {
  362           return _file;
  363       }
  364   
  365       /**
  366        * If adding metadata, the metadata file to add to.
  367        */
  368       public void setMetaDataFile(File file) {
  369           _file = file;
  370       }
  371   
  372       /**
  373        * Return the repository to use to access mapping information.
  374        * Defaults to a new {@link MappingRepository}.
  375        */
  376       public MappingRepository getRepository() {
  377           if (_repos == null) {
  378               _repos = _conf.newMappingRepositoryInstance();
  379               _repos.setSchemaGroup(getSchemaGroup());
  380               _repos.setValidate(_repos.VALIDATE_UNENHANCED, false);
  381           }
  382           return _repos;
  383       }
  384   
  385       /**
  386        * Set the repository to use to access mapping information.
  387        */
  388       public void setRepository(MappingRepository repos) {
  389           _repos = repos;
  390       }
  391   
  392       /**
  393        * Return the schema group to use in mapping. If none has been set, the
  394        * schema will be generated from the database.
  395        */
  396       public SchemaGroup getSchemaGroup() {
  397           if (_schema == null) {
  398               if (_action.indexOf(ACTION_BUILD_SCHEMA) != -1) {
  399                   DynamicSchemaFactory factory = new DynamicSchemaFactory();
  400                   factory.setConfiguration(_conf);
  401                   _schema = factory;
  402               } else if (_readSchema 
  403                   || contains(_schemaActions,SchemaTool.ACTION_RETAIN)
  404                   || contains(_schemaActions,SchemaTool.ACTION_REFRESH)) {
  405                   _schema = (SchemaGroup) newSchemaTool(null).getDBSchemaGroup().
  406                       clone();
  407               } else {
  408                   // with this we'll just read tables as different mappings
  409                   // look for them
  410                   LazySchemaFactory factory = new LazySchemaFactory();
  411                   factory.setConfiguration(_conf);
  412                   factory.setPrimaryKeys(getPrimaryKeys());
  413                   factory.setForeignKeys(getForeignKeys());
  414                   factory.setIndexes(getIndexes());
  415                   _schema = factory;
  416               }
  417   
  418               if (_schema.getSchemas().length == 0)
  419                   _schema.addSchema();
  420           }
  421           return _schema;
  422       }
  423   
  424       /**
  425        * Set the schema to use in mapping.
  426        */
  427       public void setSchemaGroup(SchemaGroup schema) {
  428           _schema = schema;
  429       }
  430   
  431       /**
  432        * Reset the internal repository. This is called automatically after
  433        * every {@link #record}.
  434        */
  435       public void clear() {
  436           _repos = null;
  437           _schema = null;
  438           _schemaTool = null;
  439           _flush = false;
  440           _flushSchema = false;
  441           if (_dropCls != null)
  442               _dropCls.clear();
  443           if (_dropMap != null)
  444               _dropMap.clear();
  445       }
  446   
  447       /**
  448        * Records the changes that have been made to both the mappings and the
  449        * associated schema, and clears the tool for further use. This also
  450        * involves clearing the internal mapping repository.
  451        */
  452       public void record() {
  453           record(null);
  454       }
  455       
  456       private void record(MappingTool.Flags flags) {
  457           MappingRepository repos = getRepository();
  458           MetaDataFactory io = repos.getMetaDataFactory();
  459           ClassMapping[] mappings;
  460           if (!ACTION_DROP.equals(_action))
  461               mappings = repos.getMappings();
  462           else if (_dropMap != null)
  463               mappings = (ClassMapping[]) _dropMap.toArray
  464                   (new ClassMapping[_dropMap.size()]);
  465           else
  466               mappings = new ClassMapping[0];
  467   
  468           try {
  469               if (_dropCls != null && !_dropCls.isEmpty()) {
  470                   Class[] cls = (Class[]) _dropCls.toArray
  471                       (new Class[_dropCls.size()]);
  472                   if (!io.drop(cls, _mode, null))
  473                       _log.warn(_loc.get("bad-drop", _dropCls));
  474               }
  475   
  476               if (_flushSchema) {
  477                   // drop portions of the known schema that no mapping uses, and
  478                   // add sequences used for value generation
  479                   if (_dropUnused)
  480                       dropUnusedSchemaComponents(mappings);
  481                   addSequenceComponents(mappings);
  482   
  483                   // now run the schematool as long as we're doing some schema
  484                   // action and the user doesn't just want an xml output
  485                   String[] schemaActions = _schemaActions.split(",");
  486                   for (int i = 0; i < schemaActions.length; i++) {
  487                       if (!SCHEMA_ACTION_NONE.equals(schemaActions[i])
  488                           && (_schemaWriter == null || (_schemaTool != null
  489                               && _schemaTool.getWriter() != null))) {
  490                           SchemaTool tool = newSchemaTool(schemaActions[i]);
  491   
  492                           // configure the tool with additional settings
  493                           if (flags != null) {
  494                               tool.setDropTables(flags.dropTables);
  495                               tool.setDropSequences(flags.dropSequences);
  496                               tool.setWriter(flags.sqlWriter);
  497                               tool.setOpenJPATables(flags.openjpaTables);
  498                           }
  499   
  500                           tool.setSchemaGroup(getSchemaGroup());
  501                           tool.run();
  502                           tool.record();
  503                       }
  504                   }
  505   
  506                   // xml output of schema?
  507                   if (_schemaWriter != null) {
  508                       // serialize the planned schema to the stream
  509                       SchemaSerializer ser = new XMLSchemaSerializer(_conf);
  510                       ser.addAll(getSchemaGroup());
  511                       ser.serialize(_schemaWriter, ser.PRETTY);
  512                       _schemaWriter.flush();
  513                   }
  514               }
  515               if (!_flush)
  516                   return;
  517   
  518               QueryMetaData[] queries = repos.getQueryMetaDatas();
  519               SequenceMetaData[] seqs = repos.getSequenceMetaDatas();
  520               Map output = null;
  521   
  522               // if we're outputting to stream, set all metas to same file so
  523               // they get placed in single string
  524               if (_mappingWriter != null) {
  525                   output = new HashMap();
  526                   File tmp = new File("openjpatmp");
  527                   for (int i = 0; i < mappings.length; i++)
  528                       mappings[i].setSource(tmp, mappings[i].SRC_OTHER);
  529                   for (int i = 0; i < queries.length; i++)
  530                       queries[i].setSource(tmp, queries[i].getSourceScope(),
  531                           queries[i].SRC_OTHER);
  532                   for (int i = 0; i < seqs.length; i++)
  533                       seqs[i].setSource(tmp, seqs[i].getSourceScope(),
  534                           seqs[i].SRC_OTHER);
  535               }
  536   
  537               // store
  538               if (!io.store(mappings, queries, seqs, _mode, output))
  539                   throw new MetaDataException(_loc.get("bad-store"));
  540   
  541               // write to stream
  542               if (_mappingWriter != null) {
  543                   PrintWriter out = new PrintWriter(_mappingWriter);
  544                   for (Iterator itr = output.values().iterator();
  545                       itr.hasNext();)
  546                       out.println((String) itr.next());
  547                   out.flush();
  548               }
  549           }
  550           catch (RuntimeException re) {
  551               throw re;
  552           } catch (Exception e) {
  553               throw new GeneralException(e);
  554           } finally {
  555               clear();
  556           }
  557       }
  558   
  559       /**
  560        * Drops schema components that appear to be unused from the local
  561        * copy of the schema group.
  562        */
  563       private void dropUnusedSchemaComponents(ClassMapping[] mappings) {
  564           FieldMapping[] fields;
  565           for (int i = 0; i < mappings.length; i++) {
  566               mappings[i].refSchemaComponents();
  567               mappings[i].getDiscriminator().refSchemaComponents();
  568               mappings[i].getVersion().refSchemaComponents();
  569               fields = mappings[i].getDefinedFieldMappings();
  570               for (int j = 0; j < fields.length; j++)
  571                   fields[j].refSchemaComponents();
  572           }
  573   
  574           // also allow the dbdictionary to ref any schema components that
  575           // it adds apart from mappings
  576           SchemaGroup group = getSchemaGroup();
  577           Schema[] schemas = group.getSchemas();
  578           Table[] tables;
  579           for (int i = 0; i < schemas.length; i++) {
  580               tables = schemas[i].getTables();
  581               for (int j = 0; j < tables.length; j++)
  582                   _dict.refSchemaComponents(tables[j]);
  583           }
  584   
  585           group.removeUnusedComponents();
  586       }
  587   
  588       /**
  589        * Add tables used by sequences to the given schema.
  590        */
  591       private void addSequenceComponents(ClassMapping[] mappings) {
  592           SchemaGroup group = getSchemaGroup();
  593           for (int i = 0; i < mappings.length; i++)
  594               addSequenceComponents(mappings[i], group);
  595       }
  596   
  597       /**
  598        * Add tables used by sequences to the given schema.
  599        */
  600       private void addSequenceComponents(ClassMapping mapping,
  601           SchemaGroup group) {
  602           SequenceMetaData smd = mapping.getIdentitySequenceMetaData();
  603           Seq seq = null;
  604           if (smd != null)
  605               seq = smd.getInstance(null);
  606           else if (mapping.getIdentityStrategy() == ValueStrategies.NATIVE
  607               || (mapping.getIdentityStrategy() == ValueStrategies.NONE
  608               && mapping.getIdentityType() == ClassMapping.ID_DATASTORE))
  609               seq = _conf.getSequenceInstance();
  610   
  611           if (seq instanceof JDBCSeq)
  612               ((JDBCSeq) seq).addSchema(mapping, group);
  613   
  614           FieldMapping[] fmds;
  615           if (mapping.getEmbeddingMetaData() == null)
  616               fmds = mapping.getDefinedFieldMappings();
  617           else
  618               fmds = mapping.getFieldMappings();
  619           for (int i = 0; i < fmds.length; i++) {
  620               smd = fmds[i].getValueSequenceMetaData();
  621               if (smd != null) {
  622                   seq = smd.getInstance(null);
  623                   if (seq instanceof JDBCSeq)
  624                       ((JDBCSeq) seq).addSchema(mapping, group);
  625               } else if (fmds[i].getEmbeddedMapping() != null)
  626                   addSequenceComponents(fmds[i].getEmbeddedMapping(), group);
  627           }
  628       }
  629   
  630       ///////////
  631       // Actions
  632       ///////////
  633   
  634       /**
  635        * Run the configured action on the given instance.
  636        */
  637       public void run(Class cls) {
  638           if (ACTION_ADD.equals(_action)) {
  639               if (_meta)
  640                   addMeta(cls);
  641               else
  642                   add(cls);
  643           } else if (ACTION_REFRESH.equals(_action))
  644               refresh(cls);
  645           else if (ACTION_BUILD_SCHEMA.equals(_action))
  646               buildSchema(cls);
  647           else if (ACTION_DROP.equals(_action))
  648               drop(cls);
  649           else if (ACTION_VALIDATE.equals(_action))
  650               validate(cls);
  651       }
  652   
  653       /**
  654        * Add the mapping for the given instance.
  655        */
  656       private void add(Class cls) {
  657           if (cls == null)
  658               return;
  659   
  660           MappingRepository repos = getRepository();
  661           repos.setStrategyInstaller(new MappingStrategyInstaller(repos));
  662           if (getMapping(repos, cls, true) != null) {
  663               _flush = true;
  664               _flushSchema = true;
  665           }
  666       }
  667   
  668       /**
  669        * Return the mapping for the given type, or null if the type is
  670        * persistence-aware.
  671        */
  672       private static ClassMapping getMapping(MappingRepository repos, Class cls,
  673           boolean validate) {
  674           // this will parse all possible metadata rsrcs looking for cls, so
  675           // will detect if p-aware
  676           ClassMapping mapping = repos.getMapping(cls, null, false);
  677           if (mapping != null)
  678               return mapping;
  679           if (!validate || cls.isInterface() 
  680               || repos.getPersistenceAware(cls) != null)
  681               return null;
  682           throw new MetaDataException(_loc.get("no-meta", cls));
  683       }
  684   
  685       /**
  686        * Create a metadata for the given instance.
  687        */
  688       private void addMeta(Class cls) {
  689           if (cls == null)
  690               return;
  691   
  692           _flush = true;
  693           MappingRepository repos = getRepository();
  694           repos.setResolve(MODE_MAPPING, false);
  695           MetaDataFactory factory = repos.getMetaDataFactory();
  696           factory.getDefaults().setIgnoreNonPersistent(false);
  697           factory.setStoreMode(MetaDataFactory.STORE_VERBOSE);
  698   
  699           ClassMetaData meta = repos.addMetaData(cls);
  700           FieldMetaData[] fmds = meta.getDeclaredFields();
  701           for (int i = 0; i < fmds.length; i++) {
  702               if (fmds[i].getDeclaredTypeCode() == JavaTypes.OBJECT
  703                   && fmds[i].getDeclaredType() != Object.class)
  704                   fmds[i].setDeclaredTypeCode(JavaTypes.PC);
  705           }
  706           meta.setSource(_file, meta.getSourceType());
  707           meta.setResolve(MODE_META, true);
  708       }
  709   
  710       /**
  711        * Refresh or add the mapping for the given instance.
  712        */
  713       private void refresh(Class cls) {
  714           if (cls == null)
  715               return;
  716   
  717           MappingRepository repos = getRepository();
  718           repos.setStrategyInstaller(new RefreshStrategyInstaller(repos));
  719           if (getMapping(repos, cls, true) != null) {
  720               _flush = true;
  721               _flushSchema = true;
  722           }
  723       }
  724   
  725       /**
  726        * Validate the mappings for the given class and its fields.
  727        */
  728       private void validate(Class cls) {
  729           if (cls == null)
  730               return;
  731   
  732           MappingRepository repos = getRepository();
  733           repos.setStrategyInstaller(new RuntimeStrategyInstaller(repos));
  734           if (getMapping(repos, cls, true) != null)
  735               _flushSchema = !contains(_schemaActions,SCHEMA_ACTION_NONE)
  736                   && !contains(_schemaActions,SchemaTool.ACTION_ADD);
  737       }
  738   
  739       /**
  740        * Create the schema using the mapping for the given instance.
  741        */
  742       private void buildSchema(Class cls) {
  743           if (cls == null)
  744               return;
  745   
  746           MappingRepository repos = getRepository();
  747           repos.setStrategyInstaller(new RuntimeStrategyInstaller(repos));
  748           if (getMapping(repos, cls, true) == null)
  749               return;
  750   
  751           // set any logical pks to non-logical so they get flushed
  752           _flushSchema = true;
  753           Schema[] schemas = _schema.getSchemas();
  754           Table[] tables;
  755           Column[] cols;
  756           for (int i = 0; i < schemas.length; i++) {
  757               tables = schemas[i].getTables();
  758               for (int j = 0; j < tables.length; j++) {
  759                   if (tables[j].getPrimaryKey() == null)
  760                       continue;
  761   
  762                   tables[j].getPrimaryKey().setLogical(false);
  763                   cols = tables[j].getPrimaryKey().getColumns();
  764                   for (int k = 0; k < cols.length; k++)
  765                       cols[k].setNotNull(true);
  766               }
  767           }
  768       }
  769   
  770       /**
  771        * Drop mapping for given class.
  772        */
  773       private void drop(Class cls) {
  774           if (cls == null)
  775               return;
  776   
  777           if (_dropCls == null)
  778               _dropCls = new HashSet();
  779           _dropCls.add(cls);
  780           if (!contains(_schemaActions,SchemaTool.ACTION_DROP))
  781               return;
  782   
  783           MappingRepository repos = getRepository();
  784           repos.setStrategyInstaller(new RuntimeStrategyInstaller(repos));
  785           ClassMapping mapping = null;
  786           try {
  787               mapping = repos.getMapping(cls, null, false);
  788           } catch (Exception e) {
  789           }
  790   
  791           if (mapping != null) {
  792               _flushSchema = true;
  793               if (_dropMap == null)
  794                   _dropMap = new HashSet();
  795               _dropMap.add(mapping);
  796           } else
  797               _log.warn(_loc.get("no-drop-meta", cls));
  798       }
  799   
  800       ////////
  801       // Main
  802       ////////
  803   
  804       /**
  805        * Usage: java org.apache.openjpa.jdbc.meta.MappingTool [option]* 
  806        * [-action/-a &lt;refresh | add | buildSchema | drop | validate | import 
  807        * | export&gt;] &lt;class name | .java file | .class file | .jdo file&gt;*
  808        * Where the following options are recognized.
  809        * <ul>
  810        * <li><i>-properties/-p &lt;properties file or resource&gt;</i>: The
  811        * path or resource name of a OpenJPA properties file containing
  812        * information as outlined in {@link OpenJPAConfiguration}. Optional.</li>
  813        * <li><i>-&lt;property name&gt; &lt;property value&gt;</i>: All bean
  814        * properties of the OpenJPA {@link JDBCConfiguration} can be set by
  815        * using their	names and supplying a value. For example:
  816        * <code>-licenseKey adslfja83r3lkadf</code></li>
  817        * <li><i>-file/-f &lt;stdout | output file or resource&gt;</i>: Use
  818        * this option to write the planned mappings to an XML document rather
  819        * than store them in the repository. This option also specifies the
  820        * metadata file to write to if using the <code>add</code> action with
  821        * the <code>-meta true</code> flag, or the file to dump to if using
  822        * the <code>export</code> action.</li>
  823        * <li><i>-schemaAction/-sa &lt;schema action | none&gt;</i>: The
  824        * {@link SchemaTool} defines the actions possible. The actions will
  825        * apply to all schema components used by the mappings involved.
  826        * Unless you are running the mapping tool on all of your persistent
  827        * types at once, be careful running schema actions that can drop data.
  828        * It is possible to accidentally drop schema components that are
  829        * used by classes you aren't currently running the tool over. The
  830        * action defaults to <code>add</code>.</li>
  831        * <li><i>-schemaFile/-sf &lt;stdout | output file or resource&gt;</i>: Use
  832        * this option to write the planned schema to an XML document rather
  833        * than modify the data store.</li>
  834        * <li><i>-sqlFile/-sql &lt;stdout | output file or resource&gt;</i>: Use
  835        * this option to write the planned schema changes as a SQL
  836        * script rather than modifying the data store.</li>
  837        * <li><i>-dropTables/-dt &lt;true/t | false/f&gt;</i>: Corresponds to the
  838        * same-named option in the {@link SchemaTool}.</li>
  839        * <li><i>-dropSequences/-dsq &lt;true/t | false/f&gt;</i>: Corresponds
  840        * to the same-named option in the {@link SchemaTool}.</li>
  841        * <li><i>-openjpaTables/-kt &lt;true/t | false/f&gt;</i>: Corresponds to
  842        * the same-named option in the {@link SchemaTool}.</li>
  843        * <li><i>-ignoreErrors/-i &lt;true/t | false/f&gt;</i>: Corresponds to the
  844        * same-named option in the {@link SchemaTool}.</li>
  845        * <li><i>-readSchema/-rs &lt;true/t | false/f&gt;</i>: Set this to true
  846        * to read the entire existing schema (even when false the parts of
  847        * the schema used by classes the tool is run on will still be read).
  848        * Turning on schema reading can ensure that no naming conflicts will
  849        * occur, but it can take a long time.</li>
  850        * <li><i>-primaryKeys/-pk &lt;true/t | false/f&gt;</i>: Whether primary
  851        * keys on existing tables are manipulated. Defaults to false.</li>
  852        * <li><i>-foreignKeys/-fk &lt;true/t | false/f&gt;</i>: Whether foreign
  853        * keys on existing tables are manipulated. Defaults to false.</li>
  854        * <li><i>-indexes/-ix &lt;true/t | false/f&gt;</i>: Whether indexes on
  855        * existing tables are manipulated. Defaults to false.</li>
  856        * <li><i>-sequences/-sq &lt;true/t | false/f&gt;</i>: Whether sequences
  857        * are manipulated. Defaults to true.</li>
  858        * <li><i>-schemas/-s &lt;schema and table names&gt;</i>: A list of schemas
  859        * and/or tables to read. Corresponds to the
  860        * same-named option in the {@link SchemaGenerator}. This option
  861        * is ignored if <code>readSchema</code> is false.</li>
  862        * <li><i>-meta/-m &lt;true/t | false/f&gt;</i>: Whether the given action
  863        * applies to metadata as well as mappings.</li>
  864        * </ul>
  865        *  The various actions are as follows.
  866        * <ul>
  867        * <li><i>refresh</i>: Bring the mapping information up-to-date
  868        * with the class definitions. OpenJPA will attempt to use any provided
  869        * mapping information, and fill in missing information. If the
  870        * provided information conflicts with the class definition, the
  871        * conflicting information will be discarded and the class/field will
  872        * be re-mapped to new columns/tables. This is the default action.</li>
  873        * <li><i>add</i>: If used with the <code>-meta</code> option, adds new
  874        * default metadata for the given class(es). Otherwise, brings the
  875        * mapping information up-to-date with the class
  876        * definitions. OpenJPA will attempt to use any provided mapping
  877        * information, and fill in missing information. OpenJPA will fail if
  878        * the provided information conflicts with the class definition.</li>
  879        * <li><i>buildSchema</i>: Create the schema matching the existing
  880        * mappings for the given class(es). Any invalid mapping information
  881        * will cause an exception.</li>
  882        * <li><i>drop</i>: Delete mappings for the given classes. If used with
  883        * the <code>-meta</code> option, also deletes metadata.</li>
  884        * <li><i>validate</i>: Validate the given mappings. The mapping
  885        * repository and schema will not be affected.</li>
  886        * <li><i>import</i>: Import mappings from an XML document and store
  887        * them as the current system mappings.</li>
  888        * <li><i>export</i>: Dump the current mappings for the given classes to
  889        * an XML document specified by the <code>file</code> option.</li>
  890        * If used with the <code>-meta</code> option, the metadata will be
  891        * included in the export.
  892        * </ul>
  893        *  Each class supplied as an argument must have valid metadata. If
  894        * no class arguments are given, the tool runs on all metadata files in
  895        * the CLASSPATH.
  896        *  Examples:
  897        * <ul>
  898        * <li>Refresh the mappings for given package, without dropping any
  899        * schema components:<br />
  900        * <code>java org.apache.openjpa.jdbc.meta.MappingTool 
  901        *      mypackage.jdo</code></li>
  902        * <li>Refresh the mappings for all persistent classes in the classpath,
  903        * dropping any unused columns and even tables:<br />
  904        * <code>java org.apache.openjpa.jdbc.meta.MappingTool -sa refresh
  905        * -dt true</code></li>
  906        * <li>Make sure the mappings you've created by hand match the object
  907        * model and schema:<br />
  908        * <code>java org.apache.openjpa.jbdc.meta.MappingTool
  909        * -a validate Person.java</code></li>
  910        * <li>Remove the recorded mapping for a given class:<br />
  911        * <code>java org.apache.openjpa.jbdc.meta.MappingTool
  912        * -a drop Person.java</code></li>
  913        * <li>Record the current mappings in an XML file:<br />
  914        * <code>java org.apache.openjpa.jdbc.meta.MappingTool
  915        * -f mypackage.orm -a export mypackage.jdo</code></li>
  916        * </ul>
  917        */
  918       public static void main(String[] arguments)
  919           throws IOException, SQLException {
  920           Options opts = new Options();
  921           final String[] args = opts.setFromCmdLine(arguments);
  922           boolean ret = Configurations.runAgainstAllAnchors(opts,
  923               new Configurations.Runnable() {
  924               public boolean run(Options opts) throws IOException, SQLException {
  925                   JDBCConfiguration conf = new JDBCConfigurationImpl();
  926                   try {
  927                       return MappingTool.run(conf, args, opts);
  928                   } finally {
  929                       conf.close();
  930                   }
  931               }
  932           });
  933           if (!ret)
  934               System.err.println(_loc.get("tool-usage"));
  935       }
  936   
  937       /**
  938        * Run the tool. Returns false if invalid options are given.
  939        *
  940        * @see #main
  941        */
  942       public static boolean run(JDBCConfiguration conf, String[] args,
  943           Options opts)
  944           throws IOException, SQLException {
  945           // flags
  946           Flags flags = new Flags();
  947           flags.action = opts.removeProperty("action", "a", flags.action);
  948           flags.schemaAction = opts.removeProperty("schemaAction", "sa",
  949               flags.schemaAction);
  950           flags.dropTables = opts.removeBooleanProperty
  951               ("dropTables", "dt", flags.dropTables);
  952           flags.openjpaTables = opts.removeBooleanProperty
  953               ("openjpaTables", "ot", flags.openjpaTables);
  954           flags.dropSequences = opts.removeBooleanProperty
  955               ("dropSequences", "dsq", flags.dropSequences);
  956           flags.readSchema = opts.removeBooleanProperty
  957               ("readSchema", "rs", flags.readSchema);
  958           flags.primaryKeys = opts.removeBooleanProperty
  959               ("primaryKeys", "pk", flags.primaryKeys);
  960           flags.indexes = opts.removeBooleanProperty("indexes", "ix",
  961               flags.indexes);
  962           flags.foreignKeys = opts.removeBooleanProperty("foreignKeys", "fk",
  963               flags.foreignKeys);
  964           flags.sequences = opts.removeBooleanProperty("sequences", "sq",
  965               flags.sequences);
  966           flags.ignoreErrors = opts.removeBooleanProperty
  967               ("ignoreErrors", "i", flags.ignoreErrors);
  968           flags.meta = opts.removeBooleanProperty("meta", "m", flags.meta);
  969           String fileName = opts.removeProperty("file", "f", null);
  970           String schemaFileName = opts.removeProperty("schemaFile", "sf", null);
  971           String sqlFileName = opts.removeProperty("sqlFile", "sql", null);
  972           String schemas = opts.removeProperty("s");
  973           if (schemas != null)
  974               opts.setProperty("schemas", schemas);
  975   
  976           Configurations.populateConfiguration(conf, opts);
  977           ClassLoader loader = conf.getClassResolverInstance().
  978               getClassLoader(MappingTool.class, null);
  979           if (flags.meta && ACTION_ADD.equals(flags.action))
  980               flags.metaDataFile = Files.getFile(fileName, loader);
  981           else
  982               flags.mappingWriter = Files.getWriter(fileName, loader);
  983           flags.schemaWriter = Files.getWriter(schemaFileName, loader);
  984           flags.sqlWriter = Files.getWriter(sqlFileName, loader);
  985   
  986           return run(conf, args, flags, loader);
  987       }
  988   
  989       /**
  990        * Run the tool. Return false if an invalid option was given.
  991        */
  992       public static boolean run(JDBCConfiguration conf, String[] args,
  993           Flags flags, ClassLoader loader)
  994           throws IOException, SQLException {
  995           // default action based on whether the mapping defaults fills in
  996           // missing info
  997           if (flags.action == null) {
  998               if (conf.getMappingDefaultsInstance().defaultMissingInfo())
  999                   flags.action = ACTION_BUILD_SCHEMA;
 1000               else
 1001                   flags.action = ACTION_REFRESH;
 1002           }
 1003   
 1004           // collect the classes to act on
 1005           Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
 1006           Collection classes = null;
 1007           if (args.length == 0) {
 1008               if (ACTION_IMPORT.equals(flags.action))
 1009                   return false;
 1010               log.info(_loc.get("running-all-classes"));
 1011               classes = conf.getMappingRepositoryInstance().
 1012                   loadPersistentTypes(true, loader);
 1013           } else {
 1014               classes = new HashSet();
 1015               ClassArgParser classParser = conf.getMetaDataRepositoryInstance().
 1016                   getMetaDataFactory().newClassArgParser();
 1017               classParser.setClassLoader(loader);
 1018               Class[] parsed;
 1019               for (int i = 0; args != null && i < args.length; i++) {
 1020                   parsed = classParser.parseTypes(args[i]);
 1021                   classes.addAll(Arrays.asList(parsed));
 1022               }
 1023           }
 1024   
 1025           Class[] act = (Class[]) classes.toArray(new Class[classes.size()]);
 1026           if (ACTION_EXPORT.equals(flags.action)) {
 1027               // run exports until the first export succeeds
 1028               ImportExport[] instances = newImportExports();
 1029               for (int i = 0; i < instances.length; i++) {
 1030                   if (instances[i].exportMappings(conf, act, flags.meta, log,
 1031                       flags.mappingWriter))
 1032                       return true;
 1033               }
 1034               return false;
 1035           }
 1036           if (ACTION_IMPORT.equals(flags.action)) {
 1037               // run exports until the first export succeeds
 1038               ImportExport[] instances = newImportExports();
 1039               for (int i = 0; i < instances.length; i++) {
 1040                   if (instances[i].importMappings(conf, act, args, flags.meta,
 1041                       log, loader))
 1042                       return true;
 1043               }
 1044               return false;
 1045           }
 1046   
 1047           MappingTool tool;
 1048           try {
 1049               tool = new MappingTool(conf, flags.action, flags.meta);
 1050           } catch (IllegalArgumentException iae) {
 1051               return false;
 1052           }
 1053   
 1054           // setup the tool
 1055           tool.setIgnoreErrors(flags.ignoreErrors);
 1056           tool.setMetaDataFile(flags.metaDataFile);
 1057           tool.setMappingWriter(flags.mappingWriter);
 1058           tool.setSchemaAction(flags.schemaAction);
 1059           tool.setSchemaWriter(flags.schemaWriter);
 1060           tool.setReadSchema(flags.readSchema
 1061               && !ACTION_VALIDATE.equals(flags.action));
 1062           tool.setPrimaryKeys(flags.primaryKeys);
 1063           tool.setForeignKeys(flags.foreignKeys);
 1064           tool.setIndexes(flags.indexes);
 1065           tool.setSequences(flags.sequences || flags.dropSequences);
 1066   
 1067           // and run the action
 1068           for (int i = 0; i < act.length; i++) {
 1069               log.info(_loc.get("tool-running", act[i], flags.action));
 1070               if (i == 0 && flags.readSchema)
 1071                   log.info(_loc.get("tool-time"));
 1072               tool.run(act[i]);
 1073           }
 1074           log.info(_loc.get("tool-record"));
 1075           tool.record(flags);
 1076           return true;
 1077       }
 1078   
 1079       /**
 1080        * Create an {@link ImportExport} instance.
 1081        */
 1082       private static ImportExport[] newImportExports() {
 1083           try {
 1084               Class[] types = Services.getImplementorClasses(ImportExport.class);
 1085               ImportExport[] instances = new ImportExport[types.length];
 1086               for (int i = 0; i < types.length; i++)
 1087                   instances[i] = (ImportExport) AccessController.doPrivileged(
 1088                       J2DoPrivHelper.newInstanceAction(types[i]));
 1089               return instances;
 1090           } catch (Throwable t) {
 1091               if (t instanceof PrivilegedActionException)
 1092                   t = ((PrivilegedActionException) t).getException();
 1093               throw new InternalException(_loc.get("importexport-instantiate"),t);
 1094           }
 1095       }
 1096       
 1097       private static boolean contains(String list, String key) {
 1098       	return (list == null) ? false : list.indexOf(key) != -1;
 1099       }
 1100   
 1101       /**
 1102        * Run flags.
 1103        */
 1104       public static class Flags {
 1105   
 1106           public String action = null;
 1107           public boolean meta = false;
 1108           public String schemaAction = SchemaTool.ACTION_ADD;
 1109           public File metaDataFile = null;
 1110           public Writer mappingWriter = null;
 1111           public Writer schemaWriter = null;
 1112           public Writer sqlWriter = null;
 1113           public boolean ignoreErrors = false;
 1114           public boolean readSchema = false;
 1115           public boolean dropTables = false;
 1116           public boolean openjpaTables = false;
 1117           public boolean dropSequences = false;
 1118           public boolean sequences = true;
 1119           public boolean primaryKeys = false;
 1120           public boolean foreignKeys = false;
 1121           public boolean indexes = false;
 1122       }
 1123   
 1124       /**
 1125        * Helper used to import and export mapping data.
 1126        */
 1127       public static interface ImportExport {
 1128   
 1129           /**
 1130            * Import mappings for the given classes based on the given arguments.
 1131            */
 1132           public boolean importMappings(JDBCConfiguration conf, Class[] act,
 1133               String[] args, boolean meta, Log log, ClassLoader loader)
 1134               throws IOException;
 1135   
 1136           /**
 1137            * Export mappings for the given classes based on the given arguments.
 1138            */
 1139           public boolean exportMappings(JDBCConfiguration conf, Class[] act,
 1140               boolean meta, Log log, Writer writer)
 1141               throws IOException;
 1142       }
 1143   }

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