diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java index 055f786f6..7c70e2391 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AbstractMetaDataDefaults.java @@ -179,7 +179,8 @@ public abstract class AbstractMetaDataDefaults FieldMetaData fmd; for (int i = 0; i < fieldNames.length; i ++) { String property = fieldNames[i]; - member = getMemberByProperty(meta, property); + member = getMemberByProperty(meta, property, + AccessCode.UNKNOWN, true); if (member == null) // transient or indeterminable access continue; fmd = meta.addDeclaredField(property, fieldTypes[i]); @@ -240,13 +241,6 @@ public abstract class AbstractMetaDataDefaults } - /** - * Called when populating from PCRegistry when only property names are - * available. - */ - protected abstract Member getMemberByProperty(ClassMetaData meta, - String property); - /** * Return the list of fields in meta that use field access, * or null if a list of fields is unobtainable. An empty list @@ -333,34 +327,10 @@ public abstract class AbstractMetaDataDefaults return null; if (fmd.getBackingMember() != null) return fmd.getBackingMember(); - return getMemberByProperty(fmd.getDeclaringMetaData(), fmd.getName()); -// try { -// //### note that we might not have access to declaring metadata yet -// //### (this could be used during parse), so we have to settle for -// //### defining. could cause problems if maps a superclass field -// //### where the superclass uses a different access type -// if (fmd.getBackingMember() == null) { -// if ((fmd.getDefiningMetaData().getAccessType() & -// ClassMetaData.ACCESS_FIELD) == ClassMetaData.ACCESS_FIELD) -// return AccessController.doPrivileged( -// J2DoPrivHelper.getDeclaredFieldAction( -// fmd.getDeclaringType(), fmd.getName())); -// return Reflection.findGetter(fmd.getDeclaringType(), -// fmd.getName(), true); -// } else { -// return fmd.getBackingMember(); -// } -// } catch (OpenJPAException ke) { -// throw ke; -// } catch (Exception e) { -// if (e instanceof PrivilegedActionException) -// e = ((PrivilegedActionException) e).getException(); -// throw new InternalException(e); -// } + return getMemberByProperty(fmd.getDeclaringMetaData(), fmd.getName(), + fmd.getAccessType(), true); } - - public Class getUnimplementedExceptionType() { return UnsupportedOperationException.class; } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AccessCode.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AccessCode.java index 2a1484298..2f1f8ceb0 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AccessCode.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/AccessCode.java @@ -18,6 +18,9 @@ */ package org.apache.openjpa.meta; +import org.apache.openjpa.lib.util.Localizer; +import org.apache.openjpa.util.UserException; + /** * Represents access styles for members of a class and field through a * 5-bit integer. @@ -74,6 +77,8 @@ public class AccessCode { public static int EXPLICIT = 2 << 2; public static int MIXED = 2 << 3; + private static Localizer _loc = Localizer.forPackage(AccessCode.class); + /** * Affirms if the given code is valid. */ @@ -167,6 +172,17 @@ public class AccessCode { return false; } + public static int mergeFieldCode(ClassMetaData meta, FieldMetaData fmd, + int fCode) { + int cCode = meta.getAccessType(); + try { + return mergeFieldCode(cCode, fCode); + } catch (IllegalStateException e) { + throw new UserException(_loc.get("access-illegal-merge", + fmd.getFullName(false), toString(fCode), toString(cCode))); + } + } + /** * Merges the field access type with the class access type provided such * merge is valid. @@ -192,8 +208,8 @@ public class AccessCode { if (fCode == cCode) return cCode; else - throw new IllegalStateException(toString(cCode) + - " not compatible to " + toString(fCode)); + throw new IllegalStateException("Can not merge field " + + toString(fCode) + " to class " + toString(cCode)); } } return cCode; 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 b05bf55f8..38ed3bb57 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 @@ -697,7 +697,7 @@ public class ClassMetaData * it may have the side-effect of changing the access code of this receiver. */ void mergeFieldAccess(FieldMetaData fmd, int fCode) { - setAccessType(AccessCode.mergeFieldCode(_accessType, fCode)); + setAccessType(AccessCode.mergeFieldCode(this, fmd, fCode)); } /** diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataDefaults.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataDefaults.java index a3aec0a42..60ae7c7e7 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataDefaults.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/MetaDataDefaults.java @@ -83,6 +83,22 @@ public interface MetaDataDefaults * Return the backing member for the given field metadata. */ public Member getBackingMember(FieldMetaData field); + + /** + * Get the field or getter for the given attribute of the given class. + * + * @param meta is the declaring class + * @param attribute name of the logical attribute + * @param access whether to look for the field of getter method. + * If unknown, then field or property is chosen based on the access type + * used by the given class. + * @param scanAnnotation if true and access is unknown then scans the + * annotation on the member to determine access. + * + * @since 2.0.0 + */ + public Member getMemberByProperty(ClassMetaData meta, String attribute, + int access, boolean scanAnnotation); /** * Return a runtime exception class to throw for un-implemented diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/NoneMetaDataFactory.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/NoneMetaDataFactory.java index e390c4c57..4e3cd02d4 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/NoneMetaDataFactory.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/NoneMetaDataFactory.java @@ -131,7 +131,12 @@ public class NoneMetaDataFactory return null; } - public Class getUnimplementedExceptionType() { + public Member getMemberByProperty(ClassMetaData meta, String property, + int access, boolean scan) { + return null; + } + + public Class getUnimplementedExceptionType() { return null; } 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 1b9931e82..a366a8e12 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 @@ -23,7 +23,8 @@ access-inconsistent-inherit: "{1}" for class "{0}" is not consistent with \ "{3}" used by its persistent superclass "{2}". All persistent classes in \ an inheritance hierarchy must use a single implicit field or property \ based access style or explicitly declare an access style. - +access-illegal-merge: Field "{0}" with "{1}" is not compatible with "{2}" \ + used by its declaring class. meta-reflect: Using reflection for metadata generation. gen-meta: Generating default metadata for type "{0}". load-cls: Loading metadata for "{0}" under mode "{1}". diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java index 8f7ab5284..1b1ea49c6 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java @@ -314,7 +314,7 @@ public class PersistenceMetaDataDefaults if (sup != null && !AccessCode.isUnknown(sup)) return sup.getAccessType(); - return AccessCode.FIELD; + return getDefaultAccessType(); } /** @@ -598,33 +598,34 @@ public class PersistenceMetaDataDefaults /** * Gets either the instance field or the getter method depending upon the * access style of the given meta-data. - *
- * Defining class is used instead of declaring class because this method - * may be invoked during parsing phase when declaring meta-data may not be - * available. */ - @Override - protected Member getMemberByProperty(ClassMetaData meta, String property) { + public Member getMemberByProperty(ClassMetaData meta, String property, + int access, boolean applyDefaultRule) { Class cls = meta.getDescribedType(); Field field = Reflection.findField(cls, property, false);; Method getter = Reflection.findGetter(cls, property, false); + Method setter = Reflection.findSetter(cls, property, false); + int accessCode = AccessCode.isUnknown(access) ? meta.getAccessType() : + access; + if (field == null && getter == null) + error(meta, _loc.get("access-no-property", cls, property)); if (isAnnotated(field) && isAnnotated(getter)) throw new IllegalStateException(_loc.get("access-duplicate", field, getter).toString()); - if (AccessCode.isField(meta)) { + if (AccessCode.isField(accessCode)) { if (isAnnotatedAccess(getter, AccessType.PROPERTY)) { meta.setAccessType(AccessCode.MIXED | meta.getAccessType()); return getter; } - return field; - } else if (AccessCode.isProperty(meta)) { + return field == null ? getter : field; + } else if (AccessCode.isProperty(accessCode)) { if (isAnnotatedAccess(field, AccessType.FIELD)) { meta.setAccessType(AccessCode.MIXED | meta.getAccessType()); return field; } - return getter; - } else if (AccessCode.isUnknown(meta)) { + return getter == null ? field : getter; + } else if (AccessCode.isUnknown(accessCode)) { if (isAnnotated(field)) { meta.setAccessType(AccessCode.FIELD); return field; diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java index 0e10a6411..d590f79cd 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java @@ -24,8 +24,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedActionException; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; @@ -35,7 +33,6 @@ import java.util.Set; import java.util.Stack; import javax.persistence.CascadeType; import javax.persistence.GenerationType; -import javax.persistence.LockModeType; import static javax.persistence.CascadeType.*; @@ -54,8 +51,8 @@ import org.apache.openjpa.kernel.jpql.JPQLParser; import org.apache.openjpa.lib.conf.Configurations; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.meta.CFMetaDataParser; +import org.apache.openjpa.lib.meta.SourceTracker; import org.apache.openjpa.lib.meta.XMLVersionParser; -import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.AccessCode; import org.apache.openjpa.meta.ClassMetaData; @@ -78,10 +75,13 @@ import org.apache.openjpa.util.ImplHelper; import serp.util.Numbers; /** - * Custom SAX parser used by the system to quickly parse persistence i - * metadata files. + * Custom SAX parser used by the system to quickly parse persistence + * metadata files. This parser may invoke + * {@linkplain AnnotationPersistenceMetaDataParser another parser} to scan + * source code annotation. * * @author Steve Kim + * @author Pinaki Poddar * @nojavadoc */ public class XMLPersistenceMetaDataParser @@ -108,9 +108,9 @@ public class XMLPersistenceMetaDataParser new HashMap(); // Map for storing deferred metadata which needs to be populated - // after embeddedables are loaded. - private static final Map> _embeddables = - new HashMap>(); + // after embeddables are loaded. + private static final Map, ArrayList> + _embeddables = new HashMap, ArrayList>(); static { _elems.put(ELEM_PKG, ELEM_PKG); @@ -178,10 +178,10 @@ public class XMLPersistenceMetaDataParser private int _mode = MODE_NONE; private boolean _override = false; - private final Stack _elements = new Stack(); - private final Stack _parents = new Stack(); + private final Stack _elements = new Stack(); + private final Stack _parents = new Stack(); - private Class _cls = null; + private Class _cls = null; private int _fieldPos = 0; private int _clsPos = 0; private int _access = AccessCode.UNKNOWN; @@ -189,7 +189,7 @@ public class XMLPersistenceMetaDataParser private Set _cascades = null; private Set _pkgCascades = null; - private Class _listener = null; + private Class _listener = null; private Collection[] _callbacks = null; private int[] _highs = null; private boolean _isXMLMappingMetaDataComplete = false; @@ -258,7 +258,7 @@ public class XMLPersistenceMetaDataParser public void setRepository(MetaDataRepository repos) { _repos = repos; if (repos != null - && (repos.getValidate() & repos.VALIDATE_RUNTIME) != 0) + && (repos.getValidate() & MetaDataRepository.VALIDATE_RUNTIME) != 0) setParseComments(false); } @@ -843,20 +843,9 @@ public class XMLPersistenceMetaDataParser return false; } - // if we don't know the default access type, check to see if a - // superclass has already defined the access type - int defaultAccess = _access; - if (defaultAccess == AccessCode.UNKNOWN) { - ClassMetaData sup = repos.getCachedMetaData(_cls.getSuperclass()); - if (sup != null) - defaultAccess = sup.getAccessType(); - } - - int access = AccessCode.UNKNOWN; if (meta == null) { - // add metadata for this type - access = toAccessType(attrs.getValue("access"), defaultAccess); - meta = repos.addMetaData(_cls); + int accessCode = toAccessType(attrs.getValue("access")); + meta = repos.addMetaData(_cls, accessCode); meta.setEnvClassLoader(_envLoader); meta.setSourceMode(MODE_NONE); @@ -864,12 +853,11 @@ public class XMLPersistenceMetaDataParser if (_parser != null) _parser.parse(_cls); } - access = meta.getAccessType(); boolean mappedSuper = "mapped-superclass".equals(elem); boolean embeddable = "embeddable".equals(elem); if (isMetaDataMode()) { - meta.setSource(getSourceFile(), meta.SRC_XML); + meta.setSource(getSourceFile(), SourceTracker.SRC_XML); meta.setSourceMode(MODE_META, true); Locator locator = getLocation().getLocator(); if (locator != null) { @@ -882,8 +870,6 @@ public class XMLPersistenceMetaDataParser meta.setTypeAlias(name); meta.setAbstract(mappedSuper); meta.setEmbeddedOnly(mappedSuper || embeddable); -// if (mappedSuper) -// meta.setIdentityType(meta.ID_UNKNOWN); if (embeddable) { addDeferredEmbeddableMetaData(_cls); @@ -935,19 +921,19 @@ public class XMLPersistenceMetaDataParser * Default access element. */ private void endAccess() { - _access = toAccessType(currentText(), AccessCode.UNKNOWN); + _access = toAccessType(currentText()); } /** * Parse the given string as an entity access type, defaulting to given * default if string is empty. */ - private int toAccessType(String str, int def) { + private int toAccessType(String str) { if (StringUtils.isEmpty(str)) - return def; + return AccessCode.UNKNOWN; if ("PROPERTY".equals(str)) - return AccessCode.PROPERTY; - return AccessCode.FIELD; + return AccessCode.EXPLICIT | AccessCode.PROPERTY; + return AccessCode.EXPLICIT | AccessCode.FIELD; } /** @@ -1006,7 +992,7 @@ public class XMLPersistenceMetaDataParser Object cur = currentElement(); Object scope = (cur instanceof ClassMetaData) ? ((ClassMetaData) cur).getDescribedType() : null; - meta.setSource(getSourceFile(), scope, meta.SRC_XML); + meta.setSource(getSourceFile(), scope, SourceTracker.SRC_XML); Locator locator = getLocation().getLocator(); if (locator != null) { meta.setLineNumber(Numbers.valueOf(locator.getLineNumber())); @@ -1064,7 +1050,7 @@ public class XMLPersistenceMetaDataParser ClassMetaData meta = (ClassMetaData) currentElement(); String cls = attrs.getValue("class"); - Class idCls = null; + Class idCls = null; try { idCls = classForName(cls); } catch (Throwable t) { @@ -1203,40 +1189,10 @@ public class XMLPersistenceMetaDataParser if ((field == null || field.getDeclaredType() == Object.class || field.getAccessType() != fldAccess) && meta.getDescribedType() != Object.class) { - Member member = null; - Class type = null; - try { - if ((field != null && - field.getAccessType() == AccessCode.PROPERTY) || - (fldAccess == AccessCode.PROPERTY)) { - String cap = StringUtils.capitalize(name); - type = meta.getDescribedType(); - try { - member = AccessController.doPrivileged( - J2DoPrivHelper.getDeclaredMethodAction( - type, "get" + cap, - (Class[]) null));// varargs disambiguate - } catch (Exception excep) { - try { - member = AccessController.doPrivileged( - J2DoPrivHelper.getDeclaredMethodAction( - type, "is" + cap, (Class[]) null)); - } catch (Exception excep2) { - throw excep; - } - } - type = ((Method) member).getReturnType(); - } else { - member = AccessController.doPrivileged( - J2DoPrivHelper.getDeclaredFieldAction( - meta.getDescribedType(), name)); - type = ((Field) member).getType(); - } - } catch (Exception e) { - if (e instanceof PrivilegedActionException) - e = ((PrivilegedActionException) e).getException(); - throw getException(_loc.get("invalid-attr", name, meta), e); - } + Member member = _repos.getMetaDataFactory().getDefaults() + .getMemberByProperty(meta, name, fldAccess, false); + Class type = Field.class.isInstance(member) ? + ((Field)member).getType() : ((Method)member).getReturnType(); if (field == null) { field = meta.addDeclaredField(name, type); @@ -1288,9 +1244,9 @@ public class XMLPersistenceMetaDataParser if (attrs != null) { String access = attrs.getValue("access"); if ("PROPERTY".equals(access)) - return AccessCode.PROPERTY; + return AccessCode.EXPLICIT | AccessCode.PROPERTY; if ("FIELD".equals(access)) - return AccessCode.FIELD; + return AccessCode.EXPLICIT | AccessCode.FIELD; } // Check access defined on field, if provided if (field != null) { @@ -1607,7 +1563,7 @@ public class XMLPersistenceMetaDataParser QueryMetaData meta = getRepository().searchQueryMetaDataByName(name); if (meta != null) { - Class defType = meta.getDefiningType(); + Class defType = meta.getDefiningType(); if ((defType != _cls) && log.isWarnEnabled()) { log.warn(_loc.get("dup-query", name, currentLocation(), defType)); @@ -1634,7 +1590,7 @@ public class XMLPersistenceMetaDataParser Object cur = currentElement(); Object scope = (cur instanceof ClassMetaData) ? ((ClassMetaData) cur).getDescribedType() : null; - meta.setSource(getSourceFile(), scope, meta.SRC_XML); + meta.setSource(getSourceFile(), scope, SourceTracker.SRC_XML); if (isMetaDataMode()) meta.setSourceMode(MODE_META); else if (isMappingMode()) @@ -1698,7 +1654,7 @@ public class XMLPersistenceMetaDataParser meta.setLanguage(QueryLanguages.LANG_SQL); String val = attrs.getValue("result-class"); if (val != null) { - Class type = classForName(val); + Class type = classForName(val); if (ImplHelper.isManagedType(getConfiguration(), type)) meta.setCandidateType(type); else @@ -1712,7 +1668,7 @@ public class XMLPersistenceMetaDataParser Object cur = currentElement(); Object scope = (cur instanceof ClassMetaData) ? ((ClassMetaData) cur).getDescribedType() : null; - meta.setSource(getSourceFile(), scope, meta.SRC_XML); + meta.setSource(getSourceFile(), scope, SourceTracker.SRC_XML); Locator locator = getLocation().getLocator(); if (locator != null) { meta.setLineNumber(Numbers.valueOf(locator.getLineNumber())); @@ -1829,7 +1785,7 @@ public class XMLPersistenceMetaDataParser return false; boolean system = currentElement() == null; - Class type = currentElement() == null ? null : + Class type = currentElement() == null ? null : ((ClassMetaData) currentElement()).getDescribedType(); if (type == null) type = Object.class; @@ -1841,7 +1797,6 @@ public class XMLPersistenceMetaDataParser _highs = new int[LifecycleEvent.ALL_EVENTS.length]; } - MetaDataDefaults def = _repos.getMetaDataFactory().getDefaults(); LifecycleCallbacks adapter; if (_listener != null) adapter = new BeanLifecycleCallbacks(_listener, @@ -1876,7 +1831,7 @@ public class XMLPersistenceMetaDataParser */ private void storeCallbacks(ClassMetaData cls) { LifecycleMetaData meta = cls.getLifecycleMetaData(); - Class supCls = cls.getDescribedType().getSuperclass(); + Class supCls = cls.getDescribedType().getSuperclass(); Collection[] supCalls = null; if (!Object.class.equals(supCls)) { supCalls = AnnotationPersistenceMetaDataParser.parseCallbackMethods @@ -1913,7 +1868,7 @@ public class XMLPersistenceMetaDataParser /** * Instantiate the given class, taking into account the default package. */ - protected Class classForName(String name) + protected Class classForName(String name) throws SAXException { if ("Entity".equals(name)) return PersistenceCapable.class; @@ -1927,7 +1882,7 @@ public class XMLPersistenceMetaDataParser * @param embedType embeddable class * @param access class level access for embeddable */ - protected void addDeferredEmbeddableMetaData(Class embedType) { + protected void addDeferredEmbeddableMetaData(Class embedType) { ArrayList fmds = _embeddables.get(embedType); if (fmds != null && fmds.size() > 0) { for (int i = fmds.size() -1 ; i >= 0; i--) { @@ -1948,7 +1903,7 @@ public class XMLPersistenceMetaDataParser } } - protected void deferEmbeddable(Class embedType, MetaDataContext fmd) { + protected void deferEmbeddable(Class embedType, MetaDataContext fmd) { ArrayList fmds = _embeddables.get(embedType); if (fmds == null) { fmds = new ArrayList(); diff --git a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties index 4a0aa2228..41b43e9b8 100644 --- a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties +++ b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties @@ -23,6 +23,8 @@ access-mixed: Class "{0}" annotated fields "{1}" with FIELD access and \ access-none: Property "{1}" in class "{0}" is not annotated an instance field \ or a getter method. It is not possible to determine its access type access-unknown: Access style for "{0}" can not be determined. +access-no-property: No field or getter method for attribute "{1}" can be found \ + in "{0}". close-invoked: You have closed the EntityManager, though the persistence \ context will remain active until the current transaction commits. no-managed-trans: There is no managed transaction in progress to sync this \