Home » apache-openjpa-1.1.0-source » org.apache.openjpa » 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.meta;
   20   
   21   import java.io.Serializable;
   22   import java.security.AccessController;
   23   import java.util.ArrayList;
   24   import java.util.Arrays;
   25   import java.util.Collection;
   26   import java.util.Collections;
   27   import java.util.HashMap;
   28   import java.util.HashSet;
   29   import java.util.Iterator;
   30   import java.util.LinkedList;
   31   import java.util.List;
   32   import java.util.Map;
   33   import java.util.Set;
   34   import java.util.TreeSet;
   35   
   36   import org.apache.commons.lang.StringUtils;
   37   import org.apache.openjpa.conf.OpenJPAConfiguration;
   38   import org.apache.openjpa.enhance.DynamicPersistenceCapable;
   39   import org.apache.openjpa.enhance.PCRegistry;
   40   import org.apache.openjpa.enhance.PCRegistry.RegisterClassListener;
   41   import org.apache.openjpa.enhance.PersistenceCapable;
   42   import org.apache.openjpa.event.LifecycleEventManager;
   43   import org.apache.openjpa.lib.conf.Configurable;
   44   import org.apache.openjpa.lib.conf.Configuration;
   45   import org.apache.openjpa.lib.log.Log;
   46   import org.apache.openjpa.lib.util.Closeable;
   47   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   48   import org.apache.openjpa.lib.util.Localizer;
   49   import org.apache.openjpa.lib.util.StringDistance;
   50   import org.apache.openjpa.util.ImplHelper;
   51   import org.apache.openjpa.util.InternalException;
   52   import org.apache.openjpa.util.MetaDataException;
   53   import org.apache.openjpa.util.OpenJPAId;
   54   import serp.util.Strings;
   55   
   56   /**
   57    * Repository of and factory for persistent metadata.
   58    *
   59    * @since 0.3.0
   60    * @author Abe White
   61    * @author Steve Kim (query metadata)
   62    */
   63   public class MetaDataRepository
   64       implements PCRegistry.RegisterClassListener, Configurable, Closeable, 
   65       MetaDataModes, Serializable {
   66   
   67       /**
   68        * Constant to not validate any metadata.
   69        */
   70       public static final int VALIDATE_NONE = 0;
   71   
   72       /**
   73        * Bit flag to validate metadata.
   74        */
   75       public static final int VALIDATE_META = 1;
   76   
   77       /**
   78        * Bit flag to validate mappings.
   79        */
   80       public static final int VALIDATE_MAPPING = 2;
   81   
   82       /**
   83        * Bit flag to validate unenhanced metadata only.
   84        */
   85       public static final int VALIDATE_UNENHANCED = 4;
   86   
   87       /**
   88        * Bit flag for runtime validation. Requires that all classes are
   89        * enhanced, and performs extra field resolution steps.
   90        */
   91       public static final int VALIDATE_RUNTIME = 8;
   92   
   93       protected static final Class[] EMPTY_CLASSES = new Class[0];
   94       protected static final NonPersistentMetaData[] EMPTY_NON_PERSISTENT = 
   95       	new NonPersistentMetaData[0];
   96       protected final ClassMetaData[] EMPTY_METAS;
   97       protected final FieldMetaData[] EMPTY_FIELDS;
   98       protected final Order[] EMPTY_ORDERS;
   99   
  100       private static final Localizer _loc = Localizer.forPackage
  101           (MetaDataRepository.class);
  102   
  103       // system sequence
  104       private SequenceMetaData _sysSeq = null;
  105   
  106       // cache of parsed metadata, oid class to class, and interface class
  107       // to metadatas
  108       private final Map _metas = new HashMap();
  109       private final Map _oids = Collections.synchronizedMap(new HashMap());
  110       private final Map _impls = Collections.synchronizedMap(new HashMap());
  111       private final Map _ifaces = Collections.synchronizedMap(new HashMap());
  112       private final Map _queries = new HashMap();
  113       private final Map _seqs = new HashMap();
  114       private final Map _aliases = Collections.synchronizedMap(new HashMap());
  115       private final Map _pawares = Collections.synchronizedMap(new HashMap());
  116       private final Map _nonMapped = Collections.synchronizedMap(new HashMap());
  117       
  118       // map of classes to lists of their subclasses
  119       private final Map _subs = Collections.synchronizedMap(new HashMap());
  120   
  121       // xml mapping
  122       protected final XMLMetaData[] EMPTY_XMLMETAS;
  123       private final Map _xmlmetas = new HashMap();
  124   
  125       private transient OpenJPAConfiguration _conf = null;
  126       private transient Log _log = null;
  127       private transient InterfaceImplGenerator _implGen = null;
  128       private transient MetaDataFactory _factory = null;
  129   
  130       private int _resMode = MODE_META | MODE_MAPPING;
  131       private int _sourceMode = MODE_META | MODE_MAPPING | MODE_QUERY;
  132       private int _validate = VALIDATE_META | VALIDATE_UNENHANCED;
  133   
  134       // we buffer up any classes that register themselves to prevent
  135       // reentrancy errors if classes register during a current parse (common)
  136       private final Collection _registered = new HashSet();
  137   
  138       // set of metadatas we're in the process of resolving
  139       private final InheritanceOrderedMetaDataList _resolving =
  140           new InheritanceOrderedMetaDataList();
  141       private final InheritanceOrderedMetaDataList _mapping =
  142           new InheritanceOrderedMetaDataList();
  143       private final List _errs = new LinkedList();
  144   
  145       // system listeners
  146       private LifecycleEventManager.ListenerList _listeners =
  147           new LifecycleEventManager.ListenerList(3);
  148   
  149       /**
  150        * Default constructor.  Configure via {@link Configurable}.
  151        */
  152       public MetaDataRepository() {
  153           EMPTY_METAS = newClassMetaDataArray(0);
  154           EMPTY_FIELDS = newFieldMetaDataArray(0);
  155           EMPTY_ORDERS = newOrderArray(0);
  156           EMPTY_XMLMETAS = newXMLClassMetaDataArray(0);
  157       }
  158   
  159       /**
  160        * Return the configuration for the repository.
  161        */
  162       public OpenJPAConfiguration getConfiguration() {
  163           return _conf;
  164       }
  165   
  166       /**
  167        * Return the metadata log.
  168        */
  169       public Log getLog() {
  170           return _log;
  171       }
  172   
  173       /**
  174        * The I/O used to load metadata.
  175        */
  176       public MetaDataFactory getMetaDataFactory() {
  177           return _factory;
  178       }
  179   
  180       /**
  181        * The I/O used to load metadata.
  182        */
  183       public void setMetaDataFactory(MetaDataFactory factory) {
  184           factory.setRepository(this);
  185           _factory = factory;
  186       }
  187   
  188       /**
  189        * The metadata validation level. Defaults to
  190        * <code>VALIDATE_META | VALIDATE_UNENHANCED</code>.
  191        */
  192       public int getValidate() {
  193           return _validate;
  194       }
  195   
  196       /**
  197        * The metadata validation level. Defaults to
  198        * <code>VALIDATE_META | VALIDATE_UNENHANCED</code>.
  199        */
  200       public void setValidate(int validate) {
  201           _validate = validate;
  202       }
  203   
  204       /**
  205        * The metadata validation level. Defaults to
  206        * <code>VALIDATE_META | VALIDATE_MAPPING | VALIDATE_UNENHANCED</code>.
  207        */
  208       public void setValidate(int validate, boolean on) {
  209           if (validate == VALIDATE_NONE)
  210               _validate = validate;
  211           else if (on)
  212               _validate |= validate;
  213           else
  214               _validate &= ~validate;
  215       }
  216   
  217       /**
  218        * The metadata resolution mode. Defaults to
  219        * <code>MODE_META | MODE_MAPPING</code>.
  220        */
  221       public int getResolve() {
  222           return _resMode;
  223       }
  224   
  225       /**
  226        * The metadata resolution mode. Defaults to
  227        * <code>MODE_META | MODE_MAPPING</code>.
  228        */
  229       public void setResolve(int mode) {
  230           _resMode = mode;
  231       }
  232   
  233       /**
  234        * The metadata resolution mode. Defaults to
  235        * <code>MODE_META | MODE_MAPPING</code>.
  236        */
  237       public void setResolve(int mode, boolean on) {
  238           if (mode == MODE_NONE)
  239               _resMode = mode;
  240           else if (on)
  241               _resMode |= mode;
  242           else
  243               _resMode &= ~mode;
  244       }
  245   
  246       /**
  247        * The source mode determining what metadata to load. Defaults to
  248        * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
  249        */
  250       public int getSourceMode() {
  251           return _sourceMode;
  252       }
  253   
  254       /**
  255        * The source mode determining what metadata to load. Defaults to
  256        * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
  257        */
  258       public void setSourceMode(int mode) {
  259           _sourceMode = mode;
  260       }
  261   
  262       /**
  263        * The source mode determining what metadata to load. Defaults to
  264        * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
  265        */
  266       public void setSourceMode(int mode, boolean on) {
  267           if (mode == MODE_NONE)
  268               _sourceMode = mode;
  269           else if (on)
  270               _sourceMode |= mode;
  271           else
  272               _sourceMode &= ~mode;
  273       }
  274   
  275       /**
  276        * Return the metadata for the given class.
  277        *
  278        * @param cls the class to retrieve metadata for
  279        * @param envLoader the environmental class loader, if any
  280        * @param mustExist if true, throws a {@link MetaDataException}
  281        * if no metadata is found
  282        */
  283       public synchronized ClassMetaData getMetaData(Class cls,
  284           ClassLoader envLoader, boolean mustExist) {
  285           if (cls != null &&
  286               DynamicPersistenceCapable.class.isAssignableFrom(cls))
  287               cls = cls.getSuperclass();
  288   
  289           // if cls is a generated interface, use the user interface
  290           // to locate metadata
  291           if (cls != null && _implGen.isImplType(cls))
  292               cls = _implGen.toManagedInterface(cls);
  293   
  294           ClassMetaData meta = getMetaDataInternal(cls, envLoader);
  295           if (meta == null && mustExist) {
  296               if (cls != null &&
  297                   !ImplHelper.isManagedType(_conf, cls))
  298                   throw new MetaDataException(_loc.get("no-meta-notpc", cls)).
  299                       setFatal(false);
  300   
  301               Set pcNames = getPersistentTypeNames(false, envLoader);
  302               if (pcNames != null && pcNames.size() > 0)
  303                   throw new MetaDataException(_loc.get("no-meta-types",
  304                       cls, pcNames));
  305   
  306               throw new MetaDataException(_loc.get("no-meta", cls));
  307           }
  308           resolve(meta);
  309           return meta;
  310       }
  311   
  312       /**
  313        * Return the metadata for the given alias name.
  314        *
  315        * @param alias the alias to class to retrieve metadata for
  316        * @param envLoader the environmental class loader, if any
  317        * @param mustExist if true, throws a {@link MetaDataException}
  318        * if no metadata is found
  319        * @see ClassMetaData#getTypeAlias
  320        */
  321       public ClassMetaData getMetaData(String alias, ClassLoader envLoader,
  322           boolean mustExist) {
  323           if (alias == null && mustExist)
  324               throw new MetaDataException(_loc.get("no-alias-meta", alias,
  325                   _aliases));
  326           if (alias == null)
  327               return null;
  328   
  329           // check cache
  330           processRegisteredClasses(envLoader);
  331           List classList = (List) _aliases.get(alias);
  332   
  333           // multiple classes may have been defined with the same alias: we
  334           // will filter by checking against the current list of the
  335           // persistent types and filter based on which classes are loadable
  336           // via the current environment's ClassLoader
  337           Set pcNames = getPersistentTypeNames(false, envLoader);
  338           Class cls = null;
  339           for (int i = 0; classList != null && i < classList.size(); i++) {
  340               Class c = (Class) classList.get(i);
  341               try {
  342                   // re-load the class in the current environment loader so
  343                   // that we can handle redeployment of the same class name
  344                   Class nc = Class.forName(c.getName(), false, envLoader);
  345   
  346                   // if we have specified a list of persistent clases,
  347                   // also check to ensure that the class is in that list
  348                   if (pcNames == null || pcNames.size() == 0 
  349                       || pcNames.contains(nc.getName())) {
  350                       cls = nc;
  351                       if (!classList.contains(cls))
  352                           classList.add(cls);
  353                       break;
  354                   }
  355               } catch (Throwable t) {
  356                   // this happens when the class is not loadable by
  357                   // the environment class loader, so it was probably
  358                   // listed elsewhere; also ignore linkage failures and
  359                   // other class loading problems
  360               }
  361           }
  362           if (cls != null)
  363               return getMetaData(cls, envLoader, mustExist);
  364   
  365           // maybe this is some type we've seen but just isn't valid
  366           if (_aliases.containsKey(alias)) {
  367               if (mustExist)
  368                   throwNoRegisteredAlias(alias);
  369               return null;
  370           }
  371   
  372           // record that this is an invalid type
  373           _aliases.put(alias, null);
  374   
  375           if (!mustExist)
  376               return null;
  377           return throwNoRegisteredAlias(alias);
  378       }
  379   
  380       private ClassMetaData throwNoRegisteredAlias(String alias) {
  381           String close = getClosestAliasName(alias);
  382           if (close != null)
  383               throw new MetaDataException(
  384                   _loc.get("no-alias-meta-hint", alias, _aliases, close));
  385           else
  386               throw new MetaDataException(
  387                   _loc.get("no-alias-meta", alias, _aliases));
  388       }
  389   
  390       /**
  391        * @return the nearest match to the specified alias name
  392        * @since 1.1.0
  393        */
  394       public String getClosestAliasName(String alias) {
  395           Collection aliases = getAliasNames();
  396           return StringDistance.getClosestLevenshteinDistance(alias, aliases);
  397       }
  398   
  399       /**
  400        * @return the registered alias names
  401        * @since 1.1.0
  402        */
  403       public Collection getAliasNames() {
  404           Collection aliases = new HashSet();
  405           synchronized (_aliases) {
  406               for (Iterator iter = _aliases.entrySet().iterator();
  407                   iter.hasNext(); ) {
  408                   Map.Entry e = (Map.Entry) iter.next();
  409                   if (e.getValue() != null)
  410                       aliases.add(e.getKey());
  411               }
  412           }
  413           return aliases;
  414       }
  415   
  416       /**
  417        * Internal method to get the metadata for the given class, without
  418        * resolving it.
  419        */
  420       private ClassMetaData getMetaDataInternal(Class cls,
  421           ClassLoader envLoader) {
  422           if (cls == null)
  423               return null;
  424   
  425           // check cache for existing metadata, or give up if no metadata and
  426           // our list of configured persistent types doesn't include the class
  427           ClassMetaData meta = (ClassMetaData) _metas.get(cls);
  428           if (meta != null && ((meta.getSourceMode() & MODE_META) != 0
  429               || (_sourceMode & MODE_META) == 0))
  430               return meta;
  431   
  432           // if runtime, cut off search if not in pc list.  we don't do this at
  433           // dev time so that user can manipulate persistent classes he's writing
  434           // before adding them to the list
  435           if ((_validate & VALIDATE_RUNTIME) != 0) {
  436               Set pcNames = getPersistentTypeNames(false, envLoader);
  437               if (pcNames != null && !pcNames.contains(cls.getName()))
  438                   return meta;
  439           }
  440   
  441           if (meta == null) {
  442               // check to see if maybe we know this class has no metadata
  443               if (_metas.containsKey(cls))
  444                   return null;
  445   
  446               // make sure this isn't an obviously bad class
  447               if (cls.isPrimitive() || cls.getName().startsWith("java.")
  448                   || cls == PersistenceCapable.class)
  449                   return null;
  450   
  451               // designed to get around jikes 1.17 / JDK1.5 issue where static
  452               // initializers are not invoked when a class is referenced, so the
  453               // class never registers itself with the system
  454               if ((_validate & VALIDATE_RUNTIME) != 0) {
  455                   try {
  456                       Class.forName(cls.getName(), true,
  457                           (ClassLoader) AccessController.doPrivileged(
  458                               J2DoPrivHelper.getClassLoaderAction(cls)));
  459                   } catch (Throwable t) {
  460                   }
  461               }
  462           }
  463   
  464           // not in cache: load metadata or mappings depending on source mode.
  465           // loading metadata might also load mappings, but doesn't have to
  466           int mode = 0;
  467           if ((_sourceMode & MODE_META) != 0)
  468               mode = _sourceMode & ~MODE_MAPPING;
  469           else if ((_sourceMode & MODE_MAPPING) == 0)
  470               mode = _sourceMode;
  471           if (mode != MODE_NONE) {
  472               if (_log.isTraceEnabled())
  473                   _log.trace(_loc.get("load-cls", cls, toModeString(mode)));
  474               _factory.load(cls, mode, envLoader);
  475           }
  476   
  477           // check cache again
  478           if (meta == null)
  479               meta = (ClassMetaData) _metas.get(cls);
  480           if (meta != null && ((meta.getSourceMode() & MODE_META) != 0
  481               || (_sourceMode & MODE_META) == 0))
  482               return meta;
  483   
  484           // record that this class has no metadata; checking for this later
  485           // speeds things up in environments with slow class loading
  486           // like appservers
  487           if (meta != null)
  488               removeMetaData(meta);
  489           _metas.put(cls, null);
  490           return null;
  491       }
  492   
  493       /**
  494        * Return a string representation of the given mode flags.
  495        */
  496       private static String toModeString(int mode) {
  497           StringBuffer buf = new StringBuffer(31);
  498           if ((mode & MODE_META) != 0)
  499               buf.append("[META]");
  500           if ((mode & MODE_QUERY) != 0)
  501               buf.append("[QUERY]");
  502           if ((mode & MODE_MAPPING) != 0)
  503               buf.append("[MAPPING]");
  504           if ((mode & MODE_MAPPING_INIT) != 0)
  505               buf.append("[MAPPING_INIT]");
  506           return buf.toString();
  507       }
  508   
  509       /**
  510        * Prepare metadata for mapping resolution. This method might map parts
  511        * of the metadata that don't rely on other classes being mapped, but that
  512        * other classes might rely on during their own mapping (for example,
  513        * primary key fields). By default, this method only calls
  514        * {@link ClassMetaData#defineSuperclassFields}.
  515        */
  516       protected void prepareMapping(ClassMetaData meta) {
  517           meta.defineSuperclassFields(false);
  518       }
  519   
  520       /**
  521        * Resolve the given metadata if needed. There are three goals:
  522        * <ol>
  523        * <li>Make sure no unresolved metadata gets back to the client.</li>
  524        * <li>Avoid infinite reentrant calls for mutually-dependent metadatas by
  525        * allowing unresolved metadata to be returned to other metadatas.</li>
  526        * <li>Always make sure the superclass metadata is resolved before the
  527        * subclass metadata so that the subclass can access the super's list
  528        * of fields.</li>
  529        * </ol> Note that the code calling this method is synchronized, so this
  530        * method doesn't have to be.
  531        */
  532       private void resolve(ClassMetaData meta) {
  533           // return anything that has its metadata resolved, because that means
  534           // it is either fully resolved or must at least be in the process of
  535           // resolving mapping, etc since we do that right after meta resolve
  536           if (meta == null || _resMode == MODE_NONE
  537               || (meta.getResolve() & MODE_META) != 0)
  538               return;
  539   
  540           // resolve metadata
  541           List resolved = resolveMeta(meta);
  542           if (resolved == null)
  543               return;
  544   
  545           // load mapping data
  546           for (int i = 0; i < resolved.size(); i++)
  547               loadMapping((ClassMetaData) resolved.get(i));
  548           for (int i = 0; i < resolved.size(); i++)
  549               preMapping((ClassMetaData) resolved.get(i));
  550   
  551           // resolve mappings
  552           boolean err = true;
  553           if ((_resMode & MODE_MAPPING) != 0)
  554               for (int i = 0; i < resolved.size(); i++)
  555                   err &= resolveMapping((ClassMetaData) resolved.get(i));
  556   
  557           // throw errors encountered
  558           if (err && !_errs.isEmpty()) {
  559               RuntimeException re;
  560               if (_errs.size() == 1)
  561                   re = (RuntimeException) _errs.get(0);
  562               else
  563                   re = new MetaDataException(_loc.get("resolve-errs")).
  564                       setNestedThrowables((Throwable[]) _errs.toArray
  565                           (new Exception[_errs.size()]));
  566               _errs.clear();
  567               throw re;
  568           }
  569       }
  570   
  571       /**
  572        * Resolve metadata mode, returning list of processed metadadatas, or null
  573        * if we're still in the process of resolving other metadatas.
  574        */
  575       private List resolveMeta(ClassMetaData meta) {
  576           if (meta.getPCSuperclass() == null) {
  577               // set superclass
  578               Class sup = meta.getDescribedType().getSuperclass();
  579               ClassMetaData supMeta;
  580               while (sup != null && sup != Object.class) {
  581                   supMeta = getMetaData(sup, meta.getEnvClassLoader(), false);
  582                   if (supMeta != null) {
  583                       meta.setPCSuperclass(sup);
  584                       meta.setPCSuperclassMetaData(supMeta);
  585                       break;
  586                   } else
  587                       sup = sup.getSuperclass();
  588               }
  589               if (meta.getDescribedType().isInterface()) {
  590                   Class[] sups = meta.getDescribedType().getInterfaces();
  591                   for (int i = 0; i < sups.length; i++) {
  592                       supMeta = getMetaData(sups[i], meta.getEnvClassLoader(), 
  593                           false);
  594                       if (supMeta != null) {
  595                           meta.setPCSuperclass(sup);
  596                           meta.setPCSuperclassMetaData(supMeta);
  597                           break;
  598                       }
  599                   }
  600               }
  601               if (_log.isTraceEnabled())
  602                   _log.trace(_loc.get("assigned-sup", meta,
  603                       meta.getPCSuperclass()));
  604           }
  605   
  606           // resolve relation primary key fields for mapping dependencies
  607           FieldMetaData[] fmds = meta.getDeclaredFields();
  608           for (int i = 0; i < fmds.length; i++)
  609               if (fmds[i].isPrimaryKey())
  610                   getMetaData(fmds[i].getDeclaredType(), 
  611                       meta.getEnvClassLoader(), false);
  612   
  613           // resolve metadata; if we're not in the process of resolving
  614           // others, this will return the set of interrelated metas that
  615           // resolved
  616           return processBuffer(meta, _resolving, MODE_META);
  617       }
  618       
  619       /**
  620        * Load mapping information for the given metadata.
  621        */
  622       private void loadMapping(ClassMetaData meta) {
  623           if ((meta.getResolve() & MODE_MAPPING) != 0)
  624               return;
  625   
  626           // load mapping information
  627           if ((meta.getSourceMode() & MODE_MAPPING) == 0
  628               && (_sourceMode & MODE_MAPPING) != 0) {
  629               // embedded-only metadata doesn't have mapping, so always loaded
  630               if (meta.isEmbeddedOnly())
  631                   meta.setSourceMode(MODE_MAPPING, true);
  632               else {
  633                   // load mapping data
  634                   int mode = _sourceMode & ~MODE_META;
  635                   if (_log.isTraceEnabled())
  636                       _log.trace(_loc.get("load-mapping", meta,
  637                           toModeString(mode)));
  638                   try {
  639                       _factory.load(meta.getDescribedType(), mode,
  640                           meta.getEnvClassLoader());
  641                   } catch (RuntimeException re) {
  642                       removeMetaData(meta);
  643                       _errs.add(re);
  644                   }
  645               }
  646           }
  647       }
  648   
  649       /**
  650        * Pre-mapping preparation.
  651        */
  652       private void preMapping(ClassMetaData meta) {
  653           if ((meta.getResolve() & MODE_MAPPING) != 0)
  654               return;
  655   
  656           // prepare mappings for resolve; if not resolving mappings, then
  657           // make sure any superclass fields defined in metadata are resolved
  658           try {
  659               if ((_resMode & MODE_MAPPING) != 0) {
  660                   if (_log.isTraceEnabled())
  661                       _log.trace(_loc.get("prep-mapping", meta));
  662                   prepareMapping(meta);
  663               } else
  664                   meta.defineSuperclassFields(false);
  665           } catch (RuntimeException re) {
  666               removeMetaData(meta);
  667               _errs.add(re);
  668           }
  669       }
  670   
  671       /**
  672        * Resolve and initialize mapping.
  673        *
  674        * @return false if we're still in the process of resolving mappings
  675        */
  676       private boolean resolveMapping(ClassMetaData meta) {
  677           List mapped = processBuffer(meta, _mapping, MODE_MAPPING);
  678           if (mapped == null)
  679               return false;
  680   
  681           // initialize mapping for runtime use
  682           if ((_resMode & MODE_MAPPING_INIT) != 0) {
  683               for (int i = 0; i < mapped.size(); i++) {
  684                   meta = (ClassMetaData) mapped.get(i);
  685                   try {
  686                       meta.resolve(MODE_MAPPING_INIT);
  687                   } catch (RuntimeException re) {
  688                       removeMetaData(meta);
  689                       _errs.add(re);
  690                   }
  691               }
  692           }
  693           return true;
  694       }
  695   
  696       /**
  697        * Process the given metadata and the associated buffer.
  698        */
  699       private List processBuffer(ClassMetaData meta,
  700           InheritanceOrderedMetaDataList buffer, int mode) {
  701           // if we're already processing a metadata, just buffer this one; when
  702           // the initial metadata finishes processing, we traverse the buffer
  703           // and process all the others that were introduced during reentrant
  704           // calls
  705           if (!buffer.add(meta) || buffer.size() != 1)
  706               return null;
  707   
  708           // continually pop a metadata and process it until we run out; note
  709           // that each processing call might place more metas in the buffer as
  710           // one class tries to access metadata for another; also note that the
  711           // buffer orders itself from least to most derived
  712           ClassMetaData buffered;
  713           List processed = new ArrayList(5);
  714           while (!buffer.isEmpty()) {
  715               buffered = buffer.peek();
  716               try {
  717                   buffered.resolve(mode);
  718                   processed.add(buffered);
  719                   buffer.remove(buffered);
  720               } catch (RuntimeException re) {
  721                   _errs.add(re);
  722   
  723                   // any exception during resolution of one type means we can't
  724                   // resolve any of the related types, so clear buffer.  this also
  725                   // ensures that if two types relate to each other and one
  726                   // dies, we don't get into infinite cycles
  727                   for (Iterator itr = buffer.iterator(); itr.hasNext();) {
  728                       meta = (ClassMetaData) itr.next();
  729                       removeMetaData(meta);
  730                       if (meta != buffered) {
  731                           _errs.add(new MetaDataException(_loc.get
  732                               ("prev-errs", meta, buffered)));
  733                       }
  734                   }
  735                   buffer.clear();
  736               }
  737           }
  738           return processed;
  739       }
  740   
  741       /**
  742        * Return all the metadata instances currently in the repository.
  743        */
  744       public synchronized ClassMetaData[] getMetaDatas() {
  745           // prevent concurrent mod errors when resolving one metadata
  746           // introduces others
  747           ClassMetaData[] metas = (ClassMetaData[]) _metas.values().
  748               toArray(new ClassMetaData[_metas.size()]);
  749           for (int i = 0; i < metas.length; i++)
  750               if (metas[i] != null)
  751                   getMetaData(metas[i].getDescribedType(),
  752                       metas[i].getEnvClassLoader(), true);
  753   
  754           List resolved = new ArrayList(_metas.size());
  755           ClassMetaData meta;
  756           for (Iterator itr = _metas.values().iterator(); itr.hasNext();) {
  757               meta = (ClassMetaData) itr.next();
  758               if (meta != null)
  759                   resolved.add(meta);
  760           }
  761           metas = (ClassMetaData[]) resolved.toArray
  762               (newClassMetaDataArray(resolved.size()));
  763           Arrays.sort(metas);
  764           return metas;
  765       }
  766   
  767       /**
  768        * Return the cached metadata for the given class, without any resolution.
  769        * Return null if none.
  770        */
  771       public ClassMetaData getCachedMetaData(Class cls) {
  772           return (ClassMetaData) _metas.get(cls);
  773       }
  774       
  775       /**
  776        * Create a new metadata, populate it with default information, add it to
  777        * the repository, and return it. Use the default access type.
  778        */
  779       public ClassMetaData addMetaData(Class cls) {
  780           return addMetaData(cls, ClassMetaData.ACCESS_UNKNOWN);
  781       }
  782   
  783       /**
  784        * Create a new metadata, populate it with default information, add it to
  785        * the repository, and return it.
  786        *
  787        * @param access the access type to use in populating metadata
  788        */
  789       public ClassMetaData addMetaData(Class cls, int access) {
  790           if (cls == null || cls.isPrimitive())
  791               return null;
  792   
  793           ClassMetaData meta = newClassMetaData(cls);
  794           _factory.getDefaults().populate(meta, access);
  795   
  796           // synchronize on this rather than the map, because all other methods
  797           // that access _metas are synchronized on this
  798           synchronized (this) {
  799               if (_pawares.containsKey(cls))
  800                   throw new MetaDataException(_loc.get("pc-and-aware", cls));
  801               _metas.put(cls, meta);
  802           }
  803           return meta;
  804       }
  805       
  806       /**
  807        * Create a new class metadata instance.
  808        */
  809       protected ClassMetaData newClassMetaData(Class type) {
  810           return new ClassMetaData(type, this);
  811       }
  812   
  813       /**
  814        * Create a new array of the proper class metadata subclass.
  815        */
  816       protected ClassMetaData[] newClassMetaDataArray(int length) {
  817           return new ClassMetaData[length];
  818       }
  819   
  820       /**
  821        * Create a new field metadata instance.
  822        */
  823       protected FieldMetaData newFieldMetaData(String name, Class type,
  824           ClassMetaData owner) {
  825           return new FieldMetaData(name, type, owner);
  826       }
  827   
  828       /**
  829        * Create a new array of the proper field metadata subclass.
  830        */
  831       protected FieldMetaData[] newFieldMetaDataArray(int length) {
  832           return new FieldMetaData[length];
  833       }
  834   
  835       /**
  836        * Create a new array of the proper xml class metadata subclass.
  837        */
  838       protected XMLMetaData[] newXMLClassMetaDataArray(int length) {
  839           return new XMLClassMetaData[length];
  840       }
  841   
  842       /**
  843        * Create a new embedded class metadata instance.
  844        */
  845       protected ClassMetaData newEmbeddedClassMetaData(ValueMetaData owner) {
  846           return new ClassMetaData(owner);
  847       }
  848   
  849       /**
  850        * Create a new value metadata instance.
  851        */
  852       protected ValueMetaData newValueMetaData(FieldMetaData owner) {
  853           return new ValueMetaDataImpl(owner);
  854       }
  855   
  856       /**
  857        * Create an {@link Order} for the given field and declaration. This
  858        * method delegates to {@link #newRelatedFieldOrder} and
  859        * {@link #newValueFieldOrder} by default.
  860        */
  861       protected Order newOrder(FieldMetaData owner, String name, boolean asc) {
  862           // paths can start with (or equal) '#element'
  863           if (name.startsWith(Order.ELEMENT))
  864               name = name.substring(Order.ELEMENT.length());
  865           if (name.length() == 0)
  866               return newValueOrder(owner, asc);
  867   
  868           // next token should be '.'
  869           if (name.charAt(0) == '.')
  870               name = name.substring(1);
  871   
  872           // related field
  873           ClassMetaData meta = owner.getElement().getTypeMetaData();
  874           if (meta == null)
  875               throw new MetaDataException(_loc.get("nonpc-field-orderable",
  876                   owner, name));
  877           FieldMetaData rel = meta.getField(name);
  878           if (rel == null)
  879               throw new MetaDataException(_loc.get("bad-field-orderable",
  880                   owner, name));
  881           return newRelatedFieldOrder(owner, rel, asc);
  882       }
  883   
  884       /**
  885        * Order by the field value.
  886        */
  887       protected Order newValueOrder(FieldMetaData owner, boolean asc) {
  888           return new InMemoryValueOrder(asc, getConfiguration());
  889       }
  890   
  891       /**
  892        * Order by a field of the related type.
  893        */
  894       protected Order newRelatedFieldOrder(FieldMetaData owner,
  895           FieldMetaData rel, boolean asc) {
  896           return new InMemoryRelatedFieldOrder(rel, asc, getConfiguration());
  897       }
  898   
  899       /**
  900        * Create an array of orders of the given size.
  901        */
  902       protected Order[] newOrderArray(int size) {
  903           return new Order[size];
  904       }
  905   
  906       /**
  907        * Remove a metadata instance from the repository.
  908        *
  909        * @return true if removed, false if not in this repository
  910        */
  911       public boolean removeMetaData(ClassMetaData meta) {
  912           if (meta == null)
  913               return false;
  914           return removeMetaData(meta.getDescribedType());
  915       }
  916   
  917       /**
  918        * Remove a metadata instance from the repository.
  919        *
  920        * @return true if removed, false if not in this repository
  921        */
  922       public synchronized boolean removeMetaData(Class cls) {
  923           if (cls == null)
  924               return false;
  925           if (_metas.remove(cls) != null) {
  926               Class impl = (Class) _ifaces.remove(cls);
  927               if (impl != null)
  928                   _metas.remove(impl);
  929               return true;
  930           }
  931           return false;
  932       }
  933   
  934       /**
  935        * Add the given metadata as declared interface implementation.
  936        */
  937       void addDeclaredInterfaceImpl(ClassMetaData meta, Class iface) {
  938           synchronized (_impls) {
  939               Collection vals = (Collection) _impls.get(iface);
  940               
  941               // check to see if the superclass already declares to avoid dups
  942               if (vals != null) {
  943                   ClassMetaData sup = meta.getPCSuperclassMetaData();
  944                   for (; sup != null; sup = sup.getPCSuperclassMetaData())
  945                       if (vals.contains(sup.getDescribedType()))
  946                           return;
  947               }
  948               addToCollection(_impls, iface, meta.getDescribedType(), false);
  949           }
  950       }
  951   
  952       /**
  953        * Set the implementation for the given managed interface.
  954        */
  955       synchronized void setInterfaceImpl(ClassMetaData meta, Class impl) {
  956           if (!meta.isManagedInterface())
  957               throw new MetaDataException(_loc.get("not-managed-interface", 
  958                   meta, impl));
  959           _ifaces.put(meta.getDescribedType(), impl);
  960           addDeclaredInterfaceImpl(meta, meta.getDescribedType());
  961           ClassMetaData sup = meta.getPCSuperclassMetaData();
  962           while (sup != null) {
  963               // record superclass interface info while we can as well as we
  964               // will only register concrete superclass in PCRegistry
  965               sup.clearSubclassCache();
  966               addToCollection(_subs, sup.getDescribedType(), impl, true);
  967               sup = (ClassMetaData) sup.getPCSuperclassMetaData();
  968           }
  969       }
  970       
  971       InterfaceImplGenerator getImplGenerator() {
  972           return _implGen;
  973       }
  974   
  975       /**
  976        * Return the least-derived class metadata for the given application
  977        * identity object.
  978        *
  979        * @param oid the oid to get the metadata for
  980        * @param envLoader the environmental class loader, if any
  981        * @param mustExist if true, throws a {@link MetaDataException}
  982        * if no metadata is found
  983        */
  984       public ClassMetaData getMetaData(Object oid, ClassLoader envLoader,
  985           boolean mustExist) {
  986           if (oid == null && mustExist)
  987               throw new MetaDataException(_loc.get("no-oid-meta", oid, "?",
  988                   _oids.toString()));
  989           if (oid == null)
  990               return null;
  991   
  992           if (oid instanceof OpenJPAId) {
  993               Class cls = ((OpenJPAId) oid).getType();
  994               return getMetaData(cls, envLoader, mustExist);
  995           }
  996   
  997           // check cache
  998           processRegisteredClasses(envLoader);
  999           Class cls = (Class) _oids.get(oid.getClass());
 1000           if (cls != null)
 1001               return getMetaData(cls, envLoader, mustExist);
 1002   
 1003           // maybe this is some type we've seen but just isn't valid
 1004           if (_oids.containsKey(oid.getClass())) {
 1005               if (mustExist)
 1006                   throw new MetaDataException(_loc.get("no-oid-meta", oid,
 1007                       oid.getClass(), _oids));
 1008               return null;
 1009           }
 1010   
 1011           // if still not match, register any classes that look similar to the
 1012           // oid class and check again
 1013           resolveIdentityClass(oid);
 1014           if (processRegisteredClasses(envLoader).length > 0) {
 1015               cls = (Class) _oids.get(oid.getClass());
 1016               if (cls != null)
 1017                   return getMetaData(cls, envLoader, mustExist);
 1018           }
 1019   
 1020           // record that this is an invalid type
 1021           _oids.put(oid.getClass(), null);
 1022   
 1023           if (!mustExist)
 1024               return null;
 1025           throw new MetaDataException(_loc.get("no-oid-meta", oid,
 1026               oid.getClass(), _oids)).setFailedObject(oid);
 1027       }
 1028   
 1029       /**
 1030        * Make some guesses about the name of a target class for an
 1031        * unknown application identity class.
 1032        */
 1033       private void resolveIdentityClass(Object oid) {
 1034           if (oid == null)
 1035               return;
 1036   
 1037           Class oidClass = oid.getClass();
 1038           if (_log.isTraceEnabled())
 1039               _log.trace(_loc.get("resolve-identity", oidClass));
 1040   
 1041           ClassLoader cl = (ClassLoader) AccessController.doPrivileged(
 1042               J2DoPrivHelper.getClassLoaderAction(oidClass)); 
 1043           String className;
 1044           while (oidClass != null && oidClass != Object.class) {
 1045               className = oidClass.getName();
 1046   
 1047               // we take a brute-force approach: try to load all the class'
 1048               // substrings. this will handle the following common naming cases:
 1049               //
 1050               //   com.company.MyClass$ID	-> com.company.MyClass
 1051               //   com.company.MyClassId	-> com.company.MyClass
 1052               //   com.company.MyClassOid	-> com.company.MyClass
 1053               //   com.company.MyClassPK	-> com.company.MyClass
 1054               //
 1055               // this isn't the fastest thing possible, but this method will
 1056               // only be called once per JVM per unknown app id class
 1057               for (int i = className.length(); i > 1; i--) {
 1058                   if (className.charAt(i - 1) == '.')
 1059                       break;
 1060   
 1061                   try {
 1062                       Class.forName(className.substring(0, i), true, cl);
 1063                   } catch (Exception e) {
 1064                   } // consume all exceptions
 1065               }
 1066   
 1067               // move up the OID hierarchy
 1068               oidClass = oidClass.getSuperclass();
 1069           }
 1070       }
 1071   
 1072       /**
 1073        * Return all least-derived metadatas with some mapped assignable type that
 1074        * implement the given class.
 1075        *
 1076        * @param cls the class or interface to retrieve implementors for
 1077        * @param envLoader the environmental class loader, if any
 1078        * @param mustExist if true, throws a {@link MetaDataException}
 1079        * if no metadata is found
 1080        */
 1081       public ClassMetaData[] getImplementorMetaDatas(Class cls,
 1082           ClassLoader envLoader, boolean mustExist) {
 1083           if (cls == null && mustExist)
 1084               throw new MetaDataException(_loc.get("no-meta", cls));
 1085           if (cls == null)
 1086               return EMPTY_METAS;
 1087   
 1088           // get impls of given interface / abstract class
 1089           loadRegisteredClassMetaData(envLoader);
 1090           Collection vals = (Collection) _impls.get(cls);
 1091           ClassMetaData meta;
 1092           Collection mapped = null;
 1093           if (vals != null) {
 1094               synchronized (vals) {
 1095                   for (Iterator itr = vals.iterator(); itr.hasNext();) {
 1096                       meta = getMetaData((Class) itr.next(), envLoader, true);
 1097                       if (meta.isMapped()
 1098                           || meta.getMappedPCSubclassMetaDatas().length > 0) {
 1099                           if (mapped == null)
 1100                               mapped = new ArrayList(vals.size());
 1101                           mapped.add(meta);
 1102                       }
 1103                   }
 1104               }
 1105           }
 1106   
 1107           if (mapped == null && mustExist)
 1108               throw new MetaDataException(_loc.get("no-meta", cls));
 1109           if (mapped == null)
 1110               return EMPTY_METAS;
 1111           return (ClassMetaData[]) mapped.toArray(newClassMetaDataArray
 1112               (mapped.size()));
 1113       }
 1114        
 1115       /**
 1116        * Gets the metadata corresponding to the given persistence-aware class. 
 1117        * Returns null, if the given class is not registered as 
 1118        * persistence-aware.
 1119        */
 1120       public NonPersistentMetaData getPersistenceAware(Class cls) {
 1121       	return (NonPersistentMetaData)_pawares.get(cls);
 1122       }
 1123       
 1124       /**
 1125        * Gets all the metadatas for persistence-aware classes
 1126        * 
 1127        * @return empty array if no class has been registered as pers-aware
 1128        */
 1129       public NonPersistentMetaData[] getPersistenceAwares() {
 1130           synchronized (_pawares) {
 1131               if (_pawares.isEmpty())
 1132                   return EMPTY_NON_PERSISTENT;
 1133               return (NonPersistentMetaData[])_pawares.values().toArray
 1134                   (new NonPersistentMetaData[_pawares.size()]);
 1135           }
 1136       }
 1137   
 1138       /**
 1139        * Add the given class as persistence-aware.
 1140        * 
 1141        * @param cls non-null and must not alreaddy be added as persitence-capable
 1142        */
 1143       public NonPersistentMetaData addPersistenceAware(Class cls) {
 1144       	if (cls == null)
 1145       		return null;
 1146           synchronized(this) {
 1147               if (_pawares.containsKey(cls))
 1148                   return (NonPersistentMetaData)_pawares.get(cls);
 1149               if (getCachedMetaData(cls) != null)
 1150                   throw new MetaDataException(_loc.get("pc-and-aware", cls));
 1151               NonPersistentMetaData meta = new NonPersistentMetaData(cls, this,
 1152                   NonPersistentMetaData.TYPE_PERSISTENCE_AWARE);
 1153               _pawares.put(cls, meta);
 1154               return meta;
 1155       	}
 1156       }
 1157   
 1158       /**
 1159        * Remove a persitence-aware class from the repository
 1160        * 
 1161        * @return true if removed
 1162        */
 1163       public boolean removePersistenceAware(Class cls) {
 1164       	return _pawares.remove(cls) != null;
 1165       }
 1166   
 1167       /**
 1168        * Gets the metadata corresponding to the given non-mapped interface.
 1169        * Returns null, if the given interface is not registered as 
 1170        * persistence-aware.
 1171        */
 1172       public NonPersistentMetaData getNonMappedInterface(Class iface) {
 1173       	return (NonPersistentMetaData)_nonMapped.get(iface);
 1174       }
 1175       
 1176       /**
 1177        * Gets the corresponding metadatas for all registered, non-mapped
 1178        * interfaces
 1179        * 
 1180        * @return empty array if no non-mapped interface has been registered.
 1181        */
 1182       public NonPersistentMetaData[] getNonMappedInterfaces() {
 1183           synchronized (_nonMapped) {
 1184               if (_nonMapped.isEmpty())
 1185                   return EMPTY_NON_PERSISTENT;
 1186               return (NonPersistentMetaData[])_nonMapped.values().toArray
 1187                   (new NonPersistentMetaData[_nonMapped.size()]);
 1188           }
 1189       }
 1190   
 1191       /**
 1192        * Add the given non-mapped interface to the repository.
 1193        * 
 1194        * @param iface the non-mapped interface
 1195        */
 1196       public NonPersistentMetaData addNonMappedInterface(Class iface) {
 1197       	if (iface == null)
 1198       		return null;
 1199           if (!iface.isInterface())
 1200               throw new MetaDataException(_loc.get("not-non-mapped", iface));
 1201           synchronized(this) {
 1202               if (_nonMapped.containsKey(iface))
 1203                   return (NonPersistentMetaData)_nonMapped.get(iface);
 1204               if (getCachedMetaData(iface) != null)
 1205                   throw new MetaDataException(_loc.get("non-mapped-pc", iface));
 1206               NonPersistentMetaData meta = new NonPersistentMetaData(iface, this,
 1207                   NonPersistentMetaData.TYPE_NON_MAPPED_INTERFACE);
 1208               _nonMapped.put(iface, meta);
 1209               return meta;
 1210       	}
 1211       }
 1212   
 1213       /**
 1214        * Remove a non-mapped interface from the repository
 1215        * 
 1216        * @return true if removed
 1217        */
 1218       public boolean removeNonMappedInterface(Class iface) {
 1219       	return _nonMapped.remove(iface) != null;
 1220       }
 1221   
 1222       /**
 1223        * Clear the cache of parsed metadata. This method also clears the
 1224        * internal {@link MetaDataFactory MetaDataFactory}'s cache.
 1225        */
 1226       public synchronized void clear() {
 1227           if (_log.isTraceEnabled())
 1228               _log.trace(_loc.get("clear-repos", this));
 1229   
 1230           _metas.clear();
 1231           _oids.clear();
 1232           _subs.clear();
 1233           _impls.clear();
 1234           _queries.clear();
 1235           _seqs.clear();
 1236           _registered.clear();
 1237           _factory.clear();
 1238           _aliases.clear();
 1239           _pawares.clear();
 1240           _nonMapped.clear();
 1241       }
 1242   
 1243       /**
 1244        * Return the set of configured persistent classes, or null if the user
 1245        * did not configure any.
 1246        *
 1247        * @param devpath if true, search for metadata files in directories
 1248        * in the classpath if no classes are configured explicitly
 1249        * @param envLoader the class loader to use, or null for default
 1250        */
 1251       public synchronized Set getPersistentTypeNames(boolean devpath,
 1252           ClassLoader envLoader) {
 1253           return _factory.getPersistentTypeNames(devpath, envLoader);
 1254       }
 1255   
 1256       /**
 1257        * Load the persistent classes named in configuration.
 1258        * This ensures that all subclasses and application identity classes of
 1259        * each type are known in advance, without having to rely on the
 1260        * application loading the classes before performing operations that
 1261        * might involve them.
 1262        *
 1263        * @param devpath if true, search for metadata files in directories
 1264        * in the classpath if the no classes are configured explicitly
 1265        * @param envLoader the class loader to use, or null for default
 1266        * @return the loaded classes, or empty collection if none
 1267        */
 1268       public synchronized Collection loadPersistentTypes(boolean devpath,
 1269           ClassLoader envLoader) {
 1270           Set names = getPersistentTypeNames(devpath, envLoader);
 1271           if (names == null || names.isEmpty())
 1272               return Collections.EMPTY_LIST;
 1273   
 1274           // attempt to load classes so that they get processed
 1275           ClassLoader clsLoader = _conf.getClassResolverInstance().
 1276               getClassLoader(getClass(), envLoader);
 1277           List classes = new ArrayList(names.size());
 1278           Class cls;
 1279           for (Iterator itr = names.iterator(); itr.hasNext();) {
 1280               cls = classForName((String) itr.next(), clsLoader);
 1281               if (cls != null) {
 1282                   classes.add(cls);
 1283   
 1284                   // if the class is an interface, load its metadata to kick
 1285                   // off the impl generator
 1286                   if (cls.isInterface())
 1287                       getMetaData(cls, clsLoader, false);
 1288               }
 1289           }
 1290           return classes;
 1291       }
 1292   
 1293       /**
 1294        * Return the class for the given name, or null if not loadable.
 1295        */
 1296       private Class classForName(String name, ClassLoader loader) {
 1297           try {
 1298               return Class.forName(name, true, loader);
 1299           } catch (Exception e) {
 1300               if ((_validate & VALIDATE_RUNTIME) != 0) {
 1301                   if (_log.isWarnEnabled())
 1302                       _log.warn(_loc.get("bad-discover-class", name));
 1303               } else if (_log.isInfoEnabled())
 1304                   _log.info(_loc.get("bad-discover-class", name));
 1305               if (_log.isTraceEnabled())
 1306                   _log.trace(e);
 1307           } catch (NoSuchMethodError nsme) {
 1308               if (nsme.getMessage().indexOf(".pc") == -1)
 1309                   throw nsme;
 1310   
 1311               // if the error is about a method that uses the PersistenceCapable
 1312               // 'pc' method prefix, perform some logging and continue. This
 1313               // probably just means that the class is not yet enhanced.
 1314               if ((_validate & VALIDATE_RUNTIME) != 0) {
 1315                   if (_log.isWarnEnabled())
 1316                       _log.warn(_loc.get("bad-discover-class", name));
 1317               } else if (_log.isInfoEnabled())
 1318                   _log.info(_loc.get("bad-discover-class", name));
 1319               if (_log.isTraceEnabled())
 1320                   _log.trace(nsme);
 1321           }
 1322           return null;
 1323       }
 1324   
 1325       /**
 1326        * Return all known subclasses for the given class mapping. Note
 1327        * that this method only works during runtime when the repository is
 1328        * registered as a {@link RegisterClassListener}.
 1329        */
 1330       Collection getPCSubclasses(Class cls) {
 1331           Collection subs = (Collection) _subs.get(cls);
 1332           if (subs == null)
 1333               return Collections.EMPTY_LIST;
 1334           return subs;
 1335       }
 1336   
 1337       ////////////////////////////////////////
 1338       // RegisterClassListener implementation
 1339       ////////////////////////////////////////
 1340   
 1341       public void register(Class cls) {
 1342           // buffer registered classes until an oid metadata request is made,
 1343           // at which point we'll parse everything in the buffer
 1344           synchronized (_registered) {
 1345               _registered.add(cls);
 1346           }
 1347       }
 1348   
 1349       /**
 1350        * Parses the metadata for all registered classes.
 1351        */
 1352       private void loadRegisteredClassMetaData(ClassLoader envLoader) {
 1353           Class[] reg = processRegisteredClasses(envLoader);
 1354           for (int i = 0; i < reg.length; i++) {
 1355               try {
 1356                   getMetaData(reg[i], envLoader, false);
 1357               } catch (MetaDataException me) {
 1358                   if (_log.isWarnEnabled())
 1359                       _log.warn(me);
 1360               }
 1361           }
 1362       }
 1363   
 1364       /**
 1365        * Updates our datastructures with the latest registered classes.
 1366        */
 1367       Class[] processRegisteredClasses(ClassLoader envLoader) {
 1368           if (_registered.isEmpty())
 1369               return EMPTY_CLASSES;
 1370   
 1371           // copy into new collection to avoid concurrent mod errors on reentrant
 1372           // registrations
 1373           Class[] reg;
 1374           synchronized (_registered) {
 1375               reg = (Class[]) _registered.toArray(new Class[_registered.size()]);
 1376               _registered.clear();
 1377           }
 1378   
 1379           Collection pcNames = getPersistentTypeNames(false, envLoader);
 1380           Collection failed = null;
 1381           for (int i = 0; i < reg.length; i++) {
 1382               // don't process types that aren't listed by the user; may belong
 1383               // to a different persistence unit
 1384               if (pcNames != null && !pcNames.isEmpty()
 1385                   && !pcNames.contains(reg[i].getName()))
 1386                   continue;
 1387   
 1388               try {
 1389                   processRegisteredClass(reg[i]);
 1390               } catch (Throwable t) {
 1391                   if (!_conf.getRetryClassRegistration())
 1392                       throw new MetaDataException(_loc.get("error-registered",
 1393                           reg[i]), t);
 1394   
 1395                   if (_log.isWarnEnabled())
 1396                       _log.warn(_loc.get("failed-registered", reg[i]), t);
 1397                   if (failed == null)
 1398                       failed = new ArrayList();
 1399                   failed.add(reg[i]);
 1400               }
 1401           }
 1402           if (failed != null) {
 1403               synchronized (_registered) {
 1404                   _registered.addAll(failed);
 1405               }
 1406           }
 1407           return reg;
 1408       }
 1409   
 1410       /**
 1411        * Updates our datastructures with the given registered class.
 1412        * Relies on the fact that a child class cannot register itself without
 1413        * also registering its parent class by specifying its persistence
 1414        * capable superclass in the registration event.
 1415        */
 1416       private void processRegisteredClass(Class cls) {
 1417           if (_log.isTraceEnabled())
 1418               _log.trace(_loc.get("process-registered", cls));
 1419   
 1420           // update subclass lists; synchronize on this because accessing _metas
 1421           // requires it
 1422           Class leastDerived = cls;
 1423           synchronized (this) {
 1424               ClassMetaData meta;
 1425               for (Class anc = cls;
 1426                   (anc = PCRegistry.getPersistentSuperclass(anc)) != null;) {
 1427                   addToCollection(_subs, anc, cls, true);
 1428                   meta = (ClassMetaData) _metas.get(anc);
 1429                   if (meta != null)
 1430                       meta.clearSubclassCache();
 1431                   leastDerived = anc;
 1432               }
 1433           }
 1434   
 1435           // update oid mappings if this is a base concrete class
 1436           Object oid = null;
 1437           try {
 1438               oid = PCRegistry.newObjectId(cls);
 1439           } catch (InternalException ie) {
 1440               // thrown for single field identity with null pk field value
 1441           }
 1442           if (oid != null) {
 1443               Class existing = (Class) _oids.get(oid.getClass());
 1444               if (existing != null) {
 1445                   // if there is already a class for this OID, then we know
 1446                   // that multiple classes are using the same OID: therefore,
 1447                   // put the least derived PC superclass into the map. This
 1448                   // gets around the problem of an abstract PC superclass
 1449                   // using application identity (since newObjectId
 1450                   // will return null for abstract classes).
 1451                   Class sup = cls;
 1452                   while (PCRegistry.getPersistentSuperclass(sup) != null)
 1453                       sup = PCRegistry.getPersistentSuperclass(sup);
 1454   
 1455                   _oids.put(oid.getClass(), sup);
 1456               } else if (existing == null || cls.isAssignableFrom(existing))
 1457                   _oids.put(oid.getClass(), cls);
 1458           }
 1459   
 1460           // update mappings from interfaces and non-pc superclasses to
 1461           // pc implementing types
 1462           synchronized (_impls) {
 1463               updateImpls(cls, leastDerived, cls);
 1464           }
 1465   
 1466           // set alias for class
 1467           String alias = PCRegistry.getTypeAlias(cls);
 1468           if (alias != null) {
 1469               synchronized (_aliases) {
 1470                   List classList = (List) _aliases.get(alias);
 1471                   if (classList == null) {
 1472                       classList = new ArrayList(3);
 1473                       _aliases.put(alias, classList);
 1474                   }
 1475                   if (!classList.contains(cls))
 1476                       classList.add(cls);
 1477               }
 1478           }
 1479       }
 1480   
 1481       /**
 1482        * Update the list of implementations of base classes and interfaces.
 1483        */
 1484       private void updateImpls(Class cls, Class leastDerived, Class check) {
 1485           // allow users to query on common non-pc superclasses
 1486           Class sup = check.getSuperclass();
 1487           if (leastDerived == cls && sup != null && sup != Object.class) {
 1488               addToCollection(_impls, sup, cls, false);
 1489               updateImpls(cls, leastDerived, sup);
 1490           }
 1491   
 1492           // allow users to query on any implemented interfaces unless defaults 
 1493           // say the user must implement persistent interfaces explicitly in meta
 1494           if (!_factory.getDefaults().isDeclaredInterfacePersistent())
 1495               return;
 1496           Class[] ints = check.getInterfaces();
 1497           for (int i = 0; i < ints.length; i++) {
 1498               // don't map java-standard interfaces
 1499               if (ints[i].getName().startsWith("java."))
 1500                   continue;
 1501   
 1502               // only map least-derived interface implementors
 1503               if (leastDerived == cls || isLeastDerivedImpl(ints[i], cls)) {
 1504                   addToCollection(_impls, ints[i], cls, false);
 1505                   updateImpls(cls, leastDerived, ints[i]);
 1506               }
 1507           }
 1508       }
 1509   
 1510       /**
 1511        * Return true if the given class is the least-derived persistent
 1512        * implementor of the given interface, false otherwise.
 1513        */
 1514       private boolean isLeastDerivedImpl(Class inter, Class cls) {
 1515           Class parent = PCRegistry.getPersistentSuperclass(cls);
 1516           while (parent != null) {
 1517               if (Arrays.asList(parent.getInterfaces()).contains(inter))
 1518                   return false;
 1519               parent = PCRegistry.getPersistentSuperclass(parent);
 1520           }
 1521           return true;
 1522       }
 1523   
 1524       /**
 1525        * Add the given value to the collection cached in the given map under
 1526        * the given key.
 1527        */
 1528       private void addToCollection(Map map, Class key, Class value,
 1529           boolean inheritance) {
 1530           synchronized (map) {
 1531               Collection coll = (Collection) map.get(key);
 1532               if (coll == null) {
 1533                   if (inheritance) {
 1534                       InheritanceComparator comp = new InheritanceComparator();
 1535                       comp.setBase(key);
 1536                       coll = new TreeSet(comp);
 1537                   } else
 1538                       coll = new LinkedList();
 1539                   map.put(key, coll);
 1540               }
 1541               coll.add(value);
 1542           }
 1543       }
 1544   
 1545       ///////////////////////////////
 1546       // Configurable implementation
 1547       ///////////////////////////////
 1548   
 1549       public void setConfiguration(Configuration conf) {
 1550           _conf = (OpenJPAConfiguration) conf;
 1551           _log = _conf.getLog(OpenJPAConfiguration.LOG_METADATA);
 1552       }
 1553   
 1554       public void startConfiguration() {
 1555       }
 1556   
 1557       public void endConfiguration() {
 1558           initializeMetaDataFactory();
 1559           if (_implGen == null)
 1560               _implGen = new InterfaceImplGenerator(this);
 1561       }
 1562   
 1563       private void initializeMetaDataFactory() {
 1564           if (_factory == null) {
 1565               MetaDataFactory mdf = _conf.newMetaDataFactoryInstance();
 1566               if (mdf == null)
 1567                   throw new MetaDataException(_loc.get("no-metadatafactory"));
 1568               setMetaDataFactory(mdf);
 1569           }
 1570       }
 1571   
 1572       //////////////////
 1573       // Query metadata
 1574       //////////////////
 1575   
 1576       /**
 1577        * Return query metadata for the given class, name, and classloader.
 1578        */
 1579       public synchronized QueryMetaData getQueryMetaData(Class cls, String name,
 1580           ClassLoader envLoader, boolean mustExist) {
 1581           QueryMetaData meta = getQueryMetaDataInternal(cls, name, envLoader);
 1582           if (meta == null) {
 1583               // load all the metadatas for all the known classes so that
 1584               // query names are seen and registered
 1585               resolveAll(envLoader);
 1586               meta = getQueryMetaDataInternal(cls, name, envLoader);
 1587           }
 1588   
 1589           if (meta == null && mustExist) {
 1590               if (cls == null) {
 1591                   throw new MetaDataException(_loc.get
 1592                       ("no-named-query-null-class", 
 1593                           getPersistentTypeNames(false, envLoader), name));
 1594               } else {
 1595                   throw new MetaDataException(_loc.get("no-named-query",
 1596                       cls, name));
 1597               }
 1598           }
 1599   
 1600           return meta;
 1601       }
 1602   
 1603       /** 
 1604        * Resolve all known metadata classes. 
 1605        */
 1606       private void resolveAll(ClassLoader envLoader) {
 1607           Collection types = loadPersistentTypes(false, envLoader);
 1608           for (Iterator i = types.iterator(); i.hasNext(); ) {
 1609               Class c = (Class) i.next();
 1610               getMetaData(c, envLoader, false);
 1611           }
 1612       }
 1613   
 1614       /**
 1615        * Return query metadata for the given class, name, and classloader.
 1616        */
 1617       private QueryMetaData getQueryMetaDataInternal(Class cls, String name,
 1618           ClassLoader envLoader) {
 1619           if (name == null)
 1620               return null;
 1621   
 1622           // check cache
 1623           Object key = getQueryKey(cls, name);
 1624           QueryMetaData qm = (QueryMetaData) _queries.get(key);
 1625           if (qm != null)
 1626               return qm;
 1627   
 1628           // get metadata for class, which will find queries in metadata file
 1629           if (cls != null && getMetaData(cls, envLoader, false) != null) {
 1630               qm = (QueryMetaData) _queries.get(key);
 1631               if (qm != null)
 1632                   return qm;
 1633           }
 1634           if ((_sourceMode & MODE_QUERY) == 0)
 1635               return null;
 1636   
 1637           // see if factory can figure out a scope for this query
 1638           if (cls == null)
 1639               cls = _factory.getQueryScope(name, envLoader);
 1640   
 1641           // not in cache; load
 1642           _factory.load(cls, MODE_QUERY, envLoader);
 1643           return (QueryMetaData) _queries.get(key);
 1644       }
 1645   
 1646       /**
 1647        * Return the cached query metadata.
 1648        */
 1649       public synchronized QueryMetaData[] getQueryMetaDatas() {
 1650           return (QueryMetaData[]) _queries.values().toArray
 1651               (new QueryMetaData[_queries.size()]);
 1652       }
 1653   
 1654       /**
 1655        * Return the cached query metadata for the given name.
 1656        */
 1657       public synchronized QueryMetaData getCachedQueryMetaData(Class cls,
 1658           String name) {
 1659           return (QueryMetaData) _queries.get(getQueryKey(cls, name));
 1660       }
 1661   
 1662       /**
 1663        * Add a new query metadata to the repository and return it.
 1664        */
 1665       public synchronized QueryMetaData addQueryMetaData(Class cls, String name) {
 1666           QueryMetaData meta = newQueryMetaData(cls, name);
 1667           _queries.put(getQueryKey(meta), meta);
 1668           return meta;
 1669       }
 1670   
 1671       /**
 1672        * Create a new query metadata instance.
 1673        */
 1674       protected QueryMetaData newQueryMetaData(Class cls, String name) {
 1675           QueryMetaData meta = new QueryMetaData(name);
 1676           meta.setDefiningType(cls);
 1677           return meta;
 1678       }
 1679   
 1680       /**
 1681        * Remove the given query metadata from the repository.
 1682        */
 1683       public synchronized boolean removeQueryMetaData(QueryMetaData meta) {
 1684           if (meta == null)
 1685               return false;
 1686           return _queries.remove(getQueryKey(meta)) != null;
 1687       }
 1688   
 1689       /**
 1690        * Remove query metadata for the given class name if in the repository.
 1691        */
 1692       public synchronized boolean removeQueryMetaData(Class cls, String name) {
 1693           if (name == null)
 1694               return false;
 1695           return _queries.remove(getQueryKey(cls, name)) != null;
 1696       }
 1697   
 1698       /**
 1699        * Return a unique key for a given QueryMetaData.
 1700        */
 1701       private static Object getQueryKey(QueryMetaData meta) {
 1702           if (meta == null)
 1703               return null;
 1704           return getQueryKey(meta.getDefiningType(), meta.getName());
 1705       }
 1706   
 1707       /**
 1708        * Return a unique key for a given class / name. The class
 1709        * argument can be null.
 1710        */
 1711       protected static Object getQueryKey(Class cls, String name) {
 1712           if (cls == null)
 1713               return name;
 1714           QueryKey key = new QueryKey();
 1715           key.clsName = cls.getName();
 1716           key.name = name;
 1717           return key;
 1718       }
 1719   
 1720       /////////////////////
 1721       // Sequence metadata
 1722       /////////////////////
 1723   
 1724       /**
 1725        * Return sequence metadata for the given name and classloader.
 1726        */
 1727       public synchronized SequenceMetaData getSequenceMetaData(String name,
 1728           ClassLoader envLoader, boolean mustExist) {
 1729           SequenceMetaData meta = getSequenceMetaDataInternal(name, envLoader);
 1730           if (meta == null && SequenceMetaData.NAME_SYSTEM.equals(name)) {
 1731               if (_sysSeq == null)
 1732                   _sysSeq = newSequenceMetaData(name);
 1733               return _sysSeq;
 1734           }
 1735           if (meta == null && mustExist)
 1736               throw new MetaDataException(_loc.get("no-named-sequence", name));
 1737           return meta;
 1738       }
 1739   
 1740       /**
 1741        * Used internally by metadata to retrieve sequence metadatas based on
 1742        * possibly-unqualified sequence name.
 1743        */
 1744       SequenceMetaData getSequenceMetaData(ClassMetaData context, String name,
 1745           boolean mustExist) {
 1746           // try with given name
 1747           MetaDataException e = null;
 1748           try {
 1749               SequenceMetaData seq = getSequenceMetaData(name,
 1750                   context.getEnvClassLoader(), mustExist);
 1751               if (seq != null)
 1752                   return seq;
 1753           } catch (MetaDataException mde) {
 1754               e = mde;
 1755           }
 1756   
 1757           // if given name already fully qualified, give up
 1758           if (name.indexOf('.') != -1) {
 1759               if (e != null)
 1760                   throw e;
 1761               return null;
 1762           }
 1763   
 1764           // try with qualified name
 1765           name = Strings.getPackageName(context.getDescribedType())
 1766               + "." + name;
 1767           try {
 1768               return getSequenceMetaData(name, context.getEnvClassLoader(),
 1769                   mustExist);
 1770           } catch (MetaDataException mde) {
 1771               // throw original exception
 1772               if (e != null)
 1773                   throw e;
 1774               throw mde;
 1775           }
 1776       }
 1777   
 1778       /**
 1779        * Return sequence metadata for the given name and classloader.
 1780        */
 1781       private SequenceMetaData getSequenceMetaDataInternal(String name,
 1782           ClassLoader envLoader) {
 1783           if (name == null)
 1784               return null;
 1785   
 1786           // check cache
 1787           SequenceMetaData meta = (SequenceMetaData) _seqs.get(name);
 1788           if (meta == null) {
 1789               // load metadata for registered classes to hopefully find sequence
 1790               // definition
 1791               loadRegisteredClassMetaData(envLoader);
 1792               meta = (SequenceMetaData) _seqs.get(name);
 1793           }
 1794           return meta;
 1795       }
 1796   
 1797       /**
 1798        * Return the cached sequence metadata.
 1799        */
 1800       public synchronized SequenceMetaData[] getSequenceMetaDatas() {
 1801           return (SequenceMetaData[]) _seqs.values().toArray
 1802               (new SequenceMetaData[_seqs.size()]);
 1803       }
 1804   
 1805       /**
 1806        * Return the cached a sequence metadata for the given name.
 1807        */
 1808       public synchronized SequenceMetaData getCachedSequenceMetaData(
 1809           String name) {
 1810           return (SequenceMetaData) _seqs.get(name);
 1811       }
 1812   
 1813       /**
 1814        * Add a new sequence metadata to the repository and return it.
 1815        */
 1816       public synchronized SequenceMetaData addSequenceMetaData(String name) {
 1817           SequenceMetaData meta = newSequenceMetaData(name);
 1818           _seqs.put(name, meta);
 1819           return meta;
 1820       }
 1821   
 1822       /**
 1823        * Create a new sequence metadata instance.
 1824        */
 1825       protected SequenceMetaData newSequenceMetaData(String name) {
 1826           return new SequenceMetaData(name, this);
 1827       }
 1828   
 1829       /**
 1830        * Remove the given sequence metadata from the repository.
 1831        */
 1832       public synchronized boolean removeSequenceMetaData(SequenceMetaData meta) {
 1833           if (meta == null)
 1834               return false;
 1835           return _seqs.remove(meta.getName()) != null;
 1836       }
 1837   
 1838       /**
 1839        * Remove sequence metadata for the name if in the repository.
 1840        */
 1841       public synchronized boolean removeSequenceMetaData(String name) {
 1842           if (name == null)
 1843               return false;
 1844           return _seqs.remove(name) != null;
 1845       }
 1846   
 1847       /**
 1848        * Add the given system lifecycle listener.
 1849        */
 1850       public synchronized void addSystemListener(Object listener) {
 1851           // copy to avoid issues with ListenerList and avoid unncessary
 1852           // locking on the list during runtime
 1853           LifecycleEventManager.ListenerList listeners = new
 1854               LifecycleEventManager.ListenerList(_listeners);
 1855           listeners.add(listener);
 1856           _listeners = listeners;
 1857       }
 1858   
 1859       /**
 1860        * Remove the given system lifecycle listener.
 1861        */
 1862       public synchronized boolean removeSystemListener(Object listener) {
 1863           if (!_listeners.contains(listener))
 1864               return false;
 1865   
 1866           // copy to avoid issues with ListenerList and avoid unncessary
 1867           // locking on the list during runtime
 1868           LifecycleEventManager.ListenerList listeners = new
 1869               LifecycleEventManager.ListenerList(_listeners);
 1870           listeners.remove(listener);
 1871           _listeners = listeners;
 1872           return true;
 1873       }
 1874   
 1875       /**
 1876        * Return the system lifecycle listeners
 1877        */
 1878       public LifecycleEventManager.ListenerList getSystemListeners() {
 1879           return _listeners;
 1880       }
 1881   
 1882       /**
 1883        * Free the resources used by this repository. Closes all user sequences.
 1884        */
 1885       public synchronized void close() {
 1886           SequenceMetaData[] smds = getSequenceMetaDatas();
 1887           for (int i = 0; i < smds.length; i++)
 1888               smds[i].close();
 1889           clear();
 1890       }
 1891   
 1892       /**
 1893        * Query key struct.
 1894        */
 1895       private static class QueryKey
 1896           implements Serializable {
 1897   
 1898           public String clsName;
 1899           public String name;
 1900   
 1901           public int hashCode() {
 1902               int clsHash = (clsName == null) ? 0 : clsName.hashCode();
 1903               int nameHash = (name == null) ? 0 : name.hashCode();
 1904               return clsHash + nameHash;
 1905           }
 1906   
 1907           public boolean equals(Object obj)
 1908   		{
 1909   			if (obj == this)
 1910   				return true;
 1911   			if (!(obj instanceof QueryKey))
 1912   				return false;
 1913   		
 1914   			QueryKey qk = (QueryKey) obj;
 1915   			return StringUtils.equals (clsName, qk.clsName)
 1916   				&& StringUtils.equals (name, qk.name);	
 1917   		}
 1918   	}
 1919       
 1920       /**
 1921        * Return XML metadata for a given field metadata
 1922        * @param fmd
 1923        * @return XML metadata
 1924        */
 1925       public synchronized XMLMetaData getXMLMetaData(FieldMetaData fmd) {
 1926           Class cls = fmd.getDeclaredType();
 1927           // check if cached before
 1928           XMLMetaData xmlmeta = (XMLClassMetaData) _xmlmetas.get(cls);
 1929           if (xmlmeta != null)
 1930               return xmlmeta;
 1931           
 1932           // load JAXB XML metadata
 1933           _factory.loadXMLMetaData(fmd);
 1934           
 1935           xmlmeta = (XMLClassMetaData) _xmlmetas.get(cls);
 1936   
 1937           return xmlmeta;
 1938       }
 1939   
 1940       /**
 1941        * Create a new metadata, populate it with default information, add it to
 1942        * the repository, and return it.
 1943        *
 1944        * @param access the access type to use in populating metadata
 1945        */
 1946       public XMLClassMetaData addXMLMetaData(Class type, String name) {
 1947           XMLClassMetaData meta = newXMLClassMetaData(type, name);
 1948           
 1949           // synchronize on this rather than the map, because all other methods
 1950           // that access _xmlmetas are synchronized on this
 1951           synchronized (this) {
 1952               _xmlmetas.put(type, meta);
 1953           }
 1954           return meta;
 1955       }
 1956   
 1957       /**
 1958        * Return the cached XMLClassMetaData for the given class
 1959        * Return null if none.
 1960        */
 1961       public XMLMetaData getCachedXMLMetaData(Class cls) {
 1962           return (XMLMetaData) _xmlmetas.get(cls);
 1963       }
 1964       
 1965       /**
 1966        * Create a new xml class metadata
 1967        * @param type
 1968        * @param name
 1969        * @return a XMLClassMetaData
 1970        */
 1971       protected XMLClassMetaData newXMLClassMetaData(Class type, String name) {
 1972           return new XMLClassMetaData(type, name);
 1973       }
 1974       
 1975       /**
 1976        * Create a new xml field meta, add it to the fieldMap in the given 
 1977        *     xml class metadata
 1978        * @param type
 1979        * @param name
 1980        * @param meta
 1981        * @return a XMLFieldMetaData
 1982        */
 1983       public XMLFieldMetaData newXMLFieldMetaData(Class type, String name) {
 1984           return new XMLFieldMetaData(type, name);
 1985       }
 1986   }

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