diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java index 38241ded8..29edc6b6d 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingTool.java @@ -660,7 +660,8 @@ public class MappingTool ClassMapping mapping = repos.getMapping(cls, null, false); if (mapping != null) return mapping; - if (!validate || repos.getPersistenceAware(cls) != null) + if (!validate || cls.isInterface() || + repos.getPersistenceAware(cls) != null) return null; throw new MetaDataException(_loc.get("no-meta", cls)); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExtentImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExtentImpl.java index 4cff23f8f..cb3a22962 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExtentImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExtentImpl.java @@ -120,10 +120,11 @@ public class ExtentImpl _broker.getClassLoader(), false); ClassMetaData[] metas; - if (meta != null && (meta.isMapped() || (_subs + if (meta != null && (!_subs || !meta.isManagedInterface()) + && (meta.isMapped() || (_subs && meta.getMappedPCSubclassMetaDatas().length > 0))) metas = new ClassMetaData[]{ meta }; - else if (meta == null && _subs) + else if (_subs && (meta == null || meta.isManagedInterface())) metas = repos.getImplementorMetaDatas(_type, _broker.getClassLoader(), false); else diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java index da0dbd738..e5d2affab 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java @@ -657,11 +657,11 @@ public class QueryImpl ClassMetaData[] metas; if (_class == null || _storeQuery.supportsAbstractExecutors()) metas = new ClassMetaData[]{ meta }; - else if (meta != null && (_subclasses || meta.isMapped())) - metas = new ClassMetaData[]{ meta }; - else if (_subclasses) + else if (_subclasses && (meta == null || meta.isManagedInterface())) metas = repos.getImplementorMetaDatas(_class, _broker.getClassLoader(), true); + else if (meta != null && (_subclasses || meta.isMapped())) + metas = new ClassMetaData[]{ meta }; else metas = StoreQuery.EMPTY_METAS; diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java index 81d203c6d..86a4a348d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/ClassMetaData.java @@ -142,6 +142,7 @@ public class ClassMetaData private Boolean _embedded = null; private Boolean _interface = null; private Class _impl = null; + private Map _ifaceMap = new HashMap(); private int _identity = ID_UNKNOWN; private int _idStrategy = ValueStrategies.NONE; private int _accessType = ACCESS_UNKNOWN; @@ -694,6 +695,37 @@ public class ClassMetaData public void setInterfaceImpl(Class impl) { _impl = impl; } + + /** + * Alias properties from the given interface during queries to + * the local field. + */ + public void setInterfacePropertyAlias(Class iface, String orig, + String local) { + synchronized (_ifaceMap) { + Map fields = (Map) _ifaceMap.get(iface); + if (fields == null) { + fields = new HashMap(); + _ifaceMap.put(iface, fields); + } + if (fields.containsKey(orig)) + throw new MetaDataException(_loc.get("duplicate-iface-alias", + this, orig, local)); + fields.put(orig, local); + } + } + + /** + * Get local field alias for the given interface property. + */ + public String getInterfacePropertyAlias(Class iface, String orig) { + synchronized (_ifaceMap) { + Map fields = (Map) _ifaceMap.get(iface); + if (fields == null) + return null; + return (String) fields.get(orig); + } + } /** * Return the number of fields that use impl or intermediate data, in @@ -2194,6 +2226,10 @@ public class ClassMetaData fg = addDeclaredFetchGroup(fgs[i].getName()); fg.copy(fgs[i]); } + + // copy iface re-mapping + _ifaceMap.clear(); + _ifaceMap.putAll(meta._ifaceMap); } /** diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java index 81bf0756b..5cecca405 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataRepository.java @@ -83,8 +83,8 @@ public class MetaDataRepository public static final int VALIDATE_RUNTIME = 8; protected static final Class[] EMPTY_CLASSES = new Class[0]; - protected static final PersistenceAwareClass[] EMPTY_PAWARE_CLASSES = - new PersistenceAwareClass[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; @@ -110,6 +110,7 @@ public class MetaDataRepository private final Map _seqs = new HashMap(); private final Map _aliases = Collections.synchronizedMap(new HashMap()); private final Map _pawares = Collections.synchronizedMap(new HashMap()); + private final Map _nonMapped = Collections.synchronizedMap(new HashMap()); // map of classes to lists of their subclasses private final Map _subs = Collections.synchronizedMap(new HashMap()); @@ -886,6 +887,7 @@ public class MetaDataRepository meta, impl)); _ifaces.put(meta.getDescribedType(), impl); _metas.put(impl, meta); + addDeclaredInterfaceImpl(meta, meta.getDescribedType()); } synchronized InterfaceImplGenerator getImplGenerator() { @@ -1034,56 +1036,112 @@ public class MetaDataRepository } /** - * Gets the persistence-aware class corresponding to the given class. Can - * be null, if the given class is not registered as persistence-aware with - * this receiver. + * Gets the metadata corresponding to the given persistence-aware class. + * Returns null, if the given class is not registered as + * persistence-aware. */ - public PersistenceAwareClass getPersistenceAware(Class cls) { - return (PersistenceAwareClass)_pawares.get(cls); + public NonPersistentMetaData getPersistenceAware(Class cls) { + return (NonPersistentMetaData)_pawares.get(cls); } /** - * Gets all the registered persistence-aware classes. + * Gets all the metadatas for persistence-aware classes * - * @return empty array if no class has been registered + * @return empty array if no class has been registered as pers-aware */ - public PersistenceAwareClass[] getPersistenceAwares() { + public NonPersistentMetaData[] getPersistenceAwares() { synchronized (_pawares) { if (_pawares.isEmpty()) - return EMPTY_PAWARE_CLASSES; - return (PersistenceAwareClass[])_pawares.values().toArray - (new PersistenceAwareClass[_pawares.size()]); + return EMPTY_NON_PERSISTENT; + return (NonPersistentMetaData[])_pawares.values().toArray + (new NonPersistentMetaData[_pawares.size()]); } } /** - * Add the given class as persitence-aware. + * Add the given class as persistence-aware. * * @param cls non-null and must not alreaddy be added as persitence-capable */ - public PersistenceAwareClass addPersistenceAware(Class cls) { + public NonPersistentMetaData addPersistenceAware(Class cls) { if (cls == null) return null; synchronized(this) { if (_pawares.containsKey(cls)) - return (PersistenceAwareClass)_pawares.get(cls); + return (NonPersistentMetaData)_pawares.get(cls); if (getCachedMetaData(cls) != null) throw new MetaDataException(_loc.get("pc-and-aware", cls)); - PersistenceAwareClass result = new PersistenceAwareClass(cls, this); - _pawares.put(cls, result); - return result; + NonPersistentMetaData meta = new NonPersistentMetaData(cls, this, + NonPersistentMetaData.TYPE_PERSISTENCE_AWARE); + _pawares.put(cls, meta); + return meta; } } /** - * Remove a persitence-aware class from this receiver. + * Remove a persitence-aware class from the repository * - * @return true if removed, false if not contained in this receiver + * @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 (NonPersistentMetaData)_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() { + synchronized (_nonMapped) { + if (_nonMapped.isEmpty()) + return EMPTY_NON_PERSISTENT; + return (NonPersistentMetaData[])_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; + synchronized(this) { + if (!iface.isInterface()) + throw new MetaDataException(_loc.get("not-non-mapped", iface)); + if (_nonMapped.containsKey(iface)) + return (NonPersistentMetaData)_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. @@ -1102,6 +1160,7 @@ public class MetaDataRepository _factory.clear(); _aliases.clear(); _pawares.clear(); + _nonMapped.clear(); } /** @@ -1317,7 +1376,7 @@ public class MetaDataRepository * Update the list of implementations of base classes and interfaces. */ private void updateImpls(Class cls, Class leastDerived, Class check) { - if (_factory.getDefaults().isDeclaredInterfacePersistent()) + if (!_factory.getDefaults().isDeclaredInterfacePersistent()) return; // allow users to query on common non-pc superclasses Class sup = check.getSuperclass(); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/PersistenceAwareClass.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/NonPersistentMetaData.java similarity index 69% rename from openjpa-kernel/src/main/java/org/apache/openjpa/meta/PersistenceAwareClass.java rename to openjpa-kernel/src/main/java/org/apache/openjpa/meta/NonPersistentMetaData.java index 977b625a9..1ca5e5c8b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/PersistenceAwareClass.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/NonPersistentMetaData.java @@ -10,20 +10,25 @@ import org.apache.openjpa.lib.xml.Commentable; * * @author Pinaki Poddar */ -public class PersistenceAwareClass +public class NonPersistentMetaData implements Comparable, SourceTracker, Commentable, MetaDataContext { + public static final int TYPE_PERSISTENCE_AWARE = 1; + public static final int TYPE_NON_MAPPED_INTERFACE = 2; private final MetaDataRepository _repos; private final Class _class; + private final int _type; private File _srcFile = null; private int _srcType = SRC_OTHER; private String[] _comments = null; private int _listIndex = -1; - protected PersistenceAwareClass(Class cls, MetaDataRepository repos) { + protected NonPersistentMetaData(Class cls, MetaDataRepository repos, + int type) { _repos = repos; _class = cls; + _type = type; } /** @@ -39,6 +44,13 @@ public class PersistenceAwareClass public Class getDescribedType() { return _class; } + + /** + * The type of metadata. + */ + public int getType() { + return _type; + } /** * The index in which this class was listed in the metadata. Defaults to @@ -85,13 +97,14 @@ public class PersistenceAwareClass _comments = comments; } - public int compareTo(Object other) { - if (other == this) + public int compareTo(Object o) { + if (o == this) return 0; - if (!(other instanceof PersistenceAwareClass)) + if (!(o instanceof NonPersistentMetaData)) return 1; - return _class.getName().compareTo(((PersistenceAwareClass) other). - getDescribedType().getName()); + NonPersistentMetaData other = (NonPersistentMetaData) o; + if (_type != other.getType()) + return _type - other.getType(); + return _class.getName().compareTo(other.getDescribedType().getName()); } - } diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties index eb6bc5c75..609481daa 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/meta/localizer.properties @@ -259,8 +259,13 @@ bad-fg-inclue: Attempt to include non-existent fetch group "{1}" in fetch \ group "{0}". unknown-fg: Attempt to add fetch group "{0}" to type field "{1}" failed. \ This fetch group has not been defined. +duplicate-iface-alias: Cannot alias interface "{0}" property "{1}" to local \ + field "{2}" as the property has already been aliased. pc-and-aware: Attempt to register "{0}" as a persistence-aware class failed. \ - The same class has been registered as persistence-capable already.======= + The same class has been registered as persistence-capable already. +not-non-mapped: Type "{0}" cannot be a non-mapped interface as the class is \ + not an interface +non-mapped-pc: A non-mapped interface cannot also be mapped. no-metadatafactory: No configuration properties were found. If you are \ using Ant, please see the or attributes \ of the task''s nested element.