0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.openjpa.meta;
0020:
0021: import java.io.Serializable;
0022: import java.security.AccessController;
0023: import java.util.ArrayList;
0024: import java.util.Arrays;
0025: import java.util.Collection;
0026: import java.util.Collections;
0027: import java.util.HashMap;
0028: import java.util.HashSet;
0029: import java.util.Iterator;
0030: import java.util.LinkedList;
0031: import java.util.List;
0032: import java.util.Map;
0033: import java.util.Set;
0034: import java.util.SortedSet;
0035: import java.util.TreeSet;
0036:
0037: import org.apache.commons.lang.StringUtils;
0038: import org.apache.openjpa.conf.OpenJPAConfiguration;
0039: import org.apache.openjpa.enhance.DynamicPersistenceCapable;
0040: import org.apache.openjpa.enhance.PCRegistry;
0041: import org.apache.openjpa.enhance.PCRegistry.RegisterClassListener;
0042: import org.apache.openjpa.enhance.PersistenceCapable;
0043: import org.apache.openjpa.event.LifecycleEventManager;
0044: import org.apache.openjpa.lib.conf.Configurable;
0045: import org.apache.openjpa.lib.conf.Configuration;
0046: import org.apache.openjpa.lib.log.Log;
0047: import org.apache.openjpa.lib.util.Closeable;
0048: import org.apache.openjpa.lib.util.J2DoPrivHelper;
0049: import org.apache.openjpa.lib.util.Localizer;
0050: import org.apache.openjpa.lib.util.StringDistance;
0051: import org.apache.openjpa.util.ImplHelper;
0052: import org.apache.openjpa.util.InternalException;
0053: import org.apache.openjpa.util.MetaDataException;
0054: import org.apache.openjpa.util.OpenJPAId;
0055: import serp.util.Strings;
0056:
0057: /**
0058: * Repository of and factory for persistent metadata.
0059: *
0060: * @since 0.3.0
0061: * @author Abe White
0062: * @author Steve Kim (query metadata)
0063: */
0064: public class MetaDataRepository implements
0065: PCRegistry.RegisterClassListener, Configurable, Closeable,
0066: MetaDataModes, Serializable {
0067:
0068: /**
0069: * Constant to not validate any metadata.
0070: */
0071: public static final int VALIDATE_NONE = 0;
0072:
0073: /**
0074: * Bit flag to validate metadata.
0075: */
0076: public static final int VALIDATE_META = 1;
0077:
0078: /**
0079: * Bit flag to validate mappings.
0080: */
0081: public static final int VALIDATE_MAPPING = 2;
0082:
0083: /**
0084: * Bit flag to validate unenhanced metadata only.
0085: */
0086: public static final int VALIDATE_UNENHANCED = 4;
0087:
0088: /**
0089: * Bit flag for runtime validation. Requires that all classes are
0090: * enhanced, and performs extra field resolution steps.
0091: */
0092: public static final int VALIDATE_RUNTIME = 8;
0093:
0094: protected static final Class[] EMPTY_CLASSES = new Class[0];
0095: protected static final NonPersistentMetaData[] EMPTY_NON_PERSISTENT = new NonPersistentMetaData[0];
0096: protected final ClassMetaData[] EMPTY_METAS;
0097: protected final FieldMetaData[] EMPTY_FIELDS;
0098: protected final Order[] EMPTY_ORDERS;
0099:
0100: private static final Localizer _loc = Localizer
0101: .forPackage(MetaDataRepository.class);
0102:
0103: // system sequence
0104: private SequenceMetaData _sysSeq = null;
0105:
0106: // cache of parsed metadata, oid class to class, and interface class
0107: // to metadatas
0108: private final Map _metas = new HashMap();
0109: private final Map _oids = Collections
0110: .synchronizedMap(new HashMap());
0111: private final Map _impls = Collections
0112: .synchronizedMap(new HashMap());
0113: private final Map _ifaces = Collections
0114: .synchronizedMap(new HashMap());
0115: private final Map _queries = new HashMap();
0116: private final Map _seqs = new HashMap();
0117: private final Map _aliases = Collections
0118: .synchronizedMap(new HashMap());
0119: private final Map _pawares = Collections
0120: .synchronizedMap(new HashMap());
0121: private final Map _nonMapped = Collections
0122: .synchronizedMap(new HashMap());
0123:
0124: // map of classes to lists of their subclasses
0125: private final Map _subs = Collections
0126: .synchronizedMap(new HashMap());
0127:
0128: // xml mapping
0129: protected final XMLMetaData[] EMPTY_XMLMETAS;
0130: private final Map _xmlmetas = new HashMap();
0131:
0132: private transient OpenJPAConfiguration _conf = null;
0133: private transient Log _log = null;
0134: private transient InterfaceImplGenerator _implGen = null;
0135: private transient MetaDataFactory _factory = null;
0136:
0137: private int _resMode = MODE_META | MODE_MAPPING;
0138: private int _sourceMode = MODE_META | MODE_MAPPING | MODE_QUERY;
0139: private int _validate = VALIDATE_META | VALIDATE_UNENHANCED;
0140:
0141: // we buffer up any classes that register themselves to prevent
0142: // reentrancy errors if classes register during a current parse (common)
0143: private final Collection _registered = new HashSet();
0144:
0145: // set of metadatas we're in the process of resolving
0146: private final SortedSet _resolving = new TreeSet(
0147: new MetaDataInheritanceComparator());
0148: private final SortedSet _mapping = new TreeSet(
0149: new MetaDataInheritanceComparator());
0150: private final List _errs = new LinkedList();
0151:
0152: // system listeners
0153: private LifecycleEventManager.ListenerList _listeners = new LifecycleEventManager.ListenerList(
0154: 3);
0155:
0156: /**
0157: * Default constructor. Configure via {@link Configurable}.
0158: */
0159: public MetaDataRepository() {
0160: EMPTY_METAS = newClassMetaDataArray(0);
0161: EMPTY_FIELDS = newFieldMetaDataArray(0);
0162: EMPTY_ORDERS = newOrderArray(0);
0163: EMPTY_XMLMETAS = newXMLClassMetaDataArray(0);
0164: }
0165:
0166: /**
0167: * Return the configuration for the repository.
0168: */
0169: public OpenJPAConfiguration getConfiguration() {
0170: return _conf;
0171: }
0172:
0173: /**
0174: * Return the metadata log.
0175: */
0176: public Log getLog() {
0177: return _log;
0178: }
0179:
0180: /**
0181: * The I/O used to load metadata.
0182: */
0183: public MetaDataFactory getMetaDataFactory() {
0184: return _factory;
0185: }
0186:
0187: /**
0188: * The I/O used to load metadata.
0189: */
0190: public void setMetaDataFactory(MetaDataFactory factory) {
0191: factory.setRepository(this );
0192: _factory = factory;
0193: }
0194:
0195: /**
0196: * The metadata validation level. Defaults to
0197: * <code>VALIDATE_META | VALIDATE_UNENHANCED</code>.
0198: */
0199: public int getValidate() {
0200: return _validate;
0201: }
0202:
0203: /**
0204: * The metadata validation level. Defaults to
0205: * <code>VALIDATE_META | VALIDATE_UNENHANCED</code>.
0206: */
0207: public void setValidate(int validate) {
0208: _validate = validate;
0209: }
0210:
0211: /**
0212: * The metadata validation level. Defaults to
0213: * <code>VALIDATE_META | VALIDATE_MAPPING | VALIDATE_UNENHANCED</code>.
0214: */
0215: public void setValidate(int validate, boolean on) {
0216: if (validate == VALIDATE_NONE)
0217: _validate = validate;
0218: else if (on)
0219: _validate |= validate;
0220: else
0221: _validate &= ~validate;
0222: }
0223:
0224: /**
0225: * The metadata resolution mode. Defaults to
0226: * <code>MODE_META | MODE_MAPPING</code>.
0227: */
0228: public int getResolve() {
0229: return _resMode;
0230: }
0231:
0232: /**
0233: * The metadata resolution mode. Defaults to
0234: * <code>MODE_META | MODE_MAPPING</code>.
0235: */
0236: public void setResolve(int mode) {
0237: _resMode = mode;
0238: }
0239:
0240: /**
0241: * The metadata resolution mode. Defaults to
0242: * <code>MODE_META | MODE_MAPPING</code>.
0243: */
0244: public void setResolve(int mode, boolean on) {
0245: if (mode == MODE_NONE)
0246: _resMode = mode;
0247: else if (on)
0248: _resMode |= mode;
0249: else
0250: _resMode &= ~mode;
0251: }
0252:
0253: /**
0254: * The source mode determining what metadata to load. Defaults to
0255: * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
0256: */
0257: public int getSourceMode() {
0258: return _sourceMode;
0259: }
0260:
0261: /**
0262: * The source mode determining what metadata to load. Defaults to
0263: * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
0264: */
0265: public void setSourceMode(int mode) {
0266: _sourceMode = mode;
0267: }
0268:
0269: /**
0270: * The source mode determining what metadata to load. Defaults to
0271: * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
0272: */
0273: public void setSourceMode(int mode, boolean on) {
0274: if (mode == MODE_NONE)
0275: _sourceMode = mode;
0276: else if (on)
0277: _sourceMode |= mode;
0278: else
0279: _sourceMode &= ~mode;
0280: }
0281:
0282: /**
0283: * Return the metadata for the given class.
0284: *
0285: * @param cls the class to retrieve metadata for
0286: * @param envLoader the environmental class loader, if any
0287: * @param mustExist if true, throws a {@link MetaDataException}
0288: * if no metadata is found
0289: */
0290: public synchronized ClassMetaData getMetaData(Class cls,
0291: ClassLoader envLoader, boolean mustExist) {
0292: if (cls != null
0293: && DynamicPersistenceCapable.class
0294: .isAssignableFrom(cls))
0295: cls = cls.getSuperclass();
0296:
0297: // if cls is a generated interface, use the user interface
0298: // to locate metadata
0299: if (cls != null && _implGen.isImplType(cls))
0300: cls = _implGen.toManagedInterface(cls);
0301:
0302: ClassMetaData meta = getMetaDataInternal(cls, envLoader);
0303: if (meta == null && mustExist) {
0304: if (cls != null && !ImplHelper.isManagedType(_conf, cls))
0305: throw new MetaDataException(_loc.get("no-meta-notpc",
0306: cls)).setFatal(false);
0307:
0308: Set pcNames = getPersistentTypeNames(false, envLoader);
0309: if (pcNames != null && pcNames.size() > 0)
0310: throw new MetaDataException(_loc.get("no-meta-types",
0311: cls, pcNames));
0312:
0313: throw new MetaDataException(_loc.get("no-meta", cls));
0314: }
0315: resolve(meta);
0316: return meta;
0317: }
0318:
0319: /**
0320: * Return the metadata for the given alias name.
0321: *
0322: * @param alias the alias to class to retrieve metadata for
0323: * @param envLoader the environmental class loader, if any
0324: * @param mustExist if true, throws a {@link MetaDataException}
0325: * if no metadata is found
0326: * @see ClassMetaData#getTypeAlias
0327: */
0328: public ClassMetaData getMetaData(String alias,
0329: ClassLoader envLoader, boolean mustExist) {
0330: if (alias == null && mustExist)
0331: throw new MetaDataException(_loc.get("no-alias-meta",
0332: alias, _aliases));
0333: if (alias == null)
0334: return null;
0335:
0336: // check cache
0337: processRegisteredClasses(envLoader);
0338: List classList = (List) _aliases.get(alias);
0339:
0340: // multiple classes may have been defined with the same alias: we
0341: // will filter by checking against the current list of the
0342: // persistent types and filter based on which classes are loadable
0343: // via the current environment's ClassLoader
0344: Set pcNames = getPersistentTypeNames(false, envLoader);
0345: Class cls = null;
0346: for (int i = 0; classList != null && i < classList.size(); i++) {
0347: Class c = (Class) classList.get(i);
0348: try {
0349: // re-load the class in the current environment loader so
0350: // that we can handle redeployment of the same class name
0351: Class nc = Class.forName(c.getName(), false, envLoader);
0352:
0353: // if we have specified a list of persistent clases,
0354: // also check to ensure that the class is in that list
0355: if (pcNames == null || pcNames.size() == 0
0356: || pcNames.contains(nc.getName())) {
0357: cls = nc;
0358: if (!classList.contains(cls))
0359: classList.add(cls);
0360: break;
0361: }
0362: } catch (Throwable t) {
0363: // this happens when the class is not loadable by
0364: // the environment class loader, so it was probably
0365: // listed elsewhere; also ignore linkage failures and
0366: // other class loading problems
0367: }
0368: }
0369: if (cls != null)
0370: return getMetaData(cls, envLoader, mustExist);
0371:
0372: // maybe this is some type we've seen but just isn't valid
0373: if (_aliases.containsKey(alias)) {
0374: if (mustExist)
0375: throwNoRegisteredAlias(alias);
0376: return null;
0377: }
0378:
0379: // record that this is an invalid type
0380: _aliases.put(alias, null);
0381:
0382: if (!mustExist)
0383: return null;
0384: return throwNoRegisteredAlias(alias);
0385: }
0386:
0387: private ClassMetaData throwNoRegisteredAlias(String alias) {
0388: String close = getClosestAliasName(alias);
0389: if (close != null)
0390: throw new MetaDataException(_loc.get("no-alias-meta-hint",
0391: alias, _aliases, close));
0392: else
0393: throw new MetaDataException(_loc.get("no-alias-meta",
0394: alias, _aliases));
0395: }
0396:
0397: /**
0398: * @return the nearest match to the specified alias name
0399: * @since 1.1.0
0400: */
0401: public String getClosestAliasName(String alias) {
0402: Collection aliases = getAliasNames();
0403: return StringDistance.getClosestLevenshteinDistance(alias,
0404: aliases);
0405: }
0406:
0407: /**
0408: * @return the registered alias names
0409: * @since 1.1.0
0410: */
0411: public Collection getAliasNames() {
0412: Collection aliases = new HashSet();
0413: synchronized (_aliases) {
0414: for (Iterator iter = _aliases.entrySet().iterator(); iter
0415: .hasNext();) {
0416: Map.Entry e = (Map.Entry) iter.next();
0417: if (e.getValue() != null)
0418: aliases.add(e.getKey());
0419: }
0420: }
0421: return aliases;
0422: }
0423:
0424: /**
0425: * Internal method to get the metadata for the given class, without
0426: * resolving it.
0427: */
0428: private ClassMetaData getMetaDataInternal(Class cls,
0429: ClassLoader envLoader) {
0430: if (cls == null)
0431: return null;
0432:
0433: // check cache for existing metadata, or give up if no metadata and
0434: // our list of configured persistent types doesn't include the class
0435: ClassMetaData meta = (ClassMetaData) _metas.get(cls);
0436: if (meta != null
0437: && ((meta.getSourceMode() & MODE_META) != 0 || (_sourceMode & MODE_META) == 0))
0438: return meta;
0439:
0440: // if runtime, cut off search if not in pc list. we don't do this at
0441: // dev time so that user can manipulate persistent classes he's writing
0442: // before adding them to the list
0443: if ((_validate & VALIDATE_RUNTIME) != 0) {
0444: Set pcNames = getPersistentTypeNames(false, envLoader);
0445: if (pcNames != null && !pcNames.contains(cls.getName()))
0446: return meta;
0447: }
0448:
0449: if (meta == null) {
0450: // check to see if maybe we know this class has no metadata
0451: if (_metas.containsKey(cls))
0452: return null;
0453:
0454: // make sure this isn't an obviously bad class
0455: if (cls.isPrimitive() || cls.getName().startsWith("java.")
0456: || cls == PersistenceCapable.class)
0457: return null;
0458:
0459: // designed to get around jikes 1.17 / JDK1.5 issue where static
0460: // initializers are not invoked when a class is referenced, so the
0461: // class never registers itself with the system
0462: if ((_validate & VALIDATE_RUNTIME) != 0) {
0463: try {
0464: Class
0465: .forName(
0466: cls.getName(),
0467: true,
0468: (ClassLoader) AccessController
0469: .doPrivileged(J2DoPrivHelper
0470: .getClassLoaderAction(cls)));
0471: } catch (Throwable t) {
0472: }
0473: }
0474: }
0475:
0476: // not in cache: load metadata or mappings depending on source mode.
0477: // loading metadata might also load mappings, but doesn't have to
0478: int mode = 0;
0479: if ((_sourceMode & MODE_META) != 0)
0480: mode = _sourceMode & ~MODE_MAPPING;
0481: else if ((_sourceMode & MODE_MAPPING) == 0)
0482: mode = _sourceMode;
0483: if (mode != MODE_NONE) {
0484: if (_log.isTraceEnabled())
0485: _log.trace(_loc
0486: .get("load-cls", cls, toModeString(mode)));
0487: _factory.load(cls, mode, envLoader);
0488: }
0489:
0490: // check cache again
0491: if (meta == null)
0492: meta = (ClassMetaData) _metas.get(cls);
0493: if (meta != null
0494: && ((meta.getSourceMode() & MODE_META) != 0 || (_sourceMode & MODE_META) == 0))
0495: return meta;
0496:
0497: // record that this class has no metadata; checking for this later
0498: // speeds things up in environments with slow class loading
0499: // like appservers
0500: if (meta != null)
0501: removeMetaData(meta);
0502: _metas.put(cls, null);
0503: return null;
0504: }
0505:
0506: /**
0507: * Return a string representation of the given mode flags.
0508: */
0509: private static String toModeString(int mode) {
0510: StringBuffer buf = new StringBuffer(31);
0511: if ((mode & MODE_META) != 0)
0512: buf.append("[META]");
0513: if ((mode & MODE_QUERY) != 0)
0514: buf.append("[QUERY]");
0515: if ((mode & MODE_MAPPING) != 0)
0516: buf.append("[MAPPING]");
0517: if ((mode & MODE_MAPPING_INIT) != 0)
0518: buf.append("[MAPPING_INIT]");
0519: return buf.toString();
0520: }
0521:
0522: /**
0523: * Prepare metadata for mapping resolution. This method might map parts
0524: * of the metadata that don't rely on other classes being mapped, but that
0525: * other classes might rely on during their own mapping (for example,
0526: * primary key fields). By default, this method only calls
0527: * {@link ClassMetaData#defineSuperclassFields}.
0528: */
0529: protected void prepareMapping(ClassMetaData meta) {
0530: meta.defineSuperclassFields(false);
0531: }
0532:
0533: /**
0534: * Resolve the given metadata if needed. There are three goals:
0535: * <ol>
0536: * <li>Make sure no unresolved metadata gets back to the client.</li>
0537: * <li>Avoid infinite reentrant calls for mutually-dependent metadatas by
0538: * allowing unresolved metadata to be returned to other metadatas.</li>
0539: * <li>Always make sure the superclass metadata is resolved before the
0540: * subclass metadata so that the subclass can access the super's list
0541: * of fields.</li>
0542: * </ol> Note that the code calling this method is synchronized, so this
0543: * method doesn't have to be.
0544: */
0545: private void resolve(ClassMetaData meta) {
0546: // return anything that has its metadata resolved, because that means
0547: // it is either fully resolved or must at least be in the process of
0548: // resolving mapping, etc since we do that right after meta resolve
0549: if (meta == null || _resMode == MODE_NONE
0550: || (meta.getResolve() & MODE_META) != 0)
0551: return;
0552:
0553: // resolve metadata
0554: List resolved = resolveMeta(meta);
0555: if (resolved == null)
0556: return;
0557:
0558: // load mapping data
0559: for (int i = 0; i < resolved.size(); i++)
0560: loadMapping((ClassMetaData) resolved.get(i));
0561: for (int i = 0; i < resolved.size(); i++)
0562: preMapping((ClassMetaData) resolved.get(i));
0563:
0564: // resolve mappings
0565: boolean err = true;
0566: if ((_resMode & MODE_MAPPING) != 0)
0567: for (int i = 0; i < resolved.size(); i++)
0568: err &= resolveMapping((ClassMetaData) resolved.get(i));
0569:
0570: // throw errors encountered
0571: if (err && !_errs.isEmpty()) {
0572: RuntimeException re;
0573: if (_errs.size() == 1)
0574: re = (RuntimeException) _errs.get(0);
0575: else
0576: re = new MetaDataException(_loc.get("resolve-errs"))
0577: .setNestedThrowables((Throwable[]) _errs
0578: .toArray(new Exception[_errs.size()]));
0579: _errs.clear();
0580: throw re;
0581: }
0582: }
0583:
0584: /**
0585: * Resolve metadata mode, returning list of processed metadadatas, or null
0586: * if we're still in the process of resolving other metadatas.
0587: */
0588: private List resolveMeta(ClassMetaData meta) {
0589: if (meta.getPCSuperclass() == null) {
0590: // set superclass
0591: Class sup = meta.getDescribedType().getSuperclass();
0592: ClassMetaData supMeta;
0593: while (sup != null && sup != Object.class) {
0594: supMeta = getMetaData(sup, meta.getEnvClassLoader(),
0595: false);
0596: if (supMeta != null) {
0597: meta.setPCSuperclass(sup);
0598: meta.setPCSuperclassMetaData(supMeta);
0599: break;
0600: } else
0601: sup = sup.getSuperclass();
0602: }
0603: if (meta.getDescribedType().isInterface()) {
0604: Class[] sups = meta.getDescribedType().getInterfaces();
0605: for (int i = 0; i < sups.length; i++) {
0606: supMeta = getMetaData(sups[i], meta
0607: .getEnvClassLoader(), false);
0608: if (supMeta != null) {
0609: meta.setPCSuperclass(sup);
0610: meta.setPCSuperclassMetaData(supMeta);
0611: break;
0612: }
0613: }
0614: }
0615: if (_log.isTraceEnabled())
0616: _log.trace(_loc.get("assigned-sup", meta, meta
0617: .getPCSuperclass()));
0618: }
0619:
0620: // resolve relation primary key fields for mapping dependencies
0621: FieldMetaData[] fmds = meta.getDeclaredFields();
0622: for (int i = 0; i < fmds.length; i++)
0623: if (fmds[i].isPrimaryKey())
0624: getMetaData(fmds[i].getDeclaredType(), meta
0625: .getEnvClassLoader(), false);
0626:
0627: // resolve metadata; if we're not in the process of resolving
0628: // others, this will return the set of interrelated metas that
0629: // resolved
0630: return processBuffer(meta, _resolving, MODE_META);
0631: }
0632:
0633: /**
0634: * Load mapping information for the given metadata.
0635: */
0636: private void loadMapping(ClassMetaData meta) {
0637: if ((meta.getResolve() & MODE_MAPPING) != 0)
0638: return;
0639:
0640: // load mapping information
0641: if ((meta.getSourceMode() & MODE_MAPPING) == 0
0642: && (_sourceMode & MODE_MAPPING) != 0) {
0643: // embedded-only metadata doesn't have mapping, so always loaded
0644: if (meta.isEmbeddedOnly())
0645: meta.setSourceMode(MODE_MAPPING, true);
0646: else {
0647: // load mapping data
0648: int mode = _sourceMode & ~MODE_META;
0649: if (_log.isTraceEnabled())
0650: _log.trace(_loc.get("load-mapping", meta,
0651: toModeString(mode)));
0652: try {
0653: _factory.load(meta.getDescribedType(), mode, meta
0654: .getEnvClassLoader());
0655: } catch (RuntimeException re) {
0656: removeMetaData(meta);
0657: _errs.add(re);
0658: }
0659: }
0660: }
0661: }
0662:
0663: /**
0664: * Pre-mapping preparation.
0665: */
0666: private void preMapping(ClassMetaData meta) {
0667: if ((meta.getResolve() & MODE_MAPPING) != 0)
0668: return;
0669:
0670: // prepare mappings for resolve; if not resolving mappings, then
0671: // make sure any superclass fields defined in metadata are resolved
0672: try {
0673: if ((_resMode & MODE_MAPPING) != 0) {
0674: if (_log.isTraceEnabled())
0675: _log.trace(_loc.get("prep-mapping", meta));
0676: prepareMapping(meta);
0677: } else
0678: meta.defineSuperclassFields(false);
0679: } catch (RuntimeException re) {
0680: removeMetaData(meta);
0681: _errs.add(re);
0682: }
0683: }
0684:
0685: /**
0686: * Resolve and initialize mapping.
0687: *
0688: * @return false if we're still in the process of resolving mappings
0689: */
0690: private boolean resolveMapping(ClassMetaData meta) {
0691: List mapped = processBuffer(meta, _mapping, MODE_MAPPING);
0692: if (mapped == null)
0693: return false;
0694:
0695: // initialize mapping for runtime use
0696: if ((_resMode & MODE_MAPPING_INIT) != 0) {
0697: for (int i = 0; i < mapped.size(); i++) {
0698: meta = (ClassMetaData) mapped.get(i);
0699: try {
0700: meta.resolve(MODE_MAPPING_INIT);
0701: } catch (RuntimeException re) {
0702: removeMetaData(meta);
0703: _errs.add(re);
0704: }
0705: }
0706: }
0707: return true;
0708: }
0709:
0710: /**
0711: * Process the given metadata and the associated buffer.
0712: */
0713: private List processBuffer(ClassMetaData meta, SortedSet buffer,
0714: int mode) {
0715: // if we're already processing a metadata, just buffer this one; when
0716: // the initial metadata finishes processing, we traverse the buffer
0717: // and process all the others that were introduced during reentrant
0718: // calls
0719: if (!buffer.add(meta) || buffer.size() != 1)
0720: return null;
0721:
0722: // continually pop a metadata and process it until we run out; note
0723: // that each processing call might place more metas in the buffer as
0724: // one class tries to access metadata for another; also note that the
0725: // buffer orders itself from least to most derived
0726: ClassMetaData buffered;
0727: List processed = new ArrayList(5);
0728: while (!buffer.isEmpty()) {
0729: buffered = (ClassMetaData) buffer.first();
0730: try {
0731: buffered.resolve(mode);
0732: processed.add(buffered);
0733: buffer.remove(buffered);
0734: } catch (RuntimeException re) {
0735: _errs.add(re);
0736:
0737: // any exception during resolution of one type means we can't
0738: // resolve any of the related types, so clear buffer. this also
0739: // ensures that if two types relate to each other and one
0740: // dies, we don't get into infinite cycles
0741: for (Iterator itr = buffer.iterator(); itr.hasNext();) {
0742: meta = (ClassMetaData) itr.next();
0743: removeMetaData(meta);
0744: if (meta != buffered) {
0745: _errs.add(new MetaDataException(_loc.get(
0746: "prev-errs", meta, buffered)));
0747: }
0748: }
0749: buffer.clear();
0750: }
0751: }
0752: return processed;
0753: }
0754:
0755: /**
0756: * Return all the metadata instances currently in the repository.
0757: */
0758: public synchronized ClassMetaData[] getMetaDatas() {
0759: // prevent concurrent mod errors when resolving one metadata
0760: // introduces others
0761: ClassMetaData[] metas = (ClassMetaData[]) _metas.values()
0762: .toArray(new ClassMetaData[_metas.size()]);
0763: for (int i = 0; i < metas.length; i++)
0764: if (metas[i] != null)
0765: getMetaData(metas[i].getDescribedType(), metas[i]
0766: .getEnvClassLoader(), true);
0767:
0768: List resolved = new ArrayList(_metas.size());
0769: ClassMetaData meta;
0770: for (Iterator itr = _metas.values().iterator(); itr.hasNext();) {
0771: meta = (ClassMetaData) itr.next();
0772: if (meta != null)
0773: resolved.add(meta);
0774: }
0775: metas = (ClassMetaData[]) resolved
0776: .toArray(newClassMetaDataArray(resolved.size()));
0777: Arrays.sort(metas);
0778: return metas;
0779: }
0780:
0781: /**
0782: * Return the cached metadata for the given class, without any resolution.
0783: * Return null if none.
0784: */
0785: public ClassMetaData getCachedMetaData(Class cls) {
0786: return (ClassMetaData) _metas.get(cls);
0787: }
0788:
0789: /**
0790: * Create a new metadata, populate it with default information, add it to
0791: * the repository, and return it. Use the default access type.
0792: */
0793: public ClassMetaData addMetaData(Class cls) {
0794: return addMetaData(cls, ClassMetaData.ACCESS_UNKNOWN);
0795: }
0796:
0797: /**
0798: * Create a new metadata, populate it with default information, add it to
0799: * the repository, and return it.
0800: *
0801: * @param access the access type to use in populating metadata
0802: */
0803: public ClassMetaData addMetaData(Class cls, int access) {
0804: if (cls == null || cls.isPrimitive())
0805: return null;
0806:
0807: ClassMetaData meta = newClassMetaData(cls);
0808: _factory.getDefaults().populate(meta, access);
0809:
0810: // synchronize on this rather than the map, because all other methods
0811: // that access _metas are synchronized on this
0812: synchronized (this ) {
0813: if (_pawares.containsKey(cls))
0814: throw new MetaDataException(_loc.get("pc-and-aware",
0815: cls));
0816: _metas.put(cls, meta);
0817: }
0818: return meta;
0819: }
0820:
0821: /**
0822: * Create a new class metadata instance.
0823: */
0824: protected ClassMetaData newClassMetaData(Class type) {
0825: return new ClassMetaData(type, this );
0826: }
0827:
0828: /**
0829: * Create a new array of the proper class metadata subclass.
0830: */
0831: protected ClassMetaData[] newClassMetaDataArray(int length) {
0832: return new ClassMetaData[length];
0833: }
0834:
0835: /**
0836: * Create a new field metadata instance.
0837: */
0838: protected FieldMetaData newFieldMetaData(String name, Class type,
0839: ClassMetaData owner) {
0840: return new FieldMetaData(name, type, owner);
0841: }
0842:
0843: /**
0844: * Create a new array of the proper field metadata subclass.
0845: */
0846: protected FieldMetaData[] newFieldMetaDataArray(int length) {
0847: return new FieldMetaData[length];
0848: }
0849:
0850: /**
0851: * Create a new array of the proper xml class metadata subclass.
0852: */
0853: protected XMLMetaData[] newXMLClassMetaDataArray(int length) {
0854: return new XMLClassMetaData[length];
0855: }
0856:
0857: /**
0858: * Create a new embedded class metadata instance.
0859: */
0860: protected ClassMetaData newEmbeddedClassMetaData(ValueMetaData owner) {
0861: return new ClassMetaData(owner);
0862: }
0863:
0864: /**
0865: * Create a new value metadata instance.
0866: */
0867: protected ValueMetaData newValueMetaData(FieldMetaData owner) {
0868: return new ValueMetaDataImpl(owner);
0869: }
0870:
0871: /**
0872: * Create an {@link Order} for the given field and declaration. This
0873: * method delegates to {@link #newRelatedFieldOrder} and
0874: * {@link #newValueFieldOrder} by default.
0875: */
0876: protected Order newOrder(FieldMetaData owner, String name,
0877: boolean asc) {
0878: // paths can start with (or equal) '#element'
0879: if (name.startsWith(Order.ELEMENT))
0880: name = name.substring(Order.ELEMENT.length());
0881: if (name.length() == 0)
0882: return newValueOrder(owner, asc);
0883:
0884: // next token should be '.'
0885: if (name.charAt(0) == '.')
0886: name = name.substring(1);
0887:
0888: // related field
0889: ClassMetaData meta = owner.getElement().getTypeMetaData();
0890: if (meta == null)
0891: throw new MetaDataException(_loc.get(
0892: "nonpc-field-orderable", owner, name));
0893: FieldMetaData rel = meta.getField(name);
0894: if (rel == null)
0895: throw new MetaDataException(_loc.get("bad-field-orderable",
0896: owner, name));
0897: return newRelatedFieldOrder(owner, rel, asc);
0898: }
0899:
0900: /**
0901: * Order by the field value.
0902: */
0903: protected Order newValueOrder(FieldMetaData owner, boolean asc) {
0904: return new InMemoryValueOrder(asc, getConfiguration());
0905: }
0906:
0907: /**
0908: * Order by a field of the related type.
0909: */
0910: protected Order newRelatedFieldOrder(FieldMetaData owner,
0911: FieldMetaData rel, boolean asc) {
0912: return new InMemoryRelatedFieldOrder(rel, asc,
0913: getConfiguration());
0914: }
0915:
0916: /**
0917: * Create an array of orders of the given size.
0918: */
0919: protected Order[] newOrderArray(int size) {
0920: return new Order[size];
0921: }
0922:
0923: /**
0924: * Remove a metadata instance from the repository.
0925: *
0926: * @return true if removed, false if not in this repository
0927: */
0928: public boolean removeMetaData(ClassMetaData meta) {
0929: if (meta == null)
0930: return false;
0931: return removeMetaData(meta.getDescribedType());
0932: }
0933:
0934: /**
0935: * Remove a metadata instance from the repository.
0936: *
0937: * @return true if removed, false if not in this repository
0938: */
0939: public synchronized boolean removeMetaData(Class cls) {
0940: if (cls == null)
0941: return false;
0942: if (_metas.remove(cls) != null) {
0943: Class impl = (Class) _ifaces.remove(cls);
0944: if (impl != null)
0945: _metas.remove(impl);
0946: return true;
0947: }
0948: return false;
0949: }
0950:
0951: /**
0952: * Add the given metadata as declared interface implementation.
0953: */
0954: void addDeclaredInterfaceImpl(ClassMetaData meta, Class iface) {
0955: synchronized (_impls) {
0956: Collection vals = (Collection) _impls.get(iface);
0957:
0958: // check to see if the superclass already declares to avoid dups
0959: if (vals != null) {
0960: ClassMetaData sup = meta.getPCSuperclassMetaData();
0961: for (; sup != null; sup = sup.getPCSuperclassMetaData())
0962: if (vals.contains(sup.getDescribedType()))
0963: return;
0964: }
0965: addToCollection(_impls, iface, meta.getDescribedType(),
0966: false);
0967: }
0968: }
0969:
0970: /**
0971: * Set the implementation for the given managed interface.
0972: */
0973: synchronized void setInterfaceImpl(ClassMetaData meta, Class impl) {
0974: if (!meta.isManagedInterface())
0975: throw new MetaDataException(_loc.get(
0976: "not-managed-interface", meta, impl));
0977: _ifaces.put(meta.getDescribedType(), impl);
0978: addDeclaredInterfaceImpl(meta, meta.getDescribedType());
0979: ClassMetaData sup = meta.getPCSuperclassMetaData();
0980: while (sup != null) {
0981: // record superclass interface info while we can as well as we
0982: // will only register concrete superclass in PCRegistry
0983: sup.clearSubclassCache();
0984: addToCollection(_subs, sup.getDescribedType(), impl, true);
0985: sup = (ClassMetaData) sup.getPCSuperclassMetaData();
0986: }
0987: }
0988:
0989: InterfaceImplGenerator getImplGenerator() {
0990: return _implGen;
0991: }
0992:
0993: /**
0994: * Return the least-derived class metadata for the given application
0995: * identity object.
0996: *
0997: * @param oid the oid to get the metadata for
0998: * @param envLoader the environmental class loader, if any
0999: * @param mustExist if true, throws a {@link MetaDataException}
1000: * if no metadata is found
1001: */
1002: public ClassMetaData getMetaData(Object oid, ClassLoader envLoader,
1003: boolean mustExist) {
1004: if (oid == null && mustExist)
1005: throw new MetaDataException(_loc.get("no-oid-meta", oid,
1006: "?", _oids.toString()));
1007: if (oid == null)
1008: return null;
1009:
1010: if (oid instanceof OpenJPAId) {
1011: Class cls = ((OpenJPAId) oid).getType();
1012: return getMetaData(cls, envLoader, mustExist);
1013: }
1014:
1015: // check cache
1016: processRegisteredClasses(envLoader);
1017: Class cls = (Class) _oids.get(oid.getClass());
1018: if (cls != null)
1019: return getMetaData(cls, envLoader, mustExist);
1020:
1021: // maybe this is some type we've seen but just isn't valid
1022: if (_oids.containsKey(oid.getClass())) {
1023: if (mustExist)
1024: throw new MetaDataException(_loc.get("no-oid-meta",
1025: oid, oid.getClass(), _oids));
1026: return null;
1027: }
1028:
1029: // if still not match, register any classes that look similar to the
1030: // oid class and check again
1031: resolveIdentityClass(oid);
1032: if (processRegisteredClasses(envLoader).length > 0) {
1033: cls = (Class) _oids.get(oid.getClass());
1034: if (cls != null)
1035: return getMetaData(cls, envLoader, mustExist);
1036: }
1037:
1038: // record that this is an invalid type
1039: _oids.put(oid.getClass(), null);
1040:
1041: if (!mustExist)
1042: return null;
1043: throw new MetaDataException(_loc.get("no-oid-meta", oid, oid
1044: .getClass(), _oids)).setFailedObject(oid);
1045: }
1046:
1047: /**
1048: * Make some guesses about the name of a target class for an
1049: * unknown application identity class.
1050: */
1051: private void resolveIdentityClass(Object oid) {
1052: if (oid == null)
1053: return;
1054:
1055: Class oidClass = oid.getClass();
1056: if (_log.isTraceEnabled())
1057: _log.trace(_loc.get("resolve-identity", oidClass));
1058:
1059: ClassLoader cl = (ClassLoader) AccessController
1060: .doPrivileged(J2DoPrivHelper
1061: .getClassLoaderAction(oidClass));
1062: String className;
1063: while (oidClass != null && oidClass != Object.class) {
1064: className = oidClass.getName();
1065:
1066: // we take a brute-force approach: try to load all the class'
1067: // substrings. this will handle the following common naming cases:
1068: //
1069: // com.company.MyClass$ID -> com.company.MyClass
1070: // com.company.MyClassId -> com.company.MyClass
1071: // com.company.MyClassOid -> com.company.MyClass
1072: // com.company.MyClassPK -> com.company.MyClass
1073: //
1074: // this isn't the fastest thing possible, but this method will
1075: // only be called once per JVM per unknown app id class
1076: for (int i = className.length(); i > 1; i--) {
1077: if (className.charAt(i - 1) == '.')
1078: break;
1079:
1080: try {
1081: Class.forName(className.substring(0, i), true, cl);
1082: } catch (Exception e) {
1083: } // consume all exceptions
1084: }
1085:
1086: // move up the OID hierarchy
1087: oidClass = oidClass.getSuperclass();
1088: }
1089: }
1090:
1091: /**
1092: * Return all least-derived metadatas with some mapped assignable type that
1093: * implement the given class.
1094: *
1095: * @param cls the class or interface to retrieve implementors for
1096: * @param envLoader the environmental class loader, if any
1097: * @param mustExist if true, throws a {@link MetaDataException}
1098: * if no metadata is found
1099: */
1100: public ClassMetaData[] getImplementorMetaDatas(Class cls,
1101: ClassLoader envLoader, boolean mustExist) {
1102: if (cls == null && mustExist)
1103: throw new MetaDataException(_loc.get("no-meta", cls));
1104: if (cls == null)
1105: return EMPTY_METAS;
1106:
1107: // get impls of given interface / abstract class
1108: loadRegisteredClassMetaData(envLoader);
1109: Collection vals = (Collection) _impls.get(cls);
1110: ClassMetaData meta;
1111: Collection mapped = null;
1112: if (vals != null) {
1113: synchronized (vals) {
1114: for (Iterator itr = vals.iterator(); itr.hasNext();) {
1115: meta = getMetaData((Class) itr.next(), envLoader,
1116: true);
1117: if (meta.isMapped()
1118: || meta.getMappedPCSubclassMetaDatas().length > 0) {
1119: if (mapped == null)
1120: mapped = new ArrayList(vals.size());
1121: mapped.add(meta);
1122: }
1123: }
1124: }
1125: }
1126:
1127: if (mapped == null && mustExist)
1128: throw new MetaDataException(_loc.get("no-meta", cls));
1129: if (mapped == null)
1130: return EMPTY_METAS;
1131: return (ClassMetaData[]) mapped
1132: .toArray(newClassMetaDataArray(mapped.size()));
1133: }
1134:
1135: /**
1136: * Gets the metadata corresponding to the given persistence-aware class.
1137: * Returns null, if the given class is not registered as
1138: * persistence-aware.
1139: */
1140: public NonPersistentMetaData getPersistenceAware(Class cls) {
1141: return (NonPersistentMetaData) _pawares.get(cls);
1142: }
1143:
1144: /**
1145: * Gets all the metadatas for persistence-aware classes
1146: *
1147: * @return empty array if no class has been registered as pers-aware
1148: */
1149: public NonPersistentMetaData[] getPersistenceAwares() {
1150: synchronized (_pawares) {
1151: if (_pawares.isEmpty())
1152: return EMPTY_NON_PERSISTENT;
1153: return (NonPersistentMetaData[]) _pawares.values().toArray(
1154: new NonPersistentMetaData[_pawares.size()]);
1155: }
1156: }
1157:
1158: /**
1159: * Add the given class as persistence-aware.
1160: *
1161: * @param cls non-null and must not alreaddy be added as persitence-capable
1162: */
1163: public NonPersistentMetaData addPersistenceAware(Class cls) {
1164: if (cls == null)
1165: return null;
1166: synchronized (this ) {
1167: if (_pawares.containsKey(cls))
1168: return (NonPersistentMetaData) _pawares.get(cls);
1169: if (getCachedMetaData(cls) != null)
1170: throw new MetaDataException(_loc.get("pc-and-aware",
1171: cls));
1172: NonPersistentMetaData meta = new NonPersistentMetaData(cls,
1173: this , NonPersistentMetaData.TYPE_PERSISTENCE_AWARE);
1174: _pawares.put(cls, meta);
1175: return meta;
1176: }
1177: }
1178:
1179: /**
1180: * Remove a persitence-aware class from the repository
1181: *
1182: * @return true if removed
1183: */
1184: public boolean removePersistenceAware(Class cls) {
1185: return _pawares.remove(cls) != null;
1186: }
1187:
1188: /**
1189: * Gets the metadata corresponding to the given non-mapped interface.
1190: * Returns null, if the given interface is not registered as
1191: * persistence-aware.
1192: */
1193: public NonPersistentMetaData getNonMappedInterface(Class iface) {
1194: return (NonPersistentMetaData) _nonMapped.get(iface);
1195: }
1196:
1197: /**
1198: * Gets the corresponding metadatas for all registered, non-mapped
1199: * interfaces
1200: *
1201: * @return empty array if no non-mapped interface has been registered.
1202: */
1203: public NonPersistentMetaData[] getNonMappedInterfaces() {
1204: synchronized (_nonMapped) {
1205: if (_nonMapped.isEmpty())
1206: return EMPTY_NON_PERSISTENT;
1207: return (NonPersistentMetaData[]) _nonMapped
1208: .values()
1209: .toArray(
1210: new NonPersistentMetaData[_nonMapped.size()]);
1211: }
1212: }
1213:
1214: /**
1215: * Add the given non-mapped interface to the repository.
1216: *
1217: * @param iface the non-mapped interface
1218: */
1219: public NonPersistentMetaData addNonMappedInterface(Class iface) {
1220: if (iface == null)
1221: return null;
1222: if (!iface.isInterface())
1223: throw new MetaDataException(_loc.get("not-non-mapped",
1224: iface));
1225: synchronized (this ) {
1226: if (_nonMapped.containsKey(iface))
1227: return (NonPersistentMetaData) _nonMapped.get(iface);
1228: if (getCachedMetaData(iface) != null)
1229: throw new MetaDataException(_loc.get("non-mapped-pc",
1230: iface));
1231: NonPersistentMetaData meta = new NonPersistentMetaData(
1232: iface, this ,
1233: NonPersistentMetaData.TYPE_NON_MAPPED_INTERFACE);
1234: _nonMapped.put(iface, meta);
1235: return meta;
1236: }
1237: }
1238:
1239: /**
1240: * Remove a non-mapped interface from the repository
1241: *
1242: * @return true if removed
1243: */
1244: public boolean removeNonMappedInterface(Class iface) {
1245: return _nonMapped.remove(iface) != null;
1246: }
1247:
1248: /**
1249: * Clear the cache of parsed metadata. This method also clears the
1250: * internal {@link MetaDataFactory MetaDataFactory}'s cache.
1251: */
1252: public synchronized void clear() {
1253: if (_log.isTraceEnabled())
1254: _log.trace(_loc.get("clear-repos", this ));
1255:
1256: _metas.clear();
1257: _oids.clear();
1258: _subs.clear();
1259: _impls.clear();
1260: _queries.clear();
1261: _seqs.clear();
1262: _registered.clear();
1263: _factory.clear();
1264: _aliases.clear();
1265: _pawares.clear();
1266: _nonMapped.clear();
1267: }
1268:
1269: /**
1270: * Return the set of configured persistent classes, or null if the user
1271: * did not configure any.
1272: *
1273: * @param devpath if true, search for metadata files in directories
1274: * in the classpath if no classes are configured explicitly
1275: * @param envLoader the class loader to use, or null for default
1276: */
1277: public synchronized Set getPersistentTypeNames(boolean devpath,
1278: ClassLoader envLoader) {
1279: return _factory.getPersistentTypeNames(devpath, envLoader);
1280: }
1281:
1282: /**
1283: * Load the persistent classes named in configuration.
1284: * This ensures that all subclasses and application identity classes of
1285: * each type are known in advance, without having to rely on the
1286: * application loading the classes before performing operations that
1287: * might involve them.
1288: *
1289: * @param devpath if true, search for metadata files in directories
1290: * in the classpath if the no classes are configured explicitly
1291: * @param envLoader the class loader to use, or null for default
1292: * @return the loaded classes, or empty collection if none
1293: */
1294: public synchronized Collection loadPersistentTypes(boolean devpath,
1295: ClassLoader envLoader) {
1296: Set names = getPersistentTypeNames(devpath, envLoader);
1297: if (names == null || names.isEmpty())
1298: return Collections.EMPTY_LIST;
1299:
1300: // attempt to load classes so that they get processed
1301: ClassLoader clsLoader = _conf.getClassResolverInstance()
1302: .getClassLoader(getClass(), envLoader);
1303: List classes = new ArrayList(names.size());
1304: Class cls;
1305: for (Iterator itr = names.iterator(); itr.hasNext();) {
1306: cls = classForName((String) itr.next(), clsLoader);
1307: if (cls != null) {
1308: classes.add(cls);
1309:
1310: // if the class is an interface, load its metadata to kick
1311: // off the impl generator
1312: if (cls.isInterface())
1313: getMetaData(cls, clsLoader, false);
1314: }
1315: }
1316: return classes;
1317: }
1318:
1319: /**
1320: * Return the class for the given name, or null if not loadable.
1321: */
1322: private Class classForName(String name, ClassLoader loader) {
1323: try {
1324: return Class.forName(name, true, loader);
1325: } catch (Exception e) {
1326: if ((_validate & VALIDATE_RUNTIME) != 0) {
1327: if (_log.isWarnEnabled())
1328: _log.warn(_loc.get("bad-discover-class", name));
1329: } else if (_log.isInfoEnabled())
1330: _log.info(_loc.get("bad-discover-class", name));
1331: if (_log.isTraceEnabled())
1332: _log.trace(e);
1333: } catch (NoSuchMethodError nsme) {
1334: if (nsme.getMessage().indexOf(".pc") == -1)
1335: throw nsme;
1336:
1337: // if the error is about a method that uses the PersistenceCapable
1338: // 'pc' method prefix, perform some logging and continue. This
1339: // probably just means that the class is not yet enhanced.
1340: if ((_validate & VALIDATE_RUNTIME) != 0) {
1341: if (_log.isWarnEnabled())
1342: _log.warn(_loc.get("bad-discover-class", name));
1343: } else if (_log.isInfoEnabled())
1344: _log.info(_loc.get("bad-discover-class", name));
1345: if (_log.isTraceEnabled())
1346: _log.trace(nsme);
1347: }
1348: return null;
1349: }
1350:
1351: /**
1352: * Return all known subclasses for the given class mapping. Note
1353: * that this method only works during runtime when the repository is
1354: * registered as a {@link RegisterClassListener}.
1355: */
1356: Collection getPCSubclasses(Class cls) {
1357: Collection subs = (Collection) _subs.get(cls);
1358: if (subs == null)
1359: return Collections.EMPTY_LIST;
1360: return subs;
1361: }
1362:
1363: ////////////////////////////////////////
1364: // RegisterClassListener implementation
1365: ////////////////////////////////////////
1366:
1367: public void register(Class cls) {
1368: // buffer registered classes until an oid metadata request is made,
1369: // at which point we'll parse everything in the buffer
1370: synchronized (_registered) {
1371: _registered.add(cls);
1372: }
1373: }
1374:
1375: /**
1376: * Parses the metadata for all registered classes.
1377: */
1378: private void loadRegisteredClassMetaData(ClassLoader envLoader) {
1379: Class[] reg = processRegisteredClasses(envLoader);
1380: for (int i = 0; i < reg.length; i++) {
1381: try {
1382: getMetaData(reg[i], envLoader, false);
1383: } catch (MetaDataException me) {
1384: if (_log.isWarnEnabled())
1385: _log.warn(me);
1386: }
1387: }
1388: }
1389:
1390: /**
1391: * Updates our datastructures with the latest registered classes.
1392: */
1393: Class[] processRegisteredClasses(ClassLoader envLoader) {
1394: if (_registered.isEmpty())
1395: return EMPTY_CLASSES;
1396:
1397: // copy into new collection to avoid concurrent mod errors on reentrant
1398: // registrations
1399: Class[] reg;
1400: synchronized (_registered) {
1401: reg = (Class[]) _registered.toArray(new Class[_registered
1402: .size()]);
1403: _registered.clear();
1404: }
1405:
1406: Collection pcNames = getPersistentTypeNames(false, envLoader);
1407: Collection failed = null;
1408: for (int i = 0; i < reg.length; i++) {
1409: // don't process types that aren't listed by the user; may belong
1410: // to a different persistence unit
1411: if (pcNames != null && !pcNames.isEmpty()
1412: && !pcNames.contains(reg[i].getName()))
1413: continue;
1414:
1415: try {
1416: processRegisteredClass(reg[i]);
1417: } catch (Throwable t) {
1418: if (!_conf.getRetryClassRegistration())
1419: throw new MetaDataException(_loc.get(
1420: "error-registered", reg[i]), t);
1421:
1422: if (_log.isWarnEnabled())
1423: _log.warn(_loc.get("failed-registered", reg[i]), t);
1424: if (failed == null)
1425: failed = new ArrayList();
1426: failed.add(reg[i]);
1427: }
1428: }
1429: if (failed != null) {
1430: synchronized (_registered) {
1431: _registered.addAll(failed);
1432: }
1433: }
1434: return reg;
1435: }
1436:
1437: /**
1438: * Updates our datastructures with the given registered class.
1439: * Relies on the fact that a child class cannot register itself without
1440: * also registering its parent class by specifying its persistence
1441: * capable superclass in the registration event.
1442: */
1443: private void processRegisteredClass(Class cls) {
1444: if (_log.isTraceEnabled())
1445: _log.trace(_loc.get("process-registered", cls));
1446:
1447: // update subclass lists; synchronize on this because accessing _metas
1448: // requires it
1449: Class leastDerived = cls;
1450: synchronized (this ) {
1451: ClassMetaData meta;
1452: for (Class anc = cls; (anc = PCRegistry
1453: .getPersistentSuperclass(anc)) != null;) {
1454: addToCollection(_subs, anc, cls, true);
1455: meta = (ClassMetaData) _metas.get(anc);
1456: if (meta != null)
1457: meta.clearSubclassCache();
1458: leastDerived = anc;
1459: }
1460: }
1461:
1462: // update oid mappings if this is a base concrete class
1463: Object oid = null;
1464: try {
1465: oid = PCRegistry.newObjectId(cls);
1466: } catch (InternalException ie) {
1467: // thrown for single field identity with null pk field value
1468: }
1469: if (oid != null) {
1470: Class existing = (Class) _oids.get(oid.getClass());
1471: if (existing != null) {
1472: // if there is already a class for this OID, then we know
1473: // that multiple classes are using the same OID: therefore,
1474: // put the least derived PC superclass into the map. This
1475: // gets around the problem of an abstract PC superclass
1476: // using application identity (since newObjectId
1477: // will return null for abstract classes).
1478: Class sup = cls;
1479: while (PCRegistry.getPersistentSuperclass(sup) != null)
1480: sup = PCRegistry.getPersistentSuperclass(sup);
1481:
1482: _oids.put(oid.getClass(), sup);
1483: } else if (existing == null
1484: || cls.isAssignableFrom(existing))
1485: _oids.put(oid.getClass(), cls);
1486: }
1487:
1488: // update mappings from interfaces and non-pc superclasses to
1489: // pc implementing types
1490: synchronized (_impls) {
1491: updateImpls(cls, leastDerived, cls);
1492: }
1493:
1494: // set alias for class
1495: String alias = PCRegistry.getTypeAlias(cls);
1496: if (alias != null) {
1497: synchronized (_aliases) {
1498: List classList = (List) _aliases.get(alias);
1499: if (classList == null) {
1500: classList = new ArrayList(3);
1501: _aliases.put(alias, classList);
1502: }
1503: if (!classList.contains(cls))
1504: classList.add(cls);
1505: }
1506: }
1507: }
1508:
1509: /**
1510: * Update the list of implementations of base classes and interfaces.
1511: */
1512: private void updateImpls(Class cls, Class leastDerived, Class check) {
1513: // allow users to query on common non-pc superclasses
1514: Class sup = check.getSuperclass();
1515: if (leastDerived == cls && sup != null && sup != Object.class) {
1516: addToCollection(_impls, sup, cls, false);
1517: updateImpls(cls, leastDerived, sup);
1518: }
1519:
1520: // allow users to query on any implemented interfaces unless defaults
1521: // say the user must implement persistent interfaces explicitly in meta
1522: if (!_factory.getDefaults().isDeclaredInterfacePersistent())
1523: return;
1524: Class[] ints = check.getInterfaces();
1525: for (int i = 0; i < ints.length; i++) {
1526: // don't map java-standard interfaces
1527: if (ints[i].getName().startsWith("java."))
1528: continue;
1529:
1530: // only map least-derived interface implementors
1531: if (leastDerived == cls || isLeastDerivedImpl(ints[i], cls)) {
1532: addToCollection(_impls, ints[i], cls, false);
1533: updateImpls(cls, leastDerived, ints[i]);
1534: }
1535: }
1536: }
1537:
1538: /**
1539: * Return true if the given class is the least-derived persistent
1540: * implementor of the given interface, false otherwise.
1541: */
1542: private boolean isLeastDerivedImpl(Class inter, Class cls) {
1543: Class parent = PCRegistry.getPersistentSuperclass(cls);
1544: while (parent != null) {
1545: if (Arrays.asList(parent.getInterfaces()).contains(inter))
1546: return false;
1547: parent = PCRegistry.getPersistentSuperclass(parent);
1548: }
1549: return true;
1550: }
1551:
1552: /**
1553: * Add the given value to the collection cached in the given map under
1554: * the given key.
1555: */
1556: private void addToCollection(Map map, Class key, Class value,
1557: boolean inheritance) {
1558: synchronized (map) {
1559: Collection coll = (Collection) map.get(key);
1560: if (coll == null) {
1561: if (inheritance) {
1562: InheritanceComparator comp = new InheritanceComparator();
1563: comp.setBase(key);
1564: coll = new TreeSet(comp);
1565: } else
1566: coll = new LinkedList();
1567: map.put(key, coll);
1568: }
1569: coll.add(value);
1570: }
1571: }
1572:
1573: ///////////////////////////////
1574: // Configurable implementation
1575: ///////////////////////////////
1576:
1577: public void setConfiguration(Configuration conf) {
1578: _conf = (OpenJPAConfiguration) conf;
1579: _log = _conf.getLog(OpenJPAConfiguration.LOG_METADATA);
1580: }
1581:
1582: public void startConfiguration() {
1583: }
1584:
1585: public void endConfiguration() {
1586: initializeMetaDataFactory();
1587: if (_implGen == null)
1588: _implGen = new InterfaceImplGenerator(this );
1589: }
1590:
1591: private void initializeMetaDataFactory() {
1592: if (_factory == null) {
1593: MetaDataFactory mdf = _conf.newMetaDataFactoryInstance();
1594: if (mdf == null)
1595: throw new MetaDataException(_loc
1596: .get("no-metadatafactory"));
1597: setMetaDataFactory(mdf);
1598: }
1599: }
1600:
1601: //////////////////
1602: // Query metadata
1603: //////////////////
1604:
1605: /**
1606: * Return query metadata for the given class, name, and classloader.
1607: */
1608: public synchronized QueryMetaData getQueryMetaData(Class cls,
1609: String name, ClassLoader envLoader, boolean mustExist) {
1610: QueryMetaData meta = getQueryMetaDataInternal(cls, name,
1611: envLoader);
1612: if (meta == null) {
1613: // load all the metadatas for all the known classes so that
1614: // query names are seen and registered
1615: resolveAll(envLoader);
1616: meta = getQueryMetaDataInternal(cls, name, envLoader);
1617: }
1618:
1619: if (meta == null && mustExist) {
1620: if (cls == null) {
1621: throw new MetaDataException(_loc.get(
1622: "no-named-query-null-class",
1623: getPersistentTypeNames(false, envLoader), name));
1624: } else {
1625: throw new MetaDataException(_loc.get("no-named-query",
1626: cls, name));
1627: }
1628: }
1629:
1630: return meta;
1631: }
1632:
1633: /**
1634: * Resolve all known metadata classes.
1635: */
1636: private void resolveAll(ClassLoader envLoader) {
1637: Collection types = loadPersistentTypes(false, envLoader);
1638: for (Iterator i = types.iterator(); i.hasNext();) {
1639: Class c = (Class) i.next();
1640: getMetaData(c, envLoader, false);
1641: }
1642: }
1643:
1644: /**
1645: * Return query metadata for the given class, name, and classloader.
1646: */
1647: private QueryMetaData getQueryMetaDataInternal(Class cls,
1648: String name, ClassLoader envLoader) {
1649: if (name == null)
1650: return null;
1651:
1652: // check cache
1653: Object key = getQueryKey(cls, name);
1654: QueryMetaData qm = (QueryMetaData) _queries.get(key);
1655: if (qm != null)
1656: return qm;
1657:
1658: // get metadata for class, which will find queries in metadata file
1659: if (cls != null && getMetaData(cls, envLoader, false) != null) {
1660: qm = (QueryMetaData) _queries.get(key);
1661: if (qm != null)
1662: return qm;
1663: }
1664: if ((_sourceMode & MODE_QUERY) == 0)
1665: return null;
1666:
1667: // see if factory can figure out a scope for this query
1668: if (cls == null)
1669: cls = _factory.getQueryScope(name, envLoader);
1670:
1671: // not in cache; load
1672: _factory.load(cls, MODE_QUERY, envLoader);
1673: return (QueryMetaData) _queries.get(key);
1674: }
1675:
1676: /**
1677: * Return the cached query metadata.
1678: */
1679: public synchronized QueryMetaData[] getQueryMetaDatas() {
1680: return (QueryMetaData[]) _queries.values().toArray(
1681: new QueryMetaData[_queries.size()]);
1682: }
1683:
1684: /**
1685: * Return the cached query metadata for the given name.
1686: */
1687: public synchronized QueryMetaData getCachedQueryMetaData(Class cls,
1688: String name) {
1689: return (QueryMetaData) _queries.get(getQueryKey(cls, name));
1690: }
1691:
1692: /**
1693: * Add a new query metadata to the repository and return it.
1694: */
1695: public synchronized QueryMetaData addQueryMetaData(Class cls,
1696: String name) {
1697: QueryMetaData meta = newQueryMetaData(cls, name);
1698: _queries.put(getQueryKey(meta), meta);
1699: return meta;
1700: }
1701:
1702: /**
1703: * Create a new query metadata instance.
1704: */
1705: protected QueryMetaData newQueryMetaData(Class cls, String name) {
1706: QueryMetaData meta = new QueryMetaData(name);
1707: meta.setDefiningType(cls);
1708: return meta;
1709: }
1710:
1711: /**
1712: * Remove the given query metadata from the repository.
1713: */
1714: public synchronized boolean removeQueryMetaData(QueryMetaData meta) {
1715: if (meta == null)
1716: return false;
1717: return _queries.remove(getQueryKey(meta)) != null;
1718: }
1719:
1720: /**
1721: * Remove query metadata for the given class name if in the repository.
1722: */
1723: public synchronized boolean removeQueryMetaData(Class cls,
1724: String name) {
1725: if (name == null)
1726: return false;
1727: return _queries.remove(getQueryKey(cls, name)) != null;
1728: }
1729:
1730: /**
1731: * Return a unique key for a given QueryMetaData.
1732: */
1733: private static Object getQueryKey(QueryMetaData meta) {
1734: if (meta == null)
1735: return null;
1736: return getQueryKey(meta.getDefiningType(), meta.getName());
1737: }
1738:
1739: /**
1740: * Return a unique key for a given class / name. The class
1741: * argument can be null.
1742: */
1743: protected static Object getQueryKey(Class cls, String name) {
1744: if (cls == null)
1745: return name;
1746: QueryKey key = new QueryKey();
1747: key.clsName = cls.getName();
1748: key.name = name;
1749: return key;
1750: }
1751:
1752: /////////////////////
1753: // Sequence metadata
1754: /////////////////////
1755:
1756: /**
1757: * Return sequence metadata for the given name and classloader.
1758: */
1759: public synchronized SequenceMetaData getSequenceMetaData(
1760: String name, ClassLoader envLoader, boolean mustExist) {
1761: SequenceMetaData meta = getSequenceMetaDataInternal(name,
1762: envLoader);
1763: if (meta == null && SequenceMetaData.NAME_SYSTEM.equals(name)) {
1764: if (_sysSeq == null)
1765: _sysSeq = newSequenceMetaData(name);
1766: return _sysSeq;
1767: }
1768: if (meta == null && mustExist)
1769: throw new MetaDataException(_loc.get("no-named-sequence",
1770: name));
1771: return meta;
1772: }
1773:
1774: /**
1775: * Used internally by metadata to retrieve sequence metadatas based on
1776: * possibly-unqualified sequence name.
1777: */
1778: SequenceMetaData getSequenceMetaData(ClassMetaData context,
1779: String name, boolean mustExist) {
1780: // try with given name
1781: MetaDataException e = null;
1782: try {
1783: SequenceMetaData seq = getSequenceMetaData(name, context
1784: .getEnvClassLoader(), mustExist);
1785: if (seq != null)
1786: return seq;
1787: } catch (MetaDataException mde) {
1788: e = mde;
1789: }
1790:
1791: // if given name already fully qualified, give up
1792: if (name.indexOf('.') != -1) {
1793: if (e != null)
1794: throw e;
1795: return null;
1796: }
1797:
1798: // try with qualified name
1799: name = Strings.getPackageName(context.getDescribedType()) + "."
1800: + name;
1801: try {
1802: return getSequenceMetaData(name, context
1803: .getEnvClassLoader(), mustExist);
1804: } catch (MetaDataException mde) {
1805: // throw original exception
1806: if (e != null)
1807: throw e;
1808: throw mde;
1809: }
1810: }
1811:
1812: /**
1813: * Return sequence metadata for the given name and classloader.
1814: */
1815: private SequenceMetaData getSequenceMetaDataInternal(String name,
1816: ClassLoader envLoader) {
1817: if (name == null)
1818: return null;
1819:
1820: // check cache
1821: SequenceMetaData meta = (SequenceMetaData) _seqs.get(name);
1822: if (meta == null) {
1823: // load metadata for registered classes to hopefully find sequence
1824: // definition
1825: loadRegisteredClassMetaData(envLoader);
1826: meta = (SequenceMetaData) _seqs.get(name);
1827: }
1828: return meta;
1829: }
1830:
1831: /**
1832: * Return the cached sequence metadata.
1833: */
1834: public synchronized SequenceMetaData[] getSequenceMetaDatas() {
1835: return (SequenceMetaData[]) _seqs.values().toArray(
1836: new SequenceMetaData[_seqs.size()]);
1837: }
1838:
1839: /**
1840: * Return the cached a sequence metadata for the given name.
1841: */
1842: public synchronized SequenceMetaData getCachedSequenceMetaData(
1843: String name) {
1844: return (SequenceMetaData) _seqs.get(name);
1845: }
1846:
1847: /**
1848: * Add a new sequence metadata to the repository and return it.
1849: */
1850: public synchronized SequenceMetaData addSequenceMetaData(String name) {
1851: SequenceMetaData meta = newSequenceMetaData(name);
1852: _seqs.put(name, meta);
1853: return meta;
1854: }
1855:
1856: /**
1857: * Create a new sequence metadata instance.
1858: */
1859: protected SequenceMetaData newSequenceMetaData(String name) {
1860: return new SequenceMetaData(name, this );
1861: }
1862:
1863: /**
1864: * Remove the given sequence metadata from the repository.
1865: */
1866: public synchronized boolean removeSequenceMetaData(
1867: SequenceMetaData meta) {
1868: if (meta == null)
1869: return false;
1870: return _seqs.remove(meta.getName()) != null;
1871: }
1872:
1873: /**
1874: * Remove sequence metadata for the name if in the repository.
1875: */
1876: public synchronized boolean removeSequenceMetaData(String name) {
1877: if (name == null)
1878: return false;
1879: return _seqs.remove(name) != null;
1880: }
1881:
1882: /**
1883: * Add the given system lifecycle listener.
1884: */
1885: public synchronized void addSystemListener(Object listener) {
1886: // copy to avoid issues with ListenerList and avoid unncessary
1887: // locking on the list during runtime
1888: LifecycleEventManager.ListenerList listeners = new LifecycleEventManager.ListenerList(
1889: _listeners);
1890: listeners.add(listener);
1891: _listeners = listeners;
1892: }
1893:
1894: /**
1895: * Remove the given system lifecycle listener.
1896: */
1897: public synchronized boolean removeSystemListener(Object listener) {
1898: if (!_listeners.contains(listener))
1899: return false;
1900:
1901: // copy to avoid issues with ListenerList and avoid unncessary
1902: // locking on the list during runtime
1903: LifecycleEventManager.ListenerList listeners = new LifecycleEventManager.ListenerList(
1904: _listeners);
1905: listeners.remove(listener);
1906: _listeners = listeners;
1907: return true;
1908: }
1909:
1910: /**
1911: * Return the system lifecycle listeners
1912: */
1913: public LifecycleEventManager.ListenerList getSystemListeners() {
1914: return _listeners;
1915: }
1916:
1917: /**
1918: * Free the resources used by this repository. Closes all user sequences.
1919: */
1920: public synchronized void close() {
1921: SequenceMetaData[] smds = getSequenceMetaDatas();
1922: for (int i = 0; i < smds.length; i++)
1923: smds[i].close();
1924: clear();
1925: }
1926:
1927: /**
1928: * Query key struct.
1929: */
1930: private static class QueryKey implements Serializable {
1931:
1932: public String clsName;
1933: public String name;
1934:
1935: public int hashCode() {
1936: int clsHash = (clsName == null) ? 0 : clsName.hashCode();
1937: int nameHash = (name == null) ? 0 : name.hashCode();
1938: return clsHash + nameHash;
1939: }
1940:
1941: public boolean equals(Object obj) {
1942: if (obj == this )
1943: return true;
1944: if (!(obj instanceof QueryKey))
1945: return false;
1946:
1947: QueryKey qk = (QueryKey) obj;
1948: return StringUtils.equals(clsName, qk.clsName)
1949: && StringUtils.equals(name, qk.name);
1950: }
1951: }
1952:
1953: /**
1954: * Return XML metadata for a given field metadata
1955: * @param fmd
1956: * @return XML metadata
1957: */
1958: public synchronized XMLMetaData getXMLMetaData(FieldMetaData fmd) {
1959: Class cls = fmd.getDeclaredType();
1960: // check if cached before
1961: XMLMetaData xmlmeta = (XMLClassMetaData) _xmlmetas.get(cls);
1962: if (xmlmeta != null)
1963: return xmlmeta;
1964:
1965: // load JAXB XML metadata
1966: _factory.loadXMLMetaData(fmd);
1967:
1968: xmlmeta = (XMLClassMetaData) _xmlmetas.get(cls);
1969:
1970: return xmlmeta;
1971: }
1972:
1973: /**
1974: * Create a new metadata, populate it with default information, add it to
1975: * the repository, and return it.
1976: *
1977: * @param access the access type to use in populating metadata
1978: */
1979: public XMLClassMetaData addXMLMetaData(Class type, String name) {
1980: XMLClassMetaData meta = newXMLClassMetaData(type, name);
1981:
1982: // synchronize on this rather than the map, because all other methods
1983: // that access _xmlmetas are synchronized on this
1984: synchronized (this ) {
1985: _xmlmetas.put(type, meta);
1986: }
1987: return meta;
1988: }
1989:
1990: /**
1991: * Return the cached XMLClassMetaData for the given class
1992: * Return null if none.
1993: */
1994: public XMLMetaData getCachedXMLMetaData(Class cls) {
1995: return (XMLMetaData) _xmlmetas.get(cls);
1996: }
1997:
1998: /**
1999: * Create a new xml class metadata
2000: * @param type
2001: * @param name
2002: * @return a XMLClassMetaData
2003: */
2004: protected XMLClassMetaData newXMLClassMetaData(Class type,
2005: String name) {
2006: return new XMLClassMetaData(type, name);
2007: }
2008:
2009: /**
2010: * Create a new xml field meta, add it to the fieldMap in the given
2011: * xml class metadata
2012: * @param type
2013: * @param name
2014: * @param meta
2015: * @return a XMLFieldMetaData
2016: */
2017: public XMLFieldMetaData newXMLFieldMetaData(Class type, String name) {
2018: return new XMLFieldMetaData(type, name);
2019: }
2020: }
|