| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.meta; |
| |
| import java.io.Serializable; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.enhance.DynamicPersistenceCapable; |
| import org.apache.openjpa.enhance.PCEnhancer; |
| import org.apache.openjpa.enhance.PCRegistry; |
| import org.apache.openjpa.enhance.PCRegistry.RegisterClassListener; |
| import org.apache.openjpa.enhance.PersistenceCapable; |
| import org.apache.openjpa.event.LifecycleEventManager; |
| import org.apache.openjpa.lib.conf.Configurable; |
| import org.apache.openjpa.lib.conf.Configuration; |
| import org.apache.openjpa.lib.conf.Configurations; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.util.ClassUtil; |
| import org.apache.openjpa.lib.util.Closeable; |
| import org.apache.openjpa.lib.util.J2DoPrivHelper; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.MultiClassLoader; |
| import org.apache.openjpa.lib.util.Options; |
| import org.apache.openjpa.lib.util.StringDistance; |
| import org.apache.openjpa.util.ClassResolver; |
| import org.apache.openjpa.util.ImplHelper; |
| import org.apache.openjpa.util.InternalException; |
| import org.apache.openjpa.util.MetaDataException; |
| import org.apache.openjpa.util.OpenJPAId; |
| |
| |
| /** |
| * Repository of and factory for persistent metadata. |
| * |
| * @since 0.3.0 |
| * @author Abe White |
| * @author Steve Kim (query metadata) |
| */ |
| public class MetaDataRepository implements PCRegistry.RegisterClassListener, Configurable, Closeable, MetaDataModes, |
| Serializable { |
| |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * Constant to not validate any metadata. |
| */ |
| public static final int VALIDATE_NONE = 0; |
| |
| /** |
| * Bit flag to validate metadata. |
| */ |
| public static final int VALIDATE_META = 1; |
| |
| /** |
| * Bit flag to validate mappings. |
| */ |
| public static final int VALIDATE_MAPPING = 2; |
| |
| /** |
| * Bit flag to validate unenhanced metadata only. |
| */ |
| public static final int VALIDATE_UNENHANCED = 4; |
| |
| /** |
| * Bit flag for runtime validation. Requires that all classes are enhanced, and performs extra |
| * field resolution steps. |
| */ |
| public static final int VALIDATE_RUNTIME = 8; |
| |
| protected static final Class<?>[] EMPTY_CLASSES = new Class[0]; |
| protected static final NonPersistentMetaData[] EMPTY_NON_PERSISTENT = new NonPersistentMetaData[0]; |
| protected final ClassMetaData[] EMPTY_METAS; |
| protected final FieldMetaData[] EMPTY_FIELDS; |
| protected final Order[] EMPTY_ORDERS; |
| |
| private static final Localizer _loc = Localizer.forPackage(MetaDataRepository.class); |
| |
| // system sequence |
| private SequenceMetaData _sysSeq = null; |
| // cache of parsed metadata, oid class to class, and interface class |
| // to metadatas |
| private Map<Class<?>, ClassMetaData> _metas = new HashMap<>(); |
| private Map<String, ClassMetaData> _metaStringMap = new ConcurrentHashMap<>(); |
| private Map<Class<?>, Class<?>> _oids = Collections.synchronizedMap(new HashMap<>()); |
| private Map<Class<?>, Collection<Class<?>>> _impls = |
| Collections.synchronizedMap(new HashMap<>()); |
| private Map<Class<?>, Class<?>> _ifaces = Collections.synchronizedMap(new HashMap<>()); |
| private Map<String, QueryMetaData> _queries = new HashMap<>(); |
| private Map<String, SequenceMetaData> _seqs = new HashMap<>(); |
| private Map<String, List<Class<?>>> _aliases = Collections.synchronizedMap(new HashMap<>()); |
| private Map<Class<?>, NonPersistentMetaData> _pawares = |
| Collections.synchronizedMap(new HashMap<>()); |
| private Map<Class<?>, NonPersistentMetaData> _nonMapped = |
| Collections.synchronizedMap(new HashMap<>()); |
| private Map<Class<?>, Class<?>> _metamodel = Collections.synchronizedMap(new HashMap<>()); |
| |
| // map of classes to lists of their subclasses |
| private Map<Class<?>, Collection<Class<?>>> _subs = |
| Collections.synchronizedMap(new HashMap<>()); |
| |
| // xml mapping |
| protected final XMLMetaData[] EMPTY_XMLMETAS; |
| private final Map<Class<?>, XMLMetaData> _xmlmetas = new HashMap<>(); |
| |
| private transient OpenJPAConfiguration _conf = null; |
| private transient Log _log = null; |
| private transient InterfaceImplGenerator _implGen = null; |
| private transient MetaDataFactory _factory = null; |
| |
| private int _resMode = MODE_META | MODE_MAPPING; |
| private int _sourceMode = MODE_META | MODE_MAPPING | MODE_QUERY; |
| private int _validate = VALIDATE_META | VALIDATE_UNENHANCED; |
| |
| // we buffer up any classes that register themselves to prevent |
| // reentrancy errors if classes register during a current parse (common) |
| private final Collection<Class<?>> _registered = new HashSet<>(); |
| |
| // set of metadatas we're in the process of resolving |
| private final List<ClassMetaData> _resolving = new ArrayList<>(); |
| private final List<ClassMetaData> _mapping = new ArrayList<>(); |
| private final List<RuntimeException> _errs = new LinkedList<>(); |
| |
| // system listeners |
| private LifecycleEventManager.ListenerList _listeners = new LifecycleEventManager.ListenerList(3); |
| private boolean _systemListenersActivated = false; |
| |
| protected boolean _preload = false; |
| protected boolean _preloadComplete = false; |
| protected boolean _locking = true; |
| private static final String PRELOAD_STR = "Preload"; |
| |
| // A boolean used to decide whether or not we need to call to PCEnhancer to check whether we have any down level |
| // Entities. |
| private boolean _logEnhancementLevel = true; |
| |
| // A boolean used to decide whether to filter Class<?> objects submitted by the PCRegistry listener system |
| private boolean _filterRegisteredClasses = false; |
| |
| // we should skip these types for the enhancement |
| private Collection<Class<?>> _typesWithoutEnhancement; |
| |
| /** |
| * Default constructor. Configure via {@link Configurable}. |
| */ |
| public MetaDataRepository() { |
| EMPTY_METAS = newClassMetaDataArray(0); |
| EMPTY_FIELDS = newFieldMetaDataArray(0); |
| EMPTY_ORDERS = newOrderArray(0); |
| EMPTY_XMLMETAS = newXMLClassMetaDataArray(0); |
| |
| } |
| |
| /** |
| * Return the configuration for the repository. |
| */ |
| public OpenJPAConfiguration getConfiguration() { |
| return _conf; |
| } |
| |
| /** |
| * Return the metadata log. |
| */ |
| public Log getLog() { |
| return _log; |
| } |
| |
| /** |
| * The I/O used to load metadata. |
| */ |
| public MetaDataFactory getMetaDataFactory() { |
| return _factory; |
| } |
| |
| /** |
| * The I/O used to load metadata. |
| */ |
| public void setMetaDataFactory(MetaDataFactory factory) { |
| factory.setRepository(this); |
| _factory = factory; |
| } |
| |
| /** |
| * The metadata validation level. Defaults to <code>VALIDATE_META | VALIDATE_UNENHANCED</code>. |
| */ |
| public int getValidate() { |
| return _validate; |
| } |
| |
| /** |
| * The metadata validation level. Defaults to <code>VALIDATE_META | VALIDATE_UNENHANCED</code>. |
| */ |
| public void setValidate(int validate) { |
| _validate = validate; |
| } |
| |
| /** |
| * The metadata validation level. Defaults to |
| * <code>VALIDATE_META | VALIDATE_MAPPING | VALIDATE_UNENHANCED</code>. |
| */ |
| public void setValidate(int validate, boolean on) { |
| if (validate == VALIDATE_NONE) |
| _validate = validate; |
| else if (on) |
| _validate |= validate; |
| else |
| _validate &= ~validate; |
| } |
| |
| /** |
| * The metadata resolution mode. Defaults to <code>MODE_META | MODE_MAPPING</code>. |
| */ |
| public int getResolve() { |
| return _resMode; |
| } |
| |
| /** |
| * The metadata resolution mode. Defaults to <code>MODE_META | MODE_MAPPING</code>. |
| */ |
| public void setResolve(int mode) { |
| _resMode = mode; |
| } |
| |
| /** |
| * The metadata resolution mode. Defaults to <code>MODE_META | MODE_MAPPING</code>. |
| */ |
| public void setResolve(int mode, boolean on) { |
| if (mode == MODE_NONE) |
| _resMode = mode; |
| else if (on) |
| _resMode |= mode; |
| else |
| _resMode &= ~mode; |
| } |
| |
| /** |
| * The source mode determining what metadata to load. Defaults to |
| * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>. |
| */ |
| public int getSourceMode() { |
| return _sourceMode; |
| } |
| |
| /** |
| * The source mode determining what metadata to load. Defaults to |
| * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>. |
| */ |
| public void setSourceMode(int mode) { |
| _sourceMode = mode; |
| } |
| |
| /** |
| * The source mode determining what metadata to load. Defaults to |
| * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>. |
| */ |
| public void setSourceMode(int mode, boolean on) { |
| if (mode == MODE_NONE) |
| _sourceMode = mode; |
| else if (on) |
| _sourceMode |= mode; |
| else |
| _sourceMode &= ~mode; |
| } |
| |
| /** |
| * Sets whether this repository will load all known persistent classes at initialization. |
| * Defaults to false. |
| */ |
| public boolean getPreload() { |
| return _preload; |
| } |
| |
| /** |
| * Sets whether this repository will load all known persistent classes at initialization. |
| * Defaults to false. |
| */ |
| public void setPreload(boolean l) { |
| _preload = l; |
| } |
| |
| |
| /** |
| * If the openjpa.MetaDataRepository plugin value Preload=true is set, this method will load all |
| * MetaData for all persistent classes and will remove locking from this class. |
| */ |
| public synchronized void preload() { |
| if (!_preload) { |
| return; |
| } |
| // If pooling EMFs, this method may be invoked more than once. Only perform this work once. |
| if (_preloadComplete) { |
| return; |
| } |
| |
| |
| MultiClassLoader multi = AccessController.doPrivileged(J2DoPrivHelper.newMultiClassLoaderAction()); |
| multi.addClassLoader(AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction())); |
| multi.addClassLoader(AccessController.doPrivileged(J2DoPrivHelper |
| .getClassLoaderAction(MetaDataRepository.class))); |
| // If a ClassLoader was passed into Persistence.createContainerEntityManagerFactory on the PersistenceUnitInfo |
| // we need to add that loader to the chain of classloaders |
| ClassResolver resolver = _conf.getClassResolverInstance(); |
| if (resolver != null) { |
| ClassLoader cl = resolver.getClassLoader(null, null); |
| if (cl != null) { |
| multi.addClassLoader(cl); |
| } |
| } |
| |
| Set<String> classes = getPersistentTypeNames(false, multi); |
| if (classes == null || classes.size() == 0) { |
| throw new MetaDataException(_loc.get("repos-initializeEager-none")); |
| } |
| if (_log.isTraceEnabled()) { |
| _log.trace(_loc.get("repos-initializeEager-found", classes)); |
| } |
| |
| List<Class<?>> loaded = new ArrayList<>(); |
| for (String c : classes) { |
| try { |
| Class<?> cls = AccessController.doPrivileged((J2DoPrivHelper.getForNameAction(c, true, multi))); |
| loaded.add(cls); |
| // This call may be unnecessary? |
| _factory.load(cls, MODE_ALL, multi); |
| } catch (PrivilegedActionException pae) { |
| throw new MetaDataException(_loc.get("repos-initializeEager-error"), pae); |
| } |
| } |
| resolveAll(multi); |
| |
| // Preload XML MetaData |
| for (Class<?> cls : loaded) { |
| ClassMetaData cmd = getCachedMetaData(cls); |
| if (cmd != null) { |
| getXMLMetaData(cls); |
| for (FieldMetaData fmd : cmd.getFields()) { |
| getXMLMetaData(fmd.getDeclaredType()); |
| } |
| } |
| } |
| |
| // Hook in this class as a listener and process registered classes list to populate _aliases |
| // list. |
| PCRegistry.addRegisterClassListener(this); |
| processRegisteredClasses(multi); |
| _locking = false; |
| _preloadComplete = true; |
| } |
| |
| |
| /** |
| * Return the metadata for the given class. |
| * |
| * @param cls |
| * the class to retrieve metadata for |
| * @param envLoader |
| * the environmental class loader, if any |
| * @param mustExist |
| * if true, throws a {@link MetaDataException} if no metadata is found |
| */ |
| public ClassMetaData getMetaData(Class<?> cls, ClassLoader envLoader, boolean mustExist) { |
| if (_locking) { |
| synchronized(this){ |
| return getMetaDataInternal(cls, envLoader, mustExist); |
| } |
| } else { |
| return getMetaDataInternal(cls, envLoader, mustExist); |
| } |
| } |
| |
| private ClassMetaData getMetaDataInternal(Class<?> cls, ClassLoader envLoader, boolean mustExist) { |
| ClassMetaData meta = getMetaDataInternal(cls, envLoader); |
| if (meta == null) { |
| if (cls != null && DynamicPersistenceCapable.class.isAssignableFrom(cls)) |
| cls = cls.getSuperclass(); |
| |
| // if cls is a generated interface, use the user interface |
| // to locate metadata |
| if (cls != null && _implGen != null && _implGen.isImplType(cls)) |
| cls = _implGen.toManagedInterface(cls); |
| meta = getMetaDataInternal(cls, envLoader); |
| } |
| if (meta == null && mustExist) { |
| if (cls != null && !ImplHelper.isManagedType(_conf, cls)) |
| throw new MetaDataException(_loc.get("no-meta-notpc", cls)).setFatal(false); |
| |
| Set<String> pcNames = getPersistentTypeNames(false, envLoader); |
| if (pcNames != null && pcNames.size() > 0) |
| throw new MetaDataException(_loc.get("no-meta-types", cls, pcNames)); |
| |
| throw new MetaDataException(_loc.get("no-meta", cls)); |
| } |
| resolve(meta); |
| return meta; |
| } |
| |
| /** |
| * Return the metadata for the given alias name. |
| * |
| * @param alias |
| * the alias to class to retrieve metadata for |
| * @param envLoader |
| * the environmental class loader, if any |
| * @param mustExist |
| * if true, throws a {@link MetaDataException} if no metadata is found |
| * @see ClassMetaData#getTypeAlias |
| */ |
| public ClassMetaData getMetaData(String alias, ClassLoader envLoader, boolean mustExist) { |
| if (alias == null && mustExist) |
| throw new MetaDataException(_loc.get("no-alias-meta", alias, _aliases)); |
| if (alias == null) |
| return null; |
| |
| // check cache |
| processRegisteredClasses(envLoader); |
| List<Class<?>> classList = _aliases.get(alias); |
| |
| // multiple classes may have been defined with the same alias: we |
| // will filter by checking against the current list of the |
| // persistent types and filter based on which classes are loadable |
| // via the current environment's ClassLoader |
| Set<String> pcNames = getPersistentTypeNames(false, envLoader); |
| Class<?> cls = null; |
| for (int i = 0; classList != null && i < classList.size(); i++) { |
| Class<?> c = classList.get(i); |
| try { |
| // re-load the class in the current environment loader so |
| // that we can handle redeployment of the same class name |
| Class<?> nc = Class.forName(c.getName(), false, envLoader); |
| |
| // if we have specified a list of persistent clases, |
| // also check to ensure that the class is in that list |
| if (pcNames == null || pcNames.size() == 0 || pcNames.contains(nc.getName())) { |
| cls = nc; |
| if (!classList.contains(cls)) |
| classList.add(cls); |
| break; |
| } |
| } catch (Throwable t) { |
| // this happens when the class is not loadable by |
| // the environment class loader, so it was probably |
| // listed elsewhere; also ignore linkage failures and |
| // other class loading problems |
| } |
| } |
| if (cls != null) |
| return getMetaData(cls, envLoader, mustExist); |
| |
| // maybe this is some type we've seen but just isn't valid |
| if (_aliases.containsKey(alias)) { |
| if (mustExist) |
| throwNoRegisteredAlias(alias); |
| return null; |
| } |
| |
| // We need to synchronize on _aliases because a ConcurrentModificationException can if there |
| // is a thread in getAliasNames() AND this class isn't using any locking. |
| synchronized (_aliases) { |
| // record that this is an invalid type |
| _aliases.put(alias, null); |
| } |
| |
| if (!mustExist) |
| return null; |
| return throwNoRegisteredAlias(alias); |
| } |
| |
| private ClassMetaData throwNoRegisteredAlias(String alias) { |
| String close = getClosestAliasName(alias); |
| if (close != null) |
| throw new MetaDataException(_loc.get("no-alias-meta-hint", alias, _aliases, close)); |
| else |
| throw new MetaDataException(_loc.get("no-alias-meta", alias, _aliases)); |
| } |
| |
| /** |
| * @return the nearest match to the specified alias name |
| * @since 1.1.0 |
| */ |
| public String getClosestAliasName(String alias) { |
| Collection<String> aliases = getAliasNames(); |
| return StringDistance.getClosestLevenshteinDistance(alias, aliases); |
| } |
| |
| /** |
| * @return the registered alias names |
| * @since 1.1.0 |
| */ |
| public Collection<String> getAliasNames() { |
| if (_locking) { |
| synchronized (_aliases) { |
| return getAliasNamesInternal(); |
| } |
| } else { |
| return getAliasNamesInternal(); |
| } |
| } |
| |
| private Collection<String> getAliasNamesInternal() { |
| Collection<String> aliases = new HashSet<>(); |
| for(Map.Entry<String, List<Class<?>>> e : _aliases.entrySet()){ |
| if (e.getValue() != null) { |
| aliases.add(e.getKey()); |
| } |
| } |
| return aliases; |
| } |
| |
| /** |
| * Internal method to get the metadata for the given class, without resolving it. |
| */ |
| private ClassMetaData getMetaDataInternal(Class<?> cls, ClassLoader envLoader) { |
| if (cls == null) |
| return null; |
| |
| // check cache for existing metadata, or give up if no metadata and |
| // our list of configured persistent types doesn't include the class |
| ClassMetaData meta = _metas.get(cls); |
| if (meta != null && ((meta.getSourceMode() & MODE_META) != 0 || (_sourceMode & MODE_META) == 0)) |
| return meta; |
| |
| // if runtime, cut off search if not in pc list. we don't do this at |
| // dev time so that user can manipulate persistent classes he's writing |
| // before adding them to the list |
| if ((_validate & VALIDATE_RUNTIME) != 0) { |
| Set<String> pcNames = getPersistentTypeNames(false, envLoader); |
| if (pcNames != null && !pcNames.contains(cls.getName())) |
| return meta; |
| } |
| |
| if (meta == null) { |
| // check to see if maybe we know this class has no metadata |
| if (_metas.containsKey(cls)) |
| return null; |
| |
| // make sure this isn't an obviously bad class |
| if (cls.isPrimitive() || cls.getName().startsWith("java.") || cls == PersistenceCapable.class) |
| return null; |
| |
| // designed to get around jikes 1.17 / JDK1.5 issue where static |
| // initializers are not invoked when a class is referenced, so the |
| // class never registers itself with the system |
| if ((_validate & VALIDATE_RUNTIME) != 0) { |
| try { |
| Class.forName(cls.getName(), true, AccessController.doPrivileged(J2DoPrivHelper |
| .getClassLoaderAction(cls))); |
| } catch (Throwable t) { |
| } |
| } |
| } |
| |
| // not in cache: load metadata or mappings depending on source mode. |
| // loading metadata might also load mappings, but doesn't have to |
| int mode = 0; |
| if ((_sourceMode & MODE_META) != 0) |
| mode = _sourceMode & ~MODE_MAPPING; |
| else if ((_sourceMode & MODE_MAPPING) == 0) |
| mode = _sourceMode; |
| if (mode != MODE_NONE) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("load-cls", cls, toModeString(mode))); |
| _factory.load(cls, mode, envLoader); |
| } |
| |
| // check cache again |
| if (meta == null) |
| meta = _metas.get(cls); |
| if (meta != null && ((meta.getSourceMode() & MODE_META) != 0 || (_sourceMode & MODE_META) == 0)) |
| return meta; |
| |
| // record that this class has no metadata; checking for this later |
| // speeds things up in environments with slow class loading |
| // like appservers |
| if (meta != null) |
| removeMetaData(meta); |
| _metas.put(cls, null); |
| return null; |
| } |
| |
| /** |
| * Return a string representation of the given mode flags. |
| */ |
| private static String toModeString(int mode) { |
| StringBuilder buf = new StringBuilder(31); |
| if ((mode & MODE_META) != 0) |
| buf.append("[META]"); |
| if ((mode & MODE_QUERY) != 0) |
| buf.append("[QUERY]"); |
| if ((mode & MODE_MAPPING) != 0) |
| buf.append("[MAPPING]"); |
| if ((mode & MODE_MAPPING_INIT) != 0) |
| buf.append("[MAPPING_INIT]"); |
| return buf.toString(); |
| } |
| |
| /** |
| * Prepare metadata for mapping resolution. This method might map parts of the metadata that |
| * don't rely on other classes being mapped, but that other classes might rely on during their |
| * own mapping (for example, primary key fields). By default, this method only calls |
| * {@link ClassMetaData#defineSuperclassFields}. |
| */ |
| protected void prepareMapping(ClassMetaData meta) { |
| meta.defineSuperclassFields(false); |
| } |
| |
| /** |
| * Resolve the given metadata if needed. There are three goals: |
| * <ol> |
| * <li>Make sure no unresolved metadata gets back to the client.</li> |
| * <li>Avoid infinite reentrant calls for mutually-dependent metadatas by allowing unresolved |
| * metadata to be returned to other metadatas.</li> |
| * <li>Always make sure the superclass metadata is resolved before the subclass metadata so that |
| * the subclass can access the super's list of fields.</li> |
| * </ol> |
| * Note that the code calling this method is synchronized, so this method doesn't have to be. |
| */ |
| private void resolve(ClassMetaData meta) { |
| // return anything that has its metadata resolved, because that means |
| // it is either fully resolved or must at least be in the process of |
| // resolving mapping, etc since we do that right after meta resolve |
| if (meta == null || _resMode == MODE_NONE || (meta.getResolve() & MODE_META) != 0) |
| return; |
| |
| // resolve metadata |
| List<ClassMetaData> resolved = resolveMeta(meta); |
| if (resolved == null) |
| return; |
| |
| // load mapping data |
| for (ClassMetaData data : resolved) { |
| loadMapping(data); |
| } |
| for (ClassMetaData metaData : resolved) { |
| preMapping(metaData); |
| } |
| |
| // resolve mappings |
| boolean err = true; |
| if ((_resMode & MODE_MAPPING) != 0) |
| for (ClassMetaData classMetaData : resolved) { |
| err &= resolveMapping(classMetaData); |
| } |
| |
| // throw errors encountered |
| // OPENJPA-1535 Always throw a MetaDataException because callers |
| // of loadRegisteredClassMetaData expect only MetaDataException |
| // to be thrown. |
| if (err && !_errs.isEmpty()) { |
| RuntimeException re; |
| if ((_errs.size() == 1) && (_errs.get(0) instanceof MetaDataException)) { |
| re = _errs.get(0); |
| } else { |
| re = new MetaDataException(_loc.get("resolve-errs")) |
| .setNestedThrowables(_errs |
| .toArray(new Exception[_errs.size()])); |
| } |
| _errs.clear(); |
| throw re; |
| } |
| } |
| |
| /** |
| * Resolve metadata mode, returning list of processed metadadatas, or null if we're still in the |
| * process of resolving other metadatas. |
| */ |
| private List<ClassMetaData> resolveMeta(ClassMetaData meta) { |
| if (meta.getPCSuperclass() == null) { |
| // set superclass |
| Class<?> sup = meta.getDescribedType().getSuperclass(); |
| ClassMetaData supMeta; |
| while (sup != null && sup != Object.class) { |
| supMeta = getMetaData(sup, meta.getEnvClassLoader(), false); |
| if (supMeta != null) { |
| meta.setPCSuperclass(sup); |
| meta.setPCSuperclassMetaData(supMeta); |
| break; |
| } else |
| sup = sup.getSuperclass(); |
| } |
| if (meta.getDescribedType().isInterface()) { |
| Class<?>[] sups = meta.getDescribedType().getInterfaces(); |
| for (Class<?> aClass : sups) { |
| supMeta = getMetaData(aClass, meta.getEnvClassLoader(), false); |
| if (supMeta != null) { |
| meta.setPCSuperclass(sup); |
| meta.setPCSuperclassMetaData(supMeta); |
| break; |
| } |
| } |
| } |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("assigned-sup", meta, meta.getPCSuperclass())); |
| } |
| |
| // resolve relation primary key fields for mapping dependencies |
| FieldMetaData[] fmds = meta.getDeclaredFields(); |
| for (FieldMetaData fmd : fmds) |
| if (fmd.isPrimaryKey()) |
| getMetaData(fmd.getDeclaredType(), meta.getEnvClassLoader(), false); |
| |
| // resolve metadata; if we're not in the process of resolving |
| // others, this will return the set of interrelated metas that |
| // resolved |
| return processBuffer(meta, _resolving, MODE_META); |
| } |
| |
| /** |
| * Load mapping information for the given metadata. |
| */ |
| private void loadMapping(ClassMetaData meta) { |
| if ((meta.getResolve() & MODE_MAPPING) != 0) |
| return; |
| |
| // load mapping information |
| if ((meta.getSourceMode() & MODE_MAPPING) == 0 && (_sourceMode & MODE_MAPPING) != 0) { |
| // embedded-only metadata doesn't have mapping, so always loaded |
| if (meta.isEmbeddedOnly()) |
| meta.setSourceMode(MODE_MAPPING, true); |
| else { |
| // load mapping data |
| int mode = _sourceMode & ~MODE_META; |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("load-mapping", meta, toModeString(mode))); |
| try { |
| _factory.load(meta.getDescribedType(), mode, meta.getEnvClassLoader()); |
| } catch (RuntimeException re) { |
| removeMetaData(meta); |
| _errs.add(re); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Pre-mapping preparation. |
| */ |
| private void preMapping(ClassMetaData meta) { |
| if ((meta.getResolve() & MODE_MAPPING) != 0) |
| return; |
| |
| // prepare mappings for resolve; if not resolving mappings, then |
| // make sure any superclass fields defined in metadata are resolved |
| try { |
| if ((_resMode & MODE_MAPPING) != 0) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("prep-mapping", meta)); |
| prepareMapping(meta); |
| } else |
| meta.defineSuperclassFields(false); |
| } catch (RuntimeException re) { |
| removeMetaData(meta); |
| _errs.add(re); |
| } |
| } |
| |
| /** |
| * Resolve and initialize mapping. |
| * |
| * @return false if we're still in the process of resolving mappings |
| */ |
| private boolean resolveMapping(ClassMetaData meta) { |
| List<ClassMetaData> mapped = processBuffer(meta, _mapping, MODE_MAPPING); |
| if (mapped == null) |
| return false; |
| |
| // initialize mapping for runtime use |
| if ((_resMode & MODE_MAPPING_INIT) != 0) { |
| for (ClassMetaData classMetaData : mapped) { |
| meta = classMetaData; |
| try { |
| meta.resolve(MODE_MAPPING_INIT); |
| } |
| catch (RuntimeException re) { |
| removeMetaData(meta); |
| _errs.add(re); |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Process the given metadata and the associated buffer. |
| */ |
| private List<ClassMetaData> processBuffer(ClassMetaData meta, List<ClassMetaData> buffer, int mode) { |
| // add the metadata to the buffer unless an instance for the same entity |
| // is already there |
| for (ClassMetaData cmd : buffer) |
| if (cmd.getDescribedType().equals(meta.getDescribedType())) |
| return null; |
| |
| // if we're already processing a metadata, just buffer this one; when |
| // the initial metadata finishes processing, we traverse the buffer |
| // and process all the others that were introduced during reentrant |
| // calls |
| buffer.add(meta); |
| if (buffer.size() != 1) |
| return null; |
| |
| // continually pop a metadata and process it until we run out; note |
| // that each processing call might place more metas in the buffer as |
| // one class tries to access metadata for another |
| ClassMetaData buffered; |
| List<ClassMetaData> processed = new ArrayList<>(5); |
| while (!buffer.isEmpty()) { |
| buffered = buffer.get(0); |
| try { |
| buffered.resolve(mode); |
| processed.add(buffered); |
| buffer.remove(buffered); |
| } catch (RuntimeException re) { |
| _errs.add(re); |
| |
| // any exception during resolution of one type means we can't |
| // resolve any of the related types, so clear buffer. this also |
| // ensures that if two types relate to each other and one |
| // dies, we don't get into infinite cycles |
| for (ClassMetaData cmd : buffer) { |
| removeMetaData(cmd); |
| if (cmd != buffered) { |
| _errs.add(new MetaDataException(_loc.get("prev-errs", cmd, buffered))); |
| } |
| } |
| buffer.clear(); |
| } |
| } |
| |
| return processed; |
| } |
| |
| /** |
| * Return all the metadata instances currently in the repository. |
| */ |
| public ClassMetaData[] getMetaDatas() { |
| if (_locking) { |
| synchronized(this){ |
| return getMetaDatasInternal(); |
| } |
| } else { |
| return getMetaDatasInternal(); |
| } |
| } |
| |
| private ClassMetaData[] getMetaDatasInternal() { |
| // prevent concurrent mod errors when resolving one metadata |
| // introduces others |
| ClassMetaData[] metas = _metas.values().toArray(new ClassMetaData[_metas.size()]); |
| for (ClassMetaData classMetaData : metas) |
| if (classMetaData != null) |
| getMetaData(classMetaData.getDescribedType(), classMetaData.getEnvClassLoader(), true); |
| |
| List<ClassMetaData> resolved = new ArrayList<>(_metas.size()); |
| for (ClassMetaData meta : _metas.values()) { |
| if (meta != null) |
| resolved.add(meta); |
| } |
| metas = resolved.toArray(newClassMetaDataArray(resolved.size())); |
| Arrays.sort(metas); |
| return metas; |
| } |
| |
| /** |
| * Return the cached metadata for the given class, without any resolution. Return null if none. |
| */ |
| public ClassMetaData getCachedMetaData(Class<?> cls) { |
| return _metas.get(cls); |
| } |
| |
| /** |
| * Create a new metadata, populate it with default information, add it to the repository, and |
| * return it. Use the default access type. |
| */ |
| public ClassMetaData addMetaData(Class<?> cls) { |
| return addMetaData(cls, AccessCode.UNKNOWN); |
| } |
| |
| /** |
| * Create a new metadata, populate it with default information, add it to the repository, and |
| * return it. |
| * |
| * @param access |
| * the access type to use in populating metadata |
| */ |
| public ClassMetaData addMetaData(Class<?> cls, int access) { |
| return addMetaData(cls, access, false); |
| } |
| |
| /** |
| * Create a new metadata, populate it with default information, add it to the repository, and |
| * return it. |
| * |
| * @param access |
| * the access type to use in populating metadata |
| */ |
| public ClassMetaData addMetaData(Class<?> cls, int access, boolean ignoreTransient) { |
| if (cls == null || cls.isPrimitive()) |
| return null; |
| |
| ClassMetaData meta = newClassMetaData(cls); |
| _factory.getDefaults().populate(meta, access, ignoreTransient); |
| |
| // synchronize on this rather than the map, because all other methods |
| // that access _metas are synchronized on this |
| if (_locking) { |
| synchronized(this){ |
| return metasPutInternal(cls, meta); |
| } |
| } else { |
| return metasPutInternal(cls, meta); |
| } |
| |
| } |
| |
| private ClassMetaData metasPutInternal(Class<?> cls, ClassMetaData meta){ |
| if (_pawares.containsKey(cls)) |
| throw new MetaDataException(_loc.get("pc-and-aware", cls)); |
| _metas.put(cls, meta); |
| return meta; |
| } |
| |
| /** |
| * Create a new class metadata instance. |
| */ |
| protected ClassMetaData newClassMetaData(Class<?> type) { |
| return new ClassMetaData(type, this); |
| } |
| |
| /** |
| * Create a new array of the proper class metadata subclass. |
| */ |
| protected ClassMetaData[] newClassMetaDataArray(int length) { |
| return new ClassMetaData[length]; |
| } |
| |
| /** |
| * Create a new field metadata instance. |
| */ |
| protected FieldMetaData newFieldMetaData(String name, Class<?> type, ClassMetaData owner) { |
| return new FieldMetaData(name, type, owner); |
| } |
| |
| /** |
| * Create a new array of the proper field metadata subclass. |
| */ |
| protected FieldMetaData[] newFieldMetaDataArray(int length) { |
| return new FieldMetaData[length]; |
| } |
| |
| /** |
| * Create a new array of the proper xml class metadata subclass. |
| */ |
| protected XMLMetaData[] newXMLClassMetaDataArray(int length) { |
| return new XMLClassMetaData[length]; |
| } |
| |
| /** |
| * Create a new embedded class metadata instance. |
| */ |
| protected ClassMetaData newEmbeddedClassMetaData(ValueMetaData owner) { |
| return new ClassMetaData(owner); |
| } |
| |
| /** |
| * Create a new value metadata instance. |
| */ |
| protected ValueMetaData newValueMetaData(FieldMetaData owner) { |
| return new ValueMetaDataImpl(owner); |
| } |
| |
| /** |
| * Create an {@link Order} for the given field and declaration. This method delegates to |
| * {@link #newRelatedFieldOrder} and {@link #newValueOrder(FieldMetaData, boolean)} by default. |
| */ |
| protected Order newOrder(FieldMetaData owner, String name, boolean asc) { |
| // paths can start with (or equal) '#element' |
| if (name.startsWith(Order.ELEMENT)) |
| name = name.substring(Order.ELEMENT.length()); |
| if (name.length() == 0) |
| return newValueOrder(owner, asc); |
| |
| // next token should be '.' |
| if (name.charAt(0) == '.') |
| name = name.substring(1); |
| |
| // related field |
| ClassMetaData meta = owner.getElement().getTypeMetaData(); |
| if (meta == null) |
| throw new MetaDataException(_loc.get("nonpc-field-orderable", owner, name)); |
| FieldMetaData rel = getOrderByField(meta, name); |
| if (rel == null) |
| throw new MetaDataException(_loc.get("bad-field-orderable", owner, name)); |
| return newRelatedFieldOrder(owner, rel, asc); |
| } |
| |
| public FieldMetaData getOrderByField(ClassMetaData meta, String orderBy) { |
| FieldMetaData field = meta.getField(orderBy); |
| if (field != null) |
| return field; |
| int dotIdx = orderBy.indexOf("."); |
| if (dotIdx == -1) |
| return null; |
| String fieldName = orderBy.substring(0, dotIdx); |
| FieldMetaData field1 = meta.getField(fieldName); |
| if (field1 == null) |
| return null; |
| ClassMetaData meta1 = field1.getEmbeddedMetaData(); |
| if (meta1 == null) |
| return null; |
| String mappedBy1 = orderBy.substring(dotIdx + 1); |
| return getOrderByField(meta1, mappedBy1); |
| } |
| |
| /** |
| * Order by the field value. |
| */ |
| protected Order newValueOrder(FieldMetaData owner, boolean asc) { |
| return new InMemoryValueOrder(asc, getConfiguration()); |
| } |
| |
| /** |
| * Order by a field of the related type. |
| */ |
| protected Order newRelatedFieldOrder(FieldMetaData owner, FieldMetaData rel, boolean asc) { |
| return new InMemoryRelatedFieldOrder(rel, asc, getConfiguration()); |
| } |
| |
| /** |
| * Create an array of orders of the given size. |
| */ |
| protected Order[] newOrderArray(int size) { |
| return new Order[size]; |
| } |
| |
| /** |
| * Remove a metadata instance from the repository. |
| * |
| * @return true if removed, false if not in this repository |
| */ |
| public boolean removeMetaData(ClassMetaData meta) { |
| if (meta == null) |
| return false; |
| return removeMetaData(meta.getDescribedType()); |
| } |
| |
| /** |
| * Remove a metadata instance from the repository. |
| * |
| * @return true if removed, false if not in this repository |
| */ |
| public boolean removeMetaData(Class<?> cls) { |
| if(_locking){ |
| synchronized(this){ |
| return removeMetaDataInternal(cls); |
| } |
| }else{ |
| return removeMetaDataInternal(cls); |
| } |
| } |
| |
| private boolean removeMetaDataInternal(Class<?> cls) { |
| if (cls == null) |
| return false; |
| if (_metas.remove(cls) != null) { |
| Class<?> impl = _ifaces.remove(cls); |
| if (impl != null) |
| _metas.remove(impl); |
| return true; |
| } |
| return false; |
| } |
| /** |
| * Add the given metadata as declared interface implementation. |
| */ |
| void addDeclaredInterfaceImpl(ClassMetaData meta, Class<?> iface) { |
| if (_locking) { |
| synchronized (_impls) { |
| addDeclaredInterfaceImplInternal(meta, iface); |
| } |
| } else { |
| addDeclaredInterfaceImplInternal(meta, iface); |
| } |
| } |
| |
| private void addDeclaredInterfaceImplInternal(ClassMetaData meta, Class<?> iface) { |
| Collection<Class<?>> vals = _impls.get(iface); |
| |
| // check to see if the superclass already declares to avoid dups |
| if (vals != null) { |
| ClassMetaData sup = meta.getPCSuperclassMetaData(); |
| for (; sup != null; sup = sup.getPCSuperclassMetaData()) |
| if (vals.contains(sup.getDescribedType())) |
| return; |
| } |
| addToCollection(_impls, iface, meta.getDescribedType(), false); |
| } |
| /** |
| * Set the implementation for the given managed interface. |
| */ |
| void setInterfaceImpl(ClassMetaData meta, Class<?> impl) { |
| if (_locking) { |
| synchronized (this) { |
| setInterfaceImplInternal(meta, impl); |
| } |
| } else { |
| setInterfaceImplInternal(meta, impl); |
| } |
| } |
| |
| private void setInterfaceImplInternal(ClassMetaData meta, Class<?> impl) { |
| if (!meta.isManagedInterface()) |
| throw new MetaDataException(_loc.get("not-managed-interface", meta, impl)); |
| _ifaces.put(meta.getDescribedType(), impl); |
| addDeclaredInterfaceImpl(meta, meta.getDescribedType()); |
| ClassMetaData sup = meta.getPCSuperclassMetaData(); |
| while (sup != null) { |
| // record superclass interface info while we can as well as we |
| // will only register concrete superclass in PCRegistry |
| sup.clearSubclassCache(); |
| addToCollection(_subs, sup.getDescribedType(), impl, true); |
| sup = sup.getPCSuperclassMetaData(); |
| } |
| } |
| |
| InterfaceImplGenerator getImplGenerator() { |
| return _implGen; |
| } |
| |
| /** |
| * Return the least-derived class metadata for the given application identity object. |
| * |
| * @param oid |
| * the oid to get the metadata for |
| * @param envLoader |
| * the environmental class loader, if any |
| * @param mustExist |
| * if true, throws a {@link MetaDataException} if no metadata is found |
| */ |
| public ClassMetaData getMetaData(Object oid, ClassLoader envLoader, boolean mustExist) { |
| if (oid == null && mustExist) |
| throw new MetaDataException(_loc.get("no-oid-meta", oid, "?", _oids.toString())); |
| if (oid == null) |
| return null; |
| |
| if (oid instanceof OpenJPAId) { |
| Class<?> cls = ((OpenJPAId) oid).getType(); |
| return getMetaData(cls, envLoader, mustExist); |
| } |
| |
| // check cache |
| processRegisteredClasses(envLoader); |
| Class<?> cls = _oids.get(oid.getClass()); |
| if (cls != null) |
| return getMetaData(cls, envLoader, mustExist); |
| |
| // maybe this is some type we've seen but just isn't valid |
| if (_oids.containsKey(oid.getClass())) { |
| if (mustExist) |
| throw new MetaDataException(_loc.get("no-oid-meta", oid, oid.getClass(), _oids)); |
| return null; |
| } |
| |
| // if still not match, register any classes that look similar to the |
| // oid class and check again |
| resolveIdentityClass(oid); |
| if (processRegisteredClasses(envLoader).length > 0) { |
| cls = _oids.get(oid.getClass()); |
| if (cls != null) |
| return getMetaData(cls, envLoader, mustExist); |
| } |
| |
| // record that this is an invalid type |
| _oids.put(oid.getClass(), null); |
| |
| if (!mustExist) |
| return null; |
| throw new MetaDataException(_loc.get("no-oid-meta", oid, oid.getClass(), _oids)).setFailedObject(oid); |
| } |
| |
| /** |
| * Make some guesses about the name of a target class for an unknown application identity class. |
| */ |
| private void resolveIdentityClass(Object oid) { |
| if (oid == null) |
| return; |
| |
| Class<?> oidClass = oid.getClass(); |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("resolve-identity", oidClass)); |
| |
| ClassLoader cl = AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(oidClass)); |
| String className; |
| while (oidClass != null && oidClass != Object.class) { |
| className = oidClass.getName(); |
| |
| // we take a brute-force approach: try to load all the class' |
| // substrings. this will handle the following common naming cases: |
| // |
| // com.company.MyClass$ID -> com.company.MyClass |
| // com.company.MyClassId -> com.company.MyClass |
| // com.company.MyClassOid -> com.company.MyClass |
| // com.company.MyClassPK -> com.company.MyClass |
| // |
| // this isn't the fastest thing possible, but this method will |
| // only be called once per JVM per unknown app id class |
| for (int i = className.length(); i > 1; i--) { |
| if (className.charAt(i - 1) == '.') |
| break; |
| |
| try { |
| Class.forName(className.substring(0, i), true, cl); |
| } catch (Exception e) { |
| } // consume all exceptions |
| } |
| |
| // move up the OID hierarchy |
| oidClass = oidClass.getSuperclass(); |
| } |
| } |
| |
| /** |
| * Return all least-derived metadatas with some mapped assignable type that implement the given |
| * class. |
| * |
| * @param cls |
| * the class or interface to retrieve implementors for |
| * @param envLoader |
| * the environmental class loader, if any |
| * @param mustExist |
| * if true, throws a {@link MetaDataException} if no metadata is found |
| */ |
| public ClassMetaData[] getImplementorMetaDatas(Class<?> cls, ClassLoader envLoader, boolean mustExist) { |
| if (cls == null && mustExist) |
| throw new MetaDataException(_loc.get("no-meta", cls)); |
| if (cls == null) |
| return EMPTY_METAS; |
| |
| // get impls of given interface / abstract class |
| loadRegisteredClassMetaData(envLoader); |
| Collection<Class<?>> vals = _impls.get(cls); |
| ClassMetaData[] mapped = null; |
| if (vals != null) { |
| if (_locking) { |
| synchronized (vals) { |
| mapped = getImplementorMetaDatasInternal(vals, envLoader, mustExist); |
| } |
| } else { |
| mapped = getImplementorMetaDatasInternal(vals, envLoader, mustExist); |
| } |
| } |
| |
| if (mapped == null && mustExist) |
| throw new MetaDataException(_loc.get("no-meta", cls)); |
| if (mapped == null) |
| return EMPTY_METAS; |
| return mapped; |
| } |
| |
| private ClassMetaData[] getImplementorMetaDatasInternal(Collection<Class<?>> classes, ClassLoader envLoader, |
| boolean mustExist) { |
| Collection<ClassMetaData> mapped = new ArrayList<>(classes.size()); |
| ClassMetaData meta = null; |
| for (Class<?> c : classes) { |
| meta = getMetaData(c, envLoader, true); |
| if (meta.isMapped() || meta.getMappedPCSubclassMetaDatas().length > 0) { |
| mapped.add(meta); |
| } |
| } |
| return mapped.toArray(new ClassMetaData[mapped.size()]); |
| } |
| /** |
| * Gets the metadata corresponding to the given persistence-aware class. Returns null, if the |
| * given class is not registered as persistence-aware. |
| */ |
| public NonPersistentMetaData getPersistenceAware(Class<?> cls) { |
| return _pawares.get(cls); |
| } |
| |
| public boolean skipMetadata(final Class<?> cls) { |
| if (cls == null || cls.isEnum()) { |
| return true; |
| } |
| if (_typesWithoutEnhancement == null) { |
| return false; |
| } |
| return _typesWithoutEnhancement.stream().anyMatch(it -> it.isAssignableFrom(cls)); |
| } |
| |
| /** |
| * Gets all the metadatas for persistence-aware classes |
| * |
| * @return empty array if no class has been registered as pers-aware |
| */ |
| public NonPersistentMetaData[] getPersistenceAwares() { |
| if (_locking) { |
| synchronized (_pawares) { |
| return getPersistenceAwaresInternal(); |
| } |
| } else { |
| return getPersistenceAwaresInternal(); |
| } |
| } |
| |
| private NonPersistentMetaData[] getPersistenceAwaresInternal() { |
| if (_pawares.isEmpty()) |
| return EMPTY_NON_PERSISTENT; |
| return _pawares.values().toArray(new NonPersistentMetaData[_pawares.size()]); |
| } |
| |
| /** |
| * Add the given class as persistence-aware. |
| * |
| * @param cls |
| * non-null and must not alreaddy be added as persitence-capable |
| */ |
| public NonPersistentMetaData addPersistenceAware(Class<?> cls) { |
| if (cls == null) |
| return null; |
| if (_locking) { |
| synchronized (this) { |
| return addPersistenceAwareInternal(cls); |
| } |
| } else { |
| return addPersistenceAwareInternal(cls); |
| } |
| } |
| |
| private NonPersistentMetaData addPersistenceAwareInternal(Class<?> cls) { |
| if (_pawares.containsKey(cls)) |
| return _pawares.get(cls); |
| if (getCachedMetaData(cls) != null) |
| throw new MetaDataException(_loc.get("pc-and-aware", cls)); |
| NonPersistentMetaData meta = |
| new NonPersistentMetaData(cls, this, NonPersistentMetaData.TYPE_PERSISTENCE_AWARE); |
| _pawares.put(cls, meta); |
| return meta; |
| } |
| |
| /** |
| * Remove a persitence-aware class from the repository |
| * |
| * @return true if removed |
| */ |
| public boolean removePersistenceAware(Class<?> cls) { |
| return _pawares.remove(cls) != null; |
| } |
| |
| /** |
| * Gets the metadata corresponding to the given non-mapped interface. Returns null, if the given |
| * interface is not registered as persistence-aware. |
| */ |
| public NonPersistentMetaData getNonMappedInterface(Class<?> iface) { |
| return _nonMapped.get(iface); |
| } |
| |
| /** |
| * Gets the corresponding metadatas for all registered, non-mapped interfaces |
| * |
| * @return empty array if no non-mapped interface has been registered. |
| */ |
| public NonPersistentMetaData[] getNonMappedInterfaces() { |
| if (_locking) { |
| synchronized (_nonMapped) { |
| return getNonMappedInterfacesInternal(); |
| } |
| } else { |
| return getNonMappedInterfacesInternal(); |
| } |
| } |
| |
| private NonPersistentMetaData[] getNonMappedInterfacesInternal() { |
| if (_nonMapped.isEmpty()) |
| return EMPTY_NON_PERSISTENT; |
| return _nonMapped.values().toArray(new NonPersistentMetaData[_nonMapped.size()]); |
| } |
| |
| /** |
| * Add the given non-mapped interface to the repository. |
| * |
| * @param iface |
| * the non-mapped interface |
| */ |
| public NonPersistentMetaData addNonMappedInterface(Class<?> iface) { |
| if (iface == null) |
| return null; |
| if (!iface.isInterface()) |
| throw new MetaDataException(_loc.get("not-non-mapped", iface)); |
| if (_locking) { |
| synchronized (this) { |
| return addNonMappedInterfaceInternal(iface); |
| } |
| } else { |
| return addNonMappedInterfaceInternal(iface); |
| } |
| } |
| |
| private NonPersistentMetaData addNonMappedInterfaceInternal(Class<?> iface) { |
| if (_nonMapped.containsKey(iface)) |
| return _nonMapped.get(iface); |
| if (getCachedMetaData(iface) != null) |
| throw new MetaDataException(_loc.get("non-mapped-pc", iface)); |
| NonPersistentMetaData meta = |
| new NonPersistentMetaData(iface, this, NonPersistentMetaData.TYPE_NON_MAPPED_INTERFACE); |
| _nonMapped.put(iface, meta); |
| return meta; |
| } |
| |
| /** |
| * Remove a non-mapped interface from the repository |
| * |
| * @return true if removed |
| */ |
| public boolean removeNonMappedInterface(Class<?> iface) { |
| return _nonMapped.remove(iface) != null; |
| } |
| |
| /** |
| * Clear the cache of parsed metadata. This method also clears the internal |
| * {@link MetaDataFactory MetaDataFactory}'s cache. |
| */ |
| public void clear() { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("clear-repos", this)); |
| if (_locking) { |
| synchronized (this) { |
| clearInternal(); |
| } |
| } else { |
| clearInternal(); |
| } |
| } |
| |
| private void clearInternal(){ |
| // Recreating these datastructures is probably faster than calling clear. Future change? |
| _metas.clear(); |
| _oids.clear(); |
| _subs.clear(); |
| _impls.clear(); |
| _queries.clear(); |
| _seqs.clear(); |
| _registered.clear(); |
| _factory.clear(); |
| _aliases.clear(); |
| _pawares.clear(); |
| _nonMapped.clear(); |
| _metaStringMap.clear(); |
| } |
| /** |
| * Return the set of configured persistent classes, or null if the user did not configure any. |
| * |
| * @param devpath |
| * if true, search for metadata files in directories in the classpath if no classes |
| * are configured explicitly |
| * @param envLoader |
| * the class loader to use, or null for default |
| */ |
| public Set<String> getPersistentTypeNames(boolean devpath, ClassLoader envLoader) { |
| if (_locking) { |
| synchronized (this) { |
| return getPersistentTypeNamesInternal(devpath, envLoader); |
| } |
| } else { |
| return getPersistentTypeNamesInternal(devpath, envLoader); |
| } |
| } |
| |
| private Set<String> getPersistentTypeNamesInternal(boolean devpath, ClassLoader envLoader) { |
| return _factory.getPersistentTypeNames(devpath, envLoader); |
| } |
| /** |
| * Load the persistent classes named in configuration. |
| * This ensures that all subclasses and application identity classes of |
| * each type are known in advance, without having to rely on the |
| * application loading the classes before performing operations that |
| * might involve them. |
| * |
| * @param devpath if true, search for metadata files in directories |
| * in the classpath if the no classes are configured explicitly |
| * @param envLoader the class loader to use, or null for default |
| * @return the loaded classes, or empty collection if none |
| */ |
| public Collection<Class<?>> loadPersistentTypes(boolean devpath, ClassLoader envLoader) { |
| return loadPersistentTypes(devpath, envLoader, false); |
| } |
| /** |
| * Load the persistent classes named in configuration. This ensures that all subclasses and |
| * application identity classes of each type are known in advance, without having to rely on the |
| * application loading the classes before performing operations that might involve them. |
| * |
| * @param devpath |
| * if true, search for metadata files in directories in the classpath if the no |
| * classes are configured explicitly |
| * @param envLoader |
| * the class loader to use, or null for default |
| * @param mustExist |
| * if true then empty list of classes or any unloadable but specified class will |
| * raise an exception. |
| * @return the loaded classes, or empty collection if none |
| */ |
| public Collection<Class<?>> loadPersistentTypes(boolean devpath, ClassLoader envLoader, boolean mustExist) { |
| if (_locking) { |
| synchronized (this) { |
| return loadPersistentTypesInternal(devpath, envLoader, mustExist); |
| } |
| } else { |
| return loadPersistentTypesInternal(devpath, envLoader, mustExist); |
| } |
| } |
| |
| private Collection<Class<?>> loadPersistentTypesInternal(boolean devpath, ClassLoader envLoader, |
| boolean mustExist) { |
| Set<String> names = getPersistentTypeNames(devpath, envLoader); |
| if (names == null || names.isEmpty()) { |
| if (!mustExist) |
| return Collections.emptyList(); |
| else |
| throw new MetaDataException(_loc.get("eager-no-class-found")); |
| } |
| |
| // attempt to load classes so that they get processed |
| ClassLoader clsLoader = _conf.getClassResolverInstance().getClassLoader(getClass(), envLoader); |
| List<Class<?>> classes = new ArrayList<>(names.size()); |
| Class<?> cls; |
| for (String className : names) { |
| cls = classForName(className, clsLoader); |
| if (_factory.isMetaClass(cls)) { |
| setMetaModel(cls); |
| continue; |
| } |
| if (skipMetadata(cls)) { |
| continue; |
| } |
| if (cls != null) { |
| classes.add(cls); |
| |
| // if the class is an interface, load its metadata to kick |
| // off the impl generator |
| if (cls.isInterface()) |
| getMetaData(cls, clsLoader, false); |
| } else if (cls == null && mustExist) { |
| throw new MetaDataException(_loc.get("eager-class-not-found", className)); |
| } |
| } |
| return classes; |
| } |
| |
| /** |
| * Return the class for the given name, or null if not loadable. |
| */ |
| private Class<?> classForName(String name, ClassLoader loader) { |
| try { |
| return Class.forName(name, true, loader); |
| } catch (Exception e) { |
| if ((_validate & VALIDATE_RUNTIME) != 0) { |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("bad-discover-class", name, loader)); |
| } else if (_log.isInfoEnabled()) |
| _log.info(_loc.get("bad-discover-class", name, loader)); |
| if (_log.isTraceEnabled()) |
| _log.trace(e); |
| } catch (NoSuchMethodError nsme) { |
| if (nsme.getMessage().indexOf(".pc") == -1) |
| throw nsme; |
| |
| // if the error is about a method that uses the PersistenceCapable |
| // 'pc' method prefix, perform some logging and continue. This |
| // probably just means that the class is not yet enhanced. |
| if ((_validate & VALIDATE_RUNTIME) != 0) { |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("bad-discover-class", name, loader)); |
| } else if (_log.isInfoEnabled()) |
| _log.info(_loc.get("bad-discover-class", name, loader)); |
| if (_log.isTraceEnabled()) |
| _log.trace(nsme); |
| } |
| return null; |
| } |
| |
| /** |
| * Return all known subclasses for the given class mapping. Note that this method only works |
| * during runtime when the repository is registered as a {@link RegisterClassListener}. |
| */ |
| Collection<Class<?>> getPCSubclasses(Class<?> cls) { |
| Collection<Class<?>> subs = _subs.get(cls); |
| if (subs == null) |
| return Collections.emptyList(); |
| return subs; |
| } |
| |
| // ////////////////////////////////////// |
| // RegisterClassListener implementation |
| // ////////////////////////////////////// |
| |
| @Override |
| public void register(Class<?> cls) { |
| // buffer registered classes until an oid metadata request is made, |
| // at which point we'll parse everything in the buffer |
| synchronized (_registered) { |
| _registered.add(cls); |
| registerAlias(cls); |
| } |
| } |
| |
| /** |
| * Parses the metadata for all registered classes. |
| */ |
| private void loadRegisteredClassMetaData(ClassLoader envLoader) { |
| Class<?>[] reg = processRegisteredClasses(envLoader); |
| for (Class<?> aClass : reg) { |
| try { |
| getMetaData(aClass, envLoader, false); |
| } |
| catch (MetaDataException me) { |
| if (_log.isWarnEnabled()) |
| _log.warn(me); |
| } |
| } |
| } |
| |
| /** |
| * Updates our data structures with the latest registered classes. |
| * |
| * This method is synchronized to make sure that all data structures are fully updated |
| * before other threads attempt to call this method |
| */ |
| synchronized Class<?>[] processRegisteredClasses(ClassLoader envLoader) { |
| |
| Class<?>[] reg; |
| /*Synchronize `_registered` cache to block MetaDataRepository.register() from adding |
| * to the cache while we copy, causing a ConcurrentModificationException |
| */ |
| synchronized (_registered) { |
| if (_registered.isEmpty()) { |
| return EMPTY_CLASSES; |
| } |
| |
| // copy into new collection to avoid concurrent mod errors on reentrant |
| // registrations |
| reg = _registered.toArray(new Class[_registered.size()]); |
| _registered.clear(); |
| } |
| |
| Collection<String> pcNames = getPersistentTypeNames(false, envLoader); |
| Collection<Class<?>> failed = null; |
| for (Class<?> aClass : reg) { |
| // Don't process types that aren't listed by the user; it may belong to a different persistence unit. |
| if (pcNames != null && !pcNames.isEmpty() && !pcNames.contains(aClass.getName())) { |
| continue; |
| } |
| |
| // If the compatibility option "filterPCRegistryClasses" is enabled, then verify that the type is |
| // accessible to the envLoader/Thread Context ClassLoader |
| if (_filterRegisteredClasses) { |
| Log log = (_conf == null) ? null : _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME); |
| ClassLoader loadCL = (envLoader != null) ? |
| envLoader : |
| AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction()); |
| |
| try { |
| Class<?> classFromAppClassLoader = Class.forName(aClass.getName(), true, loadCL); |
| |
| if (!aClass.equals(classFromAppClassLoader)) { |
| // This is a class that belongs to a ClassLoader not associated with the Application, |
| // so it should be processed. |
| if (log != null && log.isTraceEnabled()) { |
| log.trace( |
| "Metadata Repository will ignore Class " + aClass.getName() + |
| ", since it originated from a ClassLoader not associated with the application."); |
| } |
| continue; |
| } |
| } |
| catch (ClassNotFoundException cnfe) { |
| // Catch exception and log its occurrence, and permit MDR processing to continue to preserve |
| // original behavior. |
| if (log != null && log.isTraceEnabled()) { |
| log.trace("The Class " + aClass.getName() + " was identified as a persistent class " + |
| "by configuration, but the Class could not be found."); |
| } |
| } |
| } |
| |
| checkEnhancementLevel(aClass); |
| try { |
| processRegisteredClass(aClass); |
| } |
| catch (Throwable t) { |
| if (!_conf.getRetryClassRegistration()) |
| throw new MetaDataException(_loc.get("error-registered", aClass), t); |
| |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("failed-registered", aClass), t); |
| if (failed == null) |
| failed = new ArrayList<>(); |
| failed.add(aClass); |
| } |
| } |
| if (failed != null) { |
| if (_locking) { |
| synchronized (_registered) { |
| _registered.addAll(failed); |
| } |
| } else { |
| _registered.addAll(failed); |
| } |
| } |
| return reg; |
| } |
| |
| /** |
| * Updates our datastructures with the given registered class. Relies on the fact that a child |
| * class cannot register itself without also registering its parent class by specifying its |
| * persistence capable superclass in the registration event. |
| */ |
| private void processRegisteredClass(Class<?> cls) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("process-registered", cls)); |
| |
| // update subclass lists; synchronize on this because accessing _metas |
| // requires it |
| Class<?> leastDerived = cls; |
| synchronized (this) { |
| ClassMetaData meta; |
| for (Class<?> anc = cls; (anc = PCRegistry.getPersistentSuperclass(anc)) != null;) { |
| addToCollection(_subs, anc, cls, true); |
| meta = _metas.get(anc); |
| if (meta != null) |
| meta.clearSubclassCache(); |
| leastDerived = anc; |
| } |
| } |
| |
| // update oid mappings if this is a base concrete class |
| Object oid = null; |
| try { |
| oid = PCRegistry.newObjectId(cls); |
| } catch (InternalException ie) { |
| // thrown for single field identity with null pk field value |
| } |
| if (oid != null) { |
| Class<?> existing = _oids.get(oid.getClass()); |
| if (existing != null) { |
| // if there is already a class for this OID, then we know |
| // that multiple classes are using the same OID: therefore, |
| // put the least derived PC superclass into the map. This |
| // gets around the problem of an abstract PC superclass |
| // using application identity (since newObjectId |
| // will return null for abstract classes). |
| Class<?> sup = cls; |
| while (PCRegistry.getPersistentSuperclass(sup) != null) |
| sup = PCRegistry.getPersistentSuperclass(sup); |
| |
| _oids.put(oid.getClass(), sup); |
| } else if (existing == null || cls.isAssignableFrom(existing)) |
| _oids.put(oid.getClass(), cls); |
| } |
| |
| // update mappings from interfaces and non-pc superclasses to |
| // pc implementing types |
| if (_locking) { |
| synchronized (_impls) { |
| updateImpls(cls, leastDerived, cls); |
| } |
| } else { |
| updateImpls(cls, leastDerived, cls); |
| } |
| |
| // set alias for class |
| registerAlias(cls); |
| } |
| |
| |
| /** |
| * Register the given class to the list of known aliases. |
| * The alias is registered only if the class has been enhanced. |
| * |
| */ |
| void registerAlias(Class<?> cls) { |
| registerAlias(PCRegistry.getTypeAlias(cls), cls); |
| } |
| |
| public void registerAlias(String alias, Class<?> cls) { |
| if (alias == null) |
| return; |
| try { |
| if (alias != null) { |
| List<Class<?>> classes = _aliases.get(alias); |
| if (classes == null) |
| classes = new ArrayList<>(3); |
| if (!classes.contains(cls)) { |
| classes.add(cls); |
| _aliases.put(alias, classes); |
| } |
| } |
| } catch (IllegalStateException ise) { |
| // the class has not been registered to PCRegistry |
| } |
| } |
| |
| /** |
| * Update the list of implementations of base classes and interfaces. |
| */ |
| private void updateImpls(Class<?> cls, Class<?> leastDerived, Class<?> check) { |
| // allow users to query on common non-pc superclasses |
| Class<?> sup = check.getSuperclass(); |
| if (leastDerived == cls && sup != null && sup != Object.class) { |
| addToCollection(_impls, sup, cls, false); |
| updateImpls(cls, leastDerived, sup); |
| } |
| |
| // allow users to query on any implemented interfaces unless defaults |
| // say the user must implement persistent interfaces explicitly in meta |
| if (!_factory.getDefaults().isDeclaredInterfacePersistent()) |
| return; |
| Class<?>[] ints = check.getInterfaces(); |
| for (Class<?> anInt : ints) { |
| // don't map java-standard interfaces |
| if (anInt.getName().startsWith("java.")) |
| continue; |
| |
| // only map least-derived interface implementors |
| if (leastDerived == cls || isLeastDerivedImpl(anInt, cls)) { |
| addToCollection(_impls, anInt, cls, false); |
| updateImpls(cls, leastDerived, anInt); |
| } |
| } |
| } |
| |
| /** |
| * Return true if the given class is the least-derived persistent implementor of the given |
| * interface, false otherwise. |
| */ |
| private boolean isLeastDerivedImpl(Class<?> inter, Class<?> cls) { |
| Class<?> parent = PCRegistry.getPersistentSuperclass(cls); |
| while (parent != null) { |
| if (Arrays.asList(parent.getInterfaces()).contains(inter)) |
| return false; |
| parent = PCRegistry.getPersistentSuperclass(parent); |
| } |
| return true; |
| } |
| |
| /** |
| * Add the given value to the collection cached in the given map under the given key. |
| */ |
| private void addToCollection(Map<Class<?>, Collection<Class<?>>> map, |
| Class<?> key, Class<?> value, boolean inheritance) { |
| if (_locking) { |
| synchronized (map) { |
| addToCollectionInternal(map, key, value, inheritance); |
| } |
| } else { |
| addToCollectionInternal(map, key, value, inheritance); |
| } |
| } |
| |
| private void addToCollectionInternal(Map<Class<?>, Collection<Class<?>>> map, |
| Class<?> key, Class<?> value, boolean inheritance) { |
| Collection<Class<?>> coll = map.get(key); |
| if (coll == null) { |
| if (inheritance) { |
| InheritanceComparator comp = new InheritanceComparator(); |
| comp.setBase(key); |
| coll = new TreeSet<Class<?>>(comp); |
| } else |
| coll = new LinkedList<>(); |
| map.put(key, coll); |
| } |
| coll.add(value); |
| } |
| |
| /** |
| * Puts the meta class corresponding to the given entity class. |
| */ |
| public void setMetaModel(Class<?> m2) { |
| Class<?> cls = _factory.getManagedClass(m2); |
| if (cls != null) |
| _metamodel.put(cls, m2); |
| } |
| |
| /** |
| * Puts the meta class corresponding to the given persistent class. |
| */ |
| public void setMetaModel(ClassMetaData meta, Class<?> m2) { |
| _metamodel.put(meta.getDescribedType(), m2); |
| } |
| |
| /** |
| * Gets the meta class corresponding to the given persistent class. |
| */ |
| public Class<?> getMetaModel(ClassMetaData meta, boolean load) { |
| return getMetaModel(meta.getDescribedType(), load); |
| } |
| |
| /** |
| * Gets the meta class corresponding to the given class. If load is false, returns the meta |
| * class if has been set for the given persistent class earlier. If the load is true then also |
| * attempts to apply the current naming policy to derive meta class name and attempts to load |
| * the meta class. |
| */ |
| public Class<?> getMetaModel(Class<?> entity, boolean load) { |
| if (_metamodel.containsKey(entity)) |
| return _metamodel.get(entity); |
| String m2 = _factory.getMetaModelClassName(entity.getName()); |
| try { |
| ClassLoader loader = AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(entity)); |
| Class<?> m2cls = AccessController.doPrivileged(J2DoPrivHelper.getForNameAction(m2, true, loader)); |
| _metamodel.put(entity, m2cls); |
| return m2cls; |
| } catch (Throwable t) { |
| if (_log.isTraceEnabled()) |
| _log.warn(_loc.get("meta-no-model", m2, entity, t)); |
| } |
| return null; |
| } |
| |
| // ///////////////////////////// |
| // Configurable implementation |
| // ///////////////////////////// |
| |
| @Override |
| public void setConfiguration(Configuration conf) { |
| _conf = (OpenJPAConfiguration) conf; |
| _log = _conf.getLog(OpenJPAConfiguration.LOG_METADATA); |
| _filterRegisteredClasses = _conf.getCompatibilityInstance().getFilterPCRegistryClasses(); |
| _typesWithoutEnhancement = _conf.getTypesWithoutEnhancement(); |
| if (_typesWithoutEnhancement == null || _typesWithoutEnhancement.isEmpty()) { |
| _typesWithoutEnhancement = null; |
| } |
| } |
| |
| @Override |
| public void startConfiguration() { |
| } |
| |
| @Override |
| public void endConfiguration() { |
| initializeMetaDataFactory(); |
| if (_implGen == null) |
| _implGen = new InterfaceImplGenerator(this); |
| if (_preload) { |
| _oids = new HashMap<>(); |
| _impls = new HashMap<>(); |
| _ifaces = new HashMap<>(); |
| _aliases = new HashMap<>(); |
| _pawares = new HashMap<>(); |
| _nonMapped = new HashMap<>(); |
| _subs = new HashMap<>(); |
| // Wait till we're done loading MetaData to flip _lock boolean. |
| } |
| } |
| |
| private void initializeMetaDataFactory() { |
| if (_factory == null) { |
| MetaDataFactory mdf = _conf.newMetaDataFactoryInstance(); |
| if (mdf == null) |
| throw new MetaDataException(_loc.get("no-metadatafactory")); |
| setMetaDataFactory(mdf); |
| } |
| } |
| |
| // //////////////// |
| // Query metadata |
| // //////////////// |
| |
| /** |
| * Return query metadata for the given class, name, and classloader. |
| */ |
| public QueryMetaData getQueryMetaData(Class<?> cls, String name, ClassLoader envLoader, boolean mustExist) { |
| if (_locking) { |
| synchronized (this) { |
| return getQueryMetaDataInternal(cls, name, envLoader, mustExist); |
| } |
| } else { |
| return getQueryMetaDataInternal(cls, name, envLoader, mustExist); |
| } |
| } |
| |
| private QueryMetaData getQueryMetaDataInternal(Class<?> cls, String name, ClassLoader envLoader, |
| boolean mustExist) { |
| QueryMetaData meta = getQueryMetaDataInternal(cls, name, envLoader); |
| if (meta == null) { |
| // load all the metadatas for all the known classes so that |
| // query names are seen and registered |
| resolveAll(envLoader); |
| meta = getQueryMetaDataInternal(cls, name, envLoader); |
| } |
| |
| if (meta == null && mustExist) { |
| if (cls == null) { |
| throw new MetaDataException(_loc.get("no-named-query-null-class", getPersistentTypeNames(false, |
| envLoader), name)); |
| } else { |
| throw new MetaDataException(_loc.get("no-named-query", cls, name)); |
| } |
| } |
| |
| return meta; |
| } |
| |
| /** |
| * Resolve all known metadata classes. |
| */ |
| private void resolveAll(ClassLoader envLoader) { |
| Collection<Class<?>> types = loadPersistentTypes(false, envLoader); |
| for (Class<?> c : types) { |
| getMetaData(c, envLoader, false); |
| } |
| } |
| |
| /** |
| * Return query metadata for the given class, name, and classloader. |
| */ |
| private QueryMetaData getQueryMetaDataInternal(Class<?> cls, String name, ClassLoader envLoader) { |
| if (name == null) |
| return null; |
| |
| // check cache |
| QueryMetaData qm = _queries.get(name); |
| if (qm != null) |
| return qm; |
| |
| // see if factory can figure out a scope for this query |
| if (cls == null) |
| cls = _factory.getQueryScope(name, envLoader); |
| |
| // get metadata for class, which will find queries in metadata file |
| if (cls != null && getMetaData(cls, envLoader, false) != null) { |
| qm = _queries.get(name); |
| if (qm != null) |
| return qm; |
| } |
| if ((_sourceMode & MODE_QUERY) == 0) |
| return null; |
| |
| // not in cache; load |
| _factory.load(cls, MODE_QUERY , envLoader); |
| return _queries.get(name); |
| } |
| |
| /** |
| * Return the cached query metadata. |
| */ |
| public QueryMetaData[] getQueryMetaDatas() { |
| if (_locking) { |
| synchronized (this) { |
| return _queries.values().toArray(new QueryMetaData[_queries.size()]); |
| } |
| } else { |
| return _queries.values().toArray(new QueryMetaData[_queries.size()]); |
| } |
| } |
| |
| |
| public QueryMetaData getCachedQueryMetaData(Class<?> cls, String name) { |
| return getCachedQueryMetaData(name); |
| } |
| |
| /** |
| * Return the cached query metadata for the given name. |
| */ |
| public QueryMetaData getCachedQueryMetaData(String name) { |
| if (_locking) { |
| synchronized (this) { |
| return _queries.get(name); |
| } |
| } else { |
| return _queries.get(name); |
| } |
| } |
| |
| /** |
| * Add a new query metadata to the repository and return it. |
| */ |
| public QueryMetaData addQueryMetaData(Class<?> cls, String name) { |
| if (_locking) { |
| synchronized (this) { |
| QueryMetaData meta = newQueryMetaData(cls, name); |
| _queries.put(name, meta); |
| return meta; |
| } |
| }else{ |
| QueryMetaData meta = newQueryMetaData(cls, name); |
| _queries.put(name, meta); |
| return meta; |
| } |
| } |
| |
| public QueryMetaData addQueryMetaData(QueryMetaData meta) { |
| if (_locking) { |
| synchronized (this) { |
| final QueryMetaData queryMetaData = _queries.get(meta.getName()); |
| return queryMetaData != null ? queryMetaData : _queries.put(meta.getName(), meta); |
| } |
| } else { |
| final QueryMetaData queryMetaData = _queries.get(meta.getName()); |
| return queryMetaData != null ? queryMetaData : _queries.put(meta.getName(), meta); |
| } |
| } |
| |
| /** |
| * Create a new query metadata instance. |
| */ |
| public QueryMetaData newQueryMetaData(Class<?> cls, String name) { |
| QueryMetaData meta = |
| new QueryMetaData(name, _conf.getCompatibilityInstance().getConvertPositionalParametersToNamed()); |
| meta.setDefiningType(cls); |
| return meta; |
| } |
| |
| /** |
| * Remove the given query metadata from the repository. |
| */ |
| public boolean removeQueryMetaData(QueryMetaData meta) { |
| if (meta == null) |
| return false; |
| if (_locking) { |
| synchronized (this) { |
| return _queries.remove(meta.getName()) != null; |
| } |
| } else { |
| return _queries.remove(meta.getName()) != null; |
| } |
| } |
| |
| /** |
| * Remove query metadata for the given class name if in the repository. |
| */ |
| public boolean removeQueryMetaData(Class<?> cls, String name) { |
| if (_locking) { |
| synchronized (this) { |
| if (name == null) |
| return false; |
| return _queries.remove(name) != null; |
| } |
| } else { |
| if (name == null) |
| return false; |
| return _queries.remove(name) != null; |
| } |
| } |
| |
| /** |
| * Searches all cached query metadata by name. |
| */ |
| public QueryMetaData searchQueryMetaDataByName(String name) { |
| return _queries.get(name); |
| } |
| |
| /** |
| * Return a unique key for a given class / name. The class argument can be null. |
| */ |
| protected static Object getQueryKey(Class<?> cls, String name) { |
| if (cls == null) |
| return name; |
| QueryKey key = new QueryKey(); |
| key.clsName = cls.getName(); |
| key.name = name; |
| return key; |
| } |
| |
| // /////////////////// |
| // Sequence metadata |
| // /////////////////// |
| |
| /** |
| * Return sequence metadata for the given name and classloader. |
| */ |
| public SequenceMetaData getSequenceMetaData(String name, ClassLoader envLoader, boolean mustExist) { |
| if (_locking) { |
| synchronized (this) { |
| return getSequenceMetaDataInternal(name, envLoader, mustExist); |
| } |
| } else { |
| return getSequenceMetaDataInternal(name, envLoader, mustExist); |
| } |
| } |
| |
| private SequenceMetaData getSequenceMetaDataInternal(String name, ClassLoader envLoader, boolean mustExist) { |
| SequenceMetaData meta = getSequenceMetaDataInternal(name, envLoader); |
| if (meta == null && SequenceMetaData.NAME_SYSTEM.equals(name)) { |
| if (_sysSeq == null) |
| _sysSeq = newSequenceMetaData(name); |
| return _sysSeq; |
| } |
| if (meta == null && mustExist) |
| throw new MetaDataException(_loc.get("no-named-sequence", name)); |
| return meta; |
| } |
| |
| /** |
| * Used internally by metadata to retrieve sequence metadatas based on possibly-unqualified |
| * sequence name. |
| */ |
| SequenceMetaData getSequenceMetaData(ClassMetaData context, String name, boolean mustExist) { |
| // try with given name |
| MetaDataException e = null; |
| try { |
| SequenceMetaData seq = getSequenceMetaData(name, context.getEnvClassLoader(), mustExist); |
| if (seq != null) |
| return seq; |
| } catch (MetaDataException mde) { |
| e = mde; |
| } |
| |
| // if given name already fully qualified, give up |
| if (name.indexOf('.') != -1) { |
| if (e != null) |
| throw e; |
| return null; |
| } |
| |
| // try with qualified name |
| name = ClassUtil.getPackageName(context.getDescribedType()) + "." + name; |
| try { |
| return getSequenceMetaData(name, context.getEnvClassLoader(), mustExist); |
| } catch (MetaDataException mde) { |
| // throw original exception |
| if (e != null) |
| throw e; |
| throw mde; |
| } |
| } |
| |
| /** |
| * Return sequence metadata for the given name and classloader. |
| */ |
| private SequenceMetaData getSequenceMetaDataInternal(String name, ClassLoader envLoader) { |
| if (name == null) |
| return null; |
| |
| // check cache |
| SequenceMetaData meta = _seqs.get(name); |
| if (meta == null) { |
| // load metadata for registered classes to hopefully find sequence |
| // definition |
| loadRegisteredClassMetaData(envLoader); |
| meta = _seqs.get(name); |
| } |
| return meta; |
| } |
| |
| /** |
| * Return the cached sequence metadata. |
| */ |
| public SequenceMetaData[] getSequenceMetaDatas() { |
| if (_locking) { |
| synchronized (this) { |
| return _seqs.values().toArray(new SequenceMetaData[_seqs.size()]); |
| } |
| } else { |
| return _seqs.values().toArray(new SequenceMetaData[_seqs.size()]); |
| } |
| } |
| |
| /** |
| * Return the cached a sequence metadata for the given name. |
| */ |
| public SequenceMetaData getCachedSequenceMetaData(String name) { |
| if (_locking) { |
| synchronized (this) { |
| return _seqs.get(name); |
| } |
| } else { |
| return _seqs.get(name); |
| } |
| } |
| |
| /** |
| * Add a new sequence metadata to the repository and return it. |
| */ |
| public SequenceMetaData addSequenceMetaData(String name) { |
| if (_locking) { |
| synchronized (this) { |
| SequenceMetaData meta = newSequenceMetaData(name); |
| _seqs.put(name, meta); |
| return meta; |
| } |
| } else { |
| SequenceMetaData meta = newSequenceMetaData(name); |
| _seqs.put(name, meta); |
| return meta; |
| } |
| } |
| |
| /** |
| * Create a new sequence metadata instance. |
| */ |
| protected SequenceMetaData newSequenceMetaData(String name) { |
| return new SequenceMetaData(name, this); |
| } |
| |
| /** |
| * Remove the given sequence metadata from the repository. |
| */ |
| public boolean removeSequenceMetaData(SequenceMetaData meta) { |
| if (meta == null) |
| return false; |
| if (_locking) { |
| synchronized (this) { |
| return _seqs.remove(meta.getName()) != null; |
| } |
| } else { |
| return _seqs.remove(meta.getName()) != null; |
| } |
| } |
| |
| /** |
| * Remove sequence metadata for the name if in the repository. |
| */ |
| public boolean removeSequenceMetaData(String name) { |
| if (name == null) |
| return false; |
| if (_locking) { |
| synchronized (this) { |
| return _seqs.remove(name) != null; |
| } |
| } else { |
| return _seqs.remove(name) != null; |
| } |
| } |
| |
| /** |
| * Whether any system (default) listeners have been registered. Used as a quick test to |
| * determine whether the callback/listener mechanism has been enabled. |
| * @return boolean |
| */ |
| public boolean is_systemListenersActivated() { |
| return _systemListenersActivated; |
| } |
| |
| /** |
| * Add the given system lifecycle listener. |
| */ |
| public void addSystemListener(Object listener) { |
| if (_locking) { |
| synchronized (this) { |
| // copy to avoid issues with ListenerList and avoid unncessary |
| // locking on the list during runtime |
| LifecycleEventManager.ListenerList listeners = new LifecycleEventManager.ListenerList(_listeners); |
| listeners.add(listener); |
| _listeners = listeners; |
| _systemListenersActivated = true; |
| } |
| } else { |
| LifecycleEventManager.ListenerList listeners = new LifecycleEventManager.ListenerList(_listeners); |
| listeners.add(listener); |
| _listeners = listeners; |
| _systemListenersActivated = true; |
| } |
| } |
| |
| /** |
| * Remove the given system lifecycle listener. |
| */ |
| public boolean removeSystemListener(Object listener) { |
| if (_locking) { |
| synchronized (this) { |
| return removeSystemListenerInternal(listener); |
| } |
| } else { |
| return removeSystemListenerInternal(listener); |
| } |
| } |
| |
| private boolean removeSystemListenerInternal(Object listener) { |
| if (!_listeners.contains(listener)) |
| return false; |
| |
| // copy to avoid issues with ListenerList and avoid unncessary |
| // locking on the list during runtime |
| LifecycleEventManager.ListenerList listeners = new LifecycleEventManager.ListenerList(_listeners); |
| listeners.remove(listener); |
| _listeners = listeners; |
| return true; |
| } |
| |
| /** |
| * Return the system lifecycle listeners |
| */ |
| public LifecycleEventManager.ListenerList getSystemListeners() { |
| return _listeners; |
| } |
| |
| /** |
| * Free the resources used by this repository. Closes all user sequences. |
| */ |
| @Override |
| public void close() { |
| if (_locking) { |
| synchronized (this) { |
| closeInternal(); |
| } |
| } else { |
| closeInternal(); |
| } |
| } |
| |
| private void closeInternal() { |
| SequenceMetaData[] smds = getSequenceMetaDatas(); |
| for (SequenceMetaData smd : smds) { |
| smd.close(); |
| } |
| clear(); |
| } |
| |
| /** |
| * Query key struct. |
| */ |
| private static class QueryKey implements Serializable { |
| |
| |
| private static final long serialVersionUID = 1L; |
| public String clsName; |
| public String name; |
| |
| @Override |
| public int hashCode() { |
| int clsHash = (clsName == null) ? 0 : clsName.hashCode(); |
| int nameHash = (name == null) ? 0 : name.hashCode(); |
| return clsHash + nameHash; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == this) |
| return true; |
| if (!(obj instanceof QueryKey)) |
| return false; |
| |
| QueryKey qk = (QueryKey) obj; |
| return Objects.equals(clsName, qk.clsName) && Objects.equals(name, qk.name); |
| } |
| } |
| |
| /** |
| * Return XML metadata for a given field metadata |
| * |
| * @return XML metadata |
| */ |
| public XMLMetaData getXMLMetaData(Class<?> cls) { |
| if (_locking) { |
| synchronized (this) { |
| return getXMLMetaDataInternal(cls); |
| } |
| } else { |
| return getXMLMetaDataInternal(cls); |
| } |
| } |
| |
| private XMLMetaData getXMLMetaDataInternal(Class<?> cls) { |
| if (cls == null) { |
| return null; |
| } |
| // check if cached before |
| XMLMetaData xmlmeta = _xmlmetas.get(cls); |
| if (xmlmeta != null) |
| return xmlmeta; |
| |
| // load JAXB XML metadata |
| _factory.loadXMLMetaData(cls); |
| |
| xmlmeta = _xmlmetas.get(cls); |
| |
| return xmlmeta; |
| } |
| |
| /** |
| * Create a new metadata, populate it with default information, add it to the repository, and |
| * return it. |
| * |
| * @param type the access type to use in populating metadata |
| */ |
| public XMLClassMetaData addXMLClassMetaData(Class<?> type) { |
| XMLClassMetaData meta = newXMLClassMetaData(type); |
| if(_locking){ |
| synchronized(this){ |
| _xmlmetas.put(type, meta); |
| } |
| }else{ |
| _xmlmetas.put(type, meta); |
| } |
| return meta; |
| } |
| |
| /** |
| * Return the cached XMLClassMetaData for the given class Return null if none. |
| */ |
| public XMLMetaData getCachedXMLMetaData(Class<?> cls) { |
| return _xmlmetas.get(cls); |
| } |
| |
| /** |
| * Create a new xml class metadata |
| * |
| * @return a XMLClassMetaData |
| */ |
| protected XMLClassMetaData newXMLClassMetaData(Class<?> type) { |
| return new XMLClassMetaData(type); |
| } |
| |
| /** |
| * Create a new xml field meta, add it to the fieldMap in the given xml class metadata |
| * |
| * @return a XMLFieldMetaData |
| */ |
| public XMLFieldMetaData newXMLFieldMetaData(Class<?> type, String name) { |
| return new XMLFieldMetaData(type, name); |
| } |
| |
| public static boolean needsPreload(OpenJPAConfiguration conf) { |
| if (conf == null) |
| return false; |
| Options o = Configurations.parseProperties(Configurations.getProperties(conf.getMetaDataRepository())); |
| if (o.getBooleanProperty(PRELOAD_STR) || o.getBooleanProperty(PRELOAD_STR.toLowerCase())) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * This private worker ensures that a message is logged when an Entity is enhanced by a version of the enhancer that |
| * is older than the current version. |
| */ |
| private void checkEnhancementLevel(Class<?> cls) { |
| if (!_logEnhancementLevel) { |
| return; |
| } |
| Log log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME); |
| boolean res = PCEnhancer.checkEnhancementLevel(cls, _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME)); |
| if (!log.isTraceEnabled() && res) { |
| // Since trace isn't enabled flip the flag so we only log this once. |
| _logEnhancementLevel = false; |
| log.info(_loc.get("down-level-entity")); |
| } |
| } |
| |
| /** |
| * This method returns the ClassMetaData whose described type name matches the typeName parameter. It ONLY operates |
| * against MetaData that is currently known by this repository. Note: This method call WILL NOT resolve any |
| * metadata. |
| */ |
| public ClassMetaData getCachedMetaData(String typeName) { |
| ClassMetaData cmd = _metaStringMap.get(typeName); |
| if (cmd == null) { |
| for (ClassMetaData c : getMetaDatas()) { |
| if (c.getDescribedType().getName().equals(typeName)) { |
| _metaStringMap.put(typeName, c); |
| return c; |
| } |
| } |
| } |
| return cmd; |
| } |
| } |