Support for managed interfaces

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@439383 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Kim 2006-09-01 17:16:50 +00:00
parent c39b7141ce
commit 423b0a418a
14 changed files with 240 additions and 47 deletions

View File

@ -90,8 +90,9 @@ public class PCEnhancer {
public static final int ENHANCE_NONE = 0;
public static final int ENHANCE_AWARE = 2 << 0;
public static final int ENHANCE_PC = 2 << 1;
public static final int ENHANCE_OID = 2 << 2;
public static final int ENHANCE_INTERFACE = 2 << 1;
public static final int ENHANCE_PC = 2 << 2;
public static final int ENHANCE_OID = 2 << 3;
private static final String PRE = "pc";
private static final Class PCTYPE = PersistenceCapable.class;
@ -127,7 +128,7 @@ public class PCEnhancer {
* Constructor. Supply configuration and type to enhance.
*/
public PCEnhancer(OpenJPAConfiguration conf, Class type) {
this(conf, new Project().loadClass(type), null);
this(conf, new Project().loadClass(type), (MetaDataRepository) null);
}
/**
@ -163,6 +164,17 @@ public class PCEnhancer {
_meta = _repos.getMetaData(type.getType(), null, false);
}
/**
* Constructor. Supply configuration, type, and metadata.
*/
public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
ClassMetaData meta) {
_pc = type;
_log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
_repos = meta.getRepository();
_meta = meta;
}
/**
* Return the bytecode representation of the class being manipulated.
*/
@ -266,6 +278,10 @@ public class PCEnhancer {
_log.trace(_loc.get("enhance-start", _pc.getType()));
try {
// if managed interface, skip
if (_pc.isInterface())
return ENHANCE_INTERFACE;
// check if already enhanced
Class[] interfaces = _pc.getDeclaredInterfaceTypes();
for (int i = 0; i < interfaces.length; i++) {
@ -612,13 +628,13 @@ public class PCEnhancer {
String prefix = (get) ? PRE + "Get" : PRE + "Set";
methodName = prefix + name;
if (get) {
mi.setMethod(owner.getDescribedType().getName(),
mi.setMethod(getType(owner).getName(),
methodName, typeName, new String[]
{ owner.getDescribedType().getName() });
{ getType(owner).getName() });
} else {
mi.setMethod(owner.getDescribedType().getName(),
mi.setMethod(getType(owner).getName(),
methodName, "void", new String[]
{ owner.getDescribedType().getName(), typeName });
{ getType(owner).getName(), typeName });
}
}
}
@ -659,6 +675,10 @@ public class PCEnhancer {
if (owner.getName().equals(Object.class.getName()))
return null;
// managed interface
if (_meta != null && _meta.getDescribedType().isInterface())
return _meta;
return _repos.getMetaData(owner, null, false);
}
@ -722,8 +742,9 @@ public class PCEnhancer {
// super.pcClearFields ()
if (_meta.getPCSuperclass() != null) {
code.aload().setThis();
code.invokespecial().setMethod(_meta.getPCSuperclass(),
PRE + "ClearFields", void.class, null);
code.invokespecial().setMethod(getType(_meta.
getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
null);
}
FieldMetaData[] fmds = _meta.getDeclaredFields();
@ -849,7 +870,8 @@ public class PCEnhancer {
// return <fields> + <superclass>.pcGetManagedFieldCount ()
code.constant().setValue(_meta.getDeclaredFields().length);
if (_meta.getPCSuperclass() != null) {
code.invokestatic().setMethod(_meta.getPCSuperclass().getName(),
code.invokestatic().setMethod(getType(_meta.
getPCSuperclassMetaData()).getName(),
PRE + "GetManagedFieldCount", int.class.getName(), null);
code.iadd();
}
@ -1039,14 +1061,15 @@ public class PCEnhancer {
loadManagedInstance(code, false);
String[] args;
if (copy) {
args = new String[]{ _meta.getPCSuperclass().getName(),
int.class.getName() };
args = new String[]{ getType(_meta.getPCSuperclassMetaData()).
getName(), int.class.getName() };
code.aload().setParam(0);
} else
args = new String[]{ int.class.getName() };
code.iload().setParam(fieldNumber);
code.invokespecial().setMethod(_meta.getPCSuperclass().
getName(), name, void.class.getName(), args);
code.invokespecial().setMethod(getType(_meta.
getPCSuperclassMetaData()).getName(), name,
void.class.getName(), args);
code.vreturn();
} else
throwException(code, IllegalArgumentException.class);
@ -1442,7 +1465,8 @@ public class PCEnhancer {
loadManagedInstance(code, false);
for (int i = 0; i < args.length; i++)
code.aload().setParam(i);
code.invokespecial().setMethod(_meta.getPCSuperclass().getName(),
code.invokespecial().setMethod(getType(_meta.
getPCSuperclassMetaData()).getName(),
PRE + "CopyKeyFieldsToObjectId", void.class.getName(), args);
}
@ -1535,7 +1559,8 @@ public class PCEnhancer {
loadManagedInstance(code, false);
for (int i = 0; i < args.length; i++)
code.aload().setParam(i);
code.invokespecial().setMethod(_meta.getPCSuperclass().getName(),
code.invokespecial().setMethod(getType(_meta.
getPCSuperclassMetaData()).getName(),
PRE + "CopyKeyFieldsFromObjectId", void.class.getName(), args);
}
@ -1729,14 +1754,14 @@ public class PCEnhancer {
// new ObjectId (cls, oid)
code.anew().setType(ObjectId.class);
code.dup();
code.classconstant().setClass(_meta.getDescribedType());
code.classconstant().setClass(getType(_meta));
}
// new <oid class> ();
code.anew().setType(oidType);
code.dup();
if (_meta.isOpenJPAIdentity() || (obj && usesClsString == Boolean.TRUE))
code.classconstant().setClass(_meta.getDescribedType());
code.classconstant().setClass(getType(_meta));
if (obj) {
code.aload().setParam(0);
code.checkcast().setType(String.class);
@ -1926,12 +1951,14 @@ public class PCEnhancer {
Code code = getOrCreateClassInitCode(true);
if (_meta.getPCSuperclass() != null) {
// pcInheritedFieldCount = <superClass>.pcGetManagedFieldCount()
code.invokestatic().setMethod(_meta.getPCSuperclass().getName(),
code.invokestatic().setMethod(getType(_meta.
getPCSuperclassMetaData()).getName(),
PRE + "GetManagedFieldCount", int.class.getName(), null);
code.putstatic().setField(INHERIT, int.class);
// pcPCSuperclass = <superClass>;
code.classconstant().setClass(_meta.getPCSuperclass());
code.classconstant().setClass(getType(_meta.
getPCSuperclassMetaData()));
code.putstatic().setField(SUPER, Class.class);
}
@ -2961,7 +2988,7 @@ public class PCEnhancer {
// readUnmanaged (in);
loadManagedInstance(code, false);
code.aload().setParam(0);
code.invokevirtual().setMethod(_meta.getDescribedType(),
code.invokevirtual().setMethod(getType(_meta),
PRE + "ReadUnmanaged", void.class, inargs);
if (detachedState) {
@ -3013,8 +3040,9 @@ public class PCEnhancer {
if (parentDetachable) {
loadManagedInstance(code, false);
code.aload().setParam(0);
code.invokespecial().setMethod(_meta.getPCSuperclass(),
PRE + "ReadUnmanaged", void.class, inargs);
code.invokespecial().setMethod(getType(_meta.
getPCSuperclassMetaData()), PRE + "ReadUnmanaged", void.class,
inargs);
}
// read declared unmanaged serializable fields
@ -3091,7 +3119,7 @@ public class PCEnhancer {
Code code = meth.getCode(true);
// super.writeExternal (out);
Class sup = _meta.getDescribedType().getSuperclass();
Class sup = getType(_meta).getSuperclass();
if (!parentDetachable && Externalizable.class.isAssignableFrom(sup)) {
loadManagedInstance(code, false);
code.aload().setParam(0);
@ -3102,7 +3130,7 @@ public class PCEnhancer {
// writeUnmanaged (out);
loadManagedInstance(code, false);
code.aload().setParam(0);
code.invokevirtual().setMethod(_meta.getDescribedType(),
code.invokevirtual().setMethod(getType(_meta),
PRE + "WriteUnmanaged", void.class, outargs);
JumpInstruction go2 = null;
@ -3169,8 +3197,9 @@ public class PCEnhancer {
if (parentDetachable) {
loadManagedInstance(code, false);
code.aload().setParam(0);
code.invokespecial().setMethod(_meta.getPCSuperclass(),
PRE + "WriteUnmanaged", void.class, outargs);
code.invokespecial().setMethod(getType(_meta.
getPCSuperclassMetaData()), PRE + "WriteUnmanaged", void.class,
outargs);
}
// write declared unmanaged serializable fields
@ -3316,7 +3345,7 @@ public class PCEnhancer {
// static void pcSet<field> (XXX inst, <fieldtype> value)
BCField field = _pc.getDeclaredField(fmd.getName());
setter = _pc.declareMethod(PRE + "Set" + fmd.getName(), void.class,
new Class[]{ _meta.getDescribedType(), fmd.getDeclaredType() });
new Class[]{ getType(_meta), fmd.getDeclaredType() });
setter.setAccessFlags(field.getAccessFlags()
& ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE);
setter.setStatic(true);
@ -3337,6 +3366,17 @@ public class PCEnhancer {
return setter;
}
/**
* Return the concrete type for the given class, i.e. impl for managed
* interfaces
*/
public Class getType(ClassMetaData meta) {
if (meta.getInterfaceImpl() != null)
return meta.getInterfaceImpl();
return meta.getDescribedType();
}
/**
* Move code-related attributes from one method to another.
*/
@ -3479,6 +3519,8 @@ public class PCEnhancer {
status = enhancer.run();
if (status == ENHANCE_NONE)
log.info(_loc.get("enhance-norun"));
else if (status == ENHANCE_INTERFACE)
log.info(_loc.get("enhance-interface"));
else if (status == ENHANCE_AWARE) {
log.info(_loc.get("enhance-aware"));
enhancer.record();

View File

@ -2494,6 +2494,9 @@ public class BrokerImpl
PersistenceCapable copy;
PCState state;
Class type = meta.getDescribedType();
if (type.isInterface())
type = meta.getInterfaceImpl();
if (obj != null) {
// give copy and the original instance the same state manager
// so that we can copy fields from one to the other
@ -2512,8 +2515,7 @@ public class BrokerImpl
// copy the instance. we do this even if it doesn't already
// have a state manager in case it is later assigned to a
// PC field; at that point it's too late to copy
copy = PCRegistry.newInstance(meta.getDescribedType(),
copySM, false);
copy = PCRegistry.newInstance(type, copySM, false);
int[] fields = new int[meta.getFields().length];
for (int i = 0; i < fields.length; i++)
fields[i] = i;
@ -2527,8 +2529,7 @@ public class BrokerImpl
pc.pcReplaceStateManager(null);
}
} else {
copy = PCRegistry.newInstance(meta.getDescribedType(), sm,
false);
copy = PCRegistry.newInstance(type, sm, false);
if ((_flags & FLAG_ACTIVE) != 0 && !_optimistic)
state = PCState.ECLEAN;
else
@ -3986,8 +3987,11 @@ public class BrokerImpl
public Object newInstance(Class cls) {
assertOpen();
//### JDO2
if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers()))
if (cls.isInterface()) {
ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
getMetaData(cls, _loader, true);
cls = meta.getInterfaceImpl();
} else if (Modifier.isAbstract(cls.getModifiers()))
throw new UnsupportedOperationException(_loc.get
("new-abstract", cls).getMessage());

View File

@ -241,6 +241,8 @@ public class StateManagerImpl
}
_meta = sub;
}
if (cls.isInterface())
cls = _meta.getInterfaceImpl();
PersistenceCapable inst = PCRegistry.newInstance(cls, this, _oid, true);
if (inst == null) {

View File

@ -41,6 +41,7 @@ public abstract class AbstractMetaDataDefaults
private int _access = ClassMetaData.ACCESS_FIELD;
private boolean _ignore = true;
private boolean _interface = true;
private boolean _pcRegistry = true;
private int _callback = CALLBACK_RETHROW;
@ -170,7 +171,8 @@ public abstract class AbstractMetaDataDefaults
*/
private void populateFromReflection(ClassMetaData meta) {
Member[] members;
if (meta.getAccessType() == ClassMetaData.ACCESS_FIELD)
boolean iface = meta.getDescribedType().isInterface();
if (meta.getAccessType() == ClassMetaData.ACCESS_FIELD && !iface)
members = meta.getDescribedType().getDeclaredFields();
else
members = meta.getDescribedType().getDeclaredMethods();
@ -263,6 +265,14 @@ public abstract class AbstractMetaDataDefaults
protected abstract boolean isDefaultPersistent(ClassMetaData meta,
Member member, String name);
public void setDeclaredInterfacePersistent(boolean pers) {
_interface = pers;
}
public boolean isDeclaredInterfacePersistent() {
return _interface;
}
public Member getBackingMember(FieldMetaData fmd) {
if (fmd == null)
return null;
@ -321,6 +331,10 @@ public abstract class AbstractMetaDataDefaults
new String[]{ name, clsName, "get" + capName, "is" + capName }));
}
public Class getUnimplementedExceptionType() {
return UnsupportedOperationException.class;
}
/**
* Helper method; returns true if the given class appears to be
* user-defined.

View File

@ -140,6 +140,8 @@ public class ClassMetaData
private Boolean _openjpaId = null;
private Boolean _extent = null;
private Boolean _embedded = null;
private Boolean _interface = null;
private Class _impl = null;
private int _identity = -1;
private int _idStrategy = ValueStrategies.NONE;
private int _accessType = ACCESS_UNKNOWN;
@ -215,9 +217,8 @@ public class ClassMetaData
* an embedded value changes its declared type.
*/
protected void setDescribedType(Class type) {
if (type.isInterface())
throw new MetaDataException(_loc.get("interface", type));
if ("java.lang.Enum".equals(type.getSuperclass().getName()))
if (type.getSuperclass() != null && "java.lang.Enum".equals
(type.getSuperclass().getName()))
throw new MetaDataException(_loc.get("enum", type));
_type = type;
}
@ -669,6 +670,38 @@ public class ClassMetaData
_embedded = (embed) ? Boolean.TRUE : Boolean.FALSE;
}
/**
* Whether the type is a managed interface.
*/
public boolean isManagedInterface() {
if (!_type.isInterface())
return false;
return _interface == null ? false : _interface.booleanValue();
}
/**
* Whether the type is a managed interface
*/
public void setManagedInterface(boolean managedInterface) {
if (!_type.isInterface())
throw new MetaDataException(_loc.get("not-interface", _type));
_interface = managedInterface ? Boolean.TRUE : Boolean.FALSE;
}
/**
* Return the managed interface implementor if any.
*/
public Class getInterfaceImpl() {
return _impl;
}
/**
* Set the managed interface implementor class.
*/
public void setInterfaceImpl(Class impl) {
_impl = impl;
}
/**
* Return the number of fields that use impl or intermediate data, in
* order to create a compacted array for storage of said data.
@ -1481,7 +1514,19 @@ public class ClassMetaData
log.trace(_loc.get((embed) ? "resolve-embed-meta" : "resolve-meta",
this + "@" + System.identityHashCode(this)));
if (runtime && !PersistenceCapable.class.isAssignableFrom(_type))
if (_type.isInterface()) {
if (!embed && _interface != Boolean.TRUE)
throw new MetaDataException(_loc.get("interface", _type));
if (runtime) {
_impl = _repos.getImplGenerator().createImpl(this);
if (!embed)
_repos.setInterfaceImpl(this, _impl);
}
}
if (runtime && !_type.isInterface() &&
!PersistenceCapable.class.isAssignableFrom(_type))
throw new MetaDataException(_loc.get("not-enhanced", _type));
// are we the target of an embedded value?
@ -2113,6 +2158,8 @@ public class ClassMetaData
_objectId = meta.getObjectIdType();
_extent = (meta.getRequiresExtent()) ? Boolean.TRUE : Boolean.FALSE;
_embedded = (meta.isEmbeddedOnly()) ? Boolean.TRUE : Boolean.FALSE;
_interface = (meta.isManagedInterface()) ? Boolean.TRUE : Boolean.FALSE;
_impl = meta.getInterfaceImpl();
_identity = meta.getIdentityType();
_idStrategy = meta.getIdentityStrategy();
_seqName = meta.getIdentitySequenceName();

View File

@ -62,8 +62,14 @@ class InheritanceComparator
int i1 = levels(c1);
int i2 = levels(c2);
if (i1 == i2)
if (i1 == i2) {
// sort simple interfaces as well as simple order test will fail.
if (c1.isAssignableFrom(o2.getClass()))
return -1;
if (c2.isAssignableFrom(o1.getClass()))
return 1;
return c1.getName().compareTo(c2.getName());
}
return i1 - i2;
}

View File

@ -50,6 +50,12 @@ public interface MetaDataDefaults
*/
public void setIgnoreNonPersistent(boolean ignore);
/**
* Whether declared interfaces of a class are treated as persistent
* types. Defaults to true.
*/
public boolean isDeclaredInterfacePersistent();
/**
* Populate the given metadata with default settings.
*
@ -61,4 +67,10 @@ public interface MetaDataDefaults
* Return the backing member for the given field metadata.
*/
public Member getBackingMember(FieldMetaData field);
/**
* Return a runtime exception class to throw for un-implemented
* managed interface methods.
*/
public Class getUnimplementedExceptionType();
}

View File

@ -25,7 +25,11 @@ public class MetaDataInheritanceComparator
extends InheritanceComparator {
protected Class toClass(Object elem) {
return (elem == null) ? null
: ((ClassMetaData) elem).getDescribedType();
if (elem == null)
return null;
ClassMetaData meta = (ClassMetaData) elem;
if (meta.getInterfaceImpl() != null)
return meta.getInterfaceImpl();
return meta.getDescribedType();
}
}

View File

@ -105,6 +105,7 @@ public class MetaDataRepository
private final Map _metas = new HashMap();
private final Map _oids = Collections.synchronizedMap(new HashMap());
private final Map _impls = Collections.synchronizedMap(new HashMap());
private final Map _ifaces = Collections.synchronizedMap(new HashMap());
private final Map _queries = new HashMap();
private final Map _seqs = new HashMap();
private final Map _aliases = Collections.synchronizedMap(new HashMap());
@ -116,6 +117,7 @@ public class MetaDataRepository
private OpenJPAConfiguration _conf = null;
private Log _log = null;
private MetaDataFactory _factory = null;
private InterfaceImplGenerator _implGen = null;
private int _resMode = MODE_META | MODE_MAPPING;
private int _sourceMode = MODE_META | MODE_MAPPING | MODE_QUERY;
private int _validate = VALIDATE_META | VALIDATE_UNENHANCED;
@ -541,6 +543,18 @@ public class MetaDataRepository
} else
sup = sup.getSuperclass();
}
if (meta.getDescribedType().isInterface()) {
Class[] sups = meta.getDescribedType().getInterfaces();
for (int i = 0; i < sups.length; i++) {
supMeta = getMetaData(sups[i], 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()));
@ -845,12 +859,41 @@ public class MetaDataRepository
if (cls == null)
return false;
if (_metas.remove(cls) != null) {
Class impl = (Class) _ifaces.remove(cls);
if (impl != null)
_metas.remove(impl);
_count--;
return true;
}
return false;
}
/**
* Add the given metadata as declared interface implementation.
*/
public void addDeclaredInterfaceImpl(ClassMetaData meta, Class iface) {
synchronized (_impls) {
addToCollection(_impls, iface, meta.getDescribedType(), false);
}
}
/**
* Set the implementation for the given managed interface.
*/
synchronized void setInterfaceImpl(ClassMetaData meta, Class impl) {
if (!meta.isManagedInterface())
throw new MetaDataException(_loc.get("not-managed-interface",
meta, impl));
_ifaces.put(meta.getDescribedType(), impl);
_metas.put(impl, meta);
}
synchronized InterfaceImplGenerator getImplGenerator() {
if (_implGen == null)
_implGen = new InterfaceImplGenerator(this);
return _implGen;
}
/**
* Return the least-derived class metadata for the given application
* identity object.
@ -1274,6 +1317,8 @@ 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())
return;
// allow users to query on common non-pc superclasses
Class sup = check.getSuperclass();
if (leastDerived == cls && sup != null && sup != Object.class) {

View File

@ -104,10 +104,18 @@ public class NoneMetaDataFactory
public void setIgnoreNonPersistent(boolean ignore) {
}
public boolean isDeclaredInterfacePersistent() {
return false;
}
public void populate(ClassMetaData meta, int access) {
}
public Member getBackingMember(FieldMetaData fmd) {
return null;
}
public Class getUnimplementedExceptionType() {
return null;
}
}

View File

@ -257,8 +257,11 @@ public class ApplicationIds {
// to the original oid values, then copy its key fields to a new
// oid instance
if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) {
PersistenceCapable pc = PCRegistry.newInstance
(meta.getDescribedType(), null, oid, false);
Class type = meta.getDescribedType();
if (meta.getInterfaceImpl() != null)
type = meta.getInterfaceImpl();
PersistenceCapable pc = PCRegistry.newInstance(type, null, oid,
false);
Object copy = pc.pcNewObjectIdInstance();
pc.pcCopyKeyFieldsToObjectId(copy);
return copy;

View File

@ -24,6 +24,7 @@ enhance-running-oids: Enhancer running on oid: {0}
enhance-aware: The class does not have metadata - enhanced as persistence-aware.
enhance-norun: The class is already persistence capable - no enhancement \
performed.
enhance-interface: The class is a managed interface - no enhancement performed.
enhance-usage: Usage: java org.apache.openjpa.enhance.PCEnhancer\n\
\t[-properties/-p <properties file or resource>]\n\
\t[-<property name> <property value>]*\n\

View File

@ -4,8 +4,8 @@ unloaded-detached: Attempt to access an unloaded field of detached instance \
"{0}".
meta-unknownid: Cannot manipulate identity of type "{0}": it''s identity type \
is unknown.
new-abstract: Cannot create an instance of "{0}": abstract classes and \
interfaces are not yet supported.
new-abstract: Cannot create an instance of "{0}": abstract classes are not \
yet supported.
bad-new-query: Attempt to construct a query from an extent or class. You must \
pass a (possibly null) query string or template to the query factory \
method when creating the query.

View File

@ -18,6 +18,8 @@ clear-repos: Clearing metadata repository "{0}".
process-registered: Processing registered persistence-capable class "{0}".
assigned-sup: Set persistence-capable superclass of "{0}" to "{1}".
found-pcs: Found {0} classes with metadata in {1} milliseconds.
not-managed-interface: Cannot set type "{1}" as interface "{0}" implementor. \
"{0}" is not a managed interface.
unmanaged-sup-field: Superclass field "{0}" is mapped in the metadata for \
subclass "{1}", but is not a persistent field.
bad-discover-class: The class "{0}" listed in the openjpa.MetaDataFactory \
@ -180,7 +182,10 @@ bad-update-strategy: "{0}" declares a read only value of "{1}". \
bad-update-strategy-hint: "{0}" declares a read only value of \
"{1}". This is not a recognized strategy, though it closely resembles \
the standard strategy "{2}". Available strategies are: {3}
interface: Type "{0}" is an interface and therefore cannot be made persistent.
not-interface: Can't set type "{0}" to be a managed interface as it is not \
and interface.
interface: The type "{0}" is an unmanaged interface and therefore cannot be \
made persistent.
enum: Type "{0}" is an enumeration and therefore cannot be made persistent.
bad-drop: The metadata for some of the following classes may not have \
been dropped: {0}