diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
index 485e8b09d..a2f527737 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
@@ -176,7 +176,9 @@ public class PCEnhancer {
private boolean _bcsConfigured = false;
/**
- * Constructor. Supply configuration and type to enhance.
+ * Constructor. Supply configuration and type to enhance. This will look
+ * up the metadata for type
from conf
's
+ * repository.
*/
public PCEnhancer(OpenJPAConfiguration conf, Class type) {
this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper
@@ -185,12 +187,14 @@ public class PCEnhancer {
}
/**
- * Constructor. Supply configuration and type to enhance.
+ * Constructor. Supply configuration and type to enhance. This will look
+ * up the metadata for meta
by converting back to a class
+ * and then loading from conf
's repository.
*/
- public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData type) {
+ public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData meta) {
this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper
- .loadProjectClassAction(new Project(), type.getDescribedType())),
- type.getRepository());
+ .loadProjectClassAction(new Project(), meta.getDescribedType())),
+ meta.getRepository());
}
/**
@@ -241,6 +245,36 @@ public class PCEnhancer {
_meta = _repos.getMetaData(type.getType(), loader, false);
}
+ /**
+ * Constructor. Supply repository. The repository's configuration will
+ * be used, and the metadata passed in will be used as-is without doing
+ * any additional lookups. This is useful when running the enhancer
+ * during metadata load.
+ *
+ * @param repos a metadata repository to use for metadata access,
+ * or null to create a new reporitory; the repository
+ * from the given configuration isn't used by default
+ * because the configuration might be an
+ * implementation-specific subclass whose metadata
+ * required more than just base metadata files
+ * @param type the bytecode representation fo the type to
+ * enhance; this can be created from any stream or file
+ * @param meta the metadata to use for processing this type.
+ *
+ * @since 1.1.0
+ */
+ public PCEnhancer(MetaDataRepository repos, BCClass type,
+ ClassMetaData meta) {
+ _managedType = type;
+ _pc = type;
+
+ _log = repos.getConfiguration()
+ .getLog(OpenJPAConfiguration.LOG_ENHANCE);
+
+ _repos = repos;
+ _meta = meta;
+ }
+
static String toPCSubclassName(Class cls) {
return Strings.getPackageName(PCEnhancer.class) + "."
+ cls.getName().replace('.', '$') + "$pcsubclass";
@@ -453,7 +487,7 @@ public class PCEnhancer {
try {
// if managed interface, skip
- if (_managedType.isInterface())
+ if (_pc.isInterface())
return ENHANCE_INTERFACE;
// check if already enhanced
@@ -526,8 +560,6 @@ public class PCEnhancer {
} else {
_isAlreadySubclassed = true;
}
- } else {
- _pc = _managedType;
}
_bcsConfigured = true;
@@ -2656,8 +2688,10 @@ public class PCEnhancer {
}
// pcPCSuperclass = ;
- code.classconstant().setClass(getType(_meta.
- getPCSuperclassMetaData()));
+ // this intentionally calls getDescribedType() directly
+ // instead of PCEnhancer.getType()
+ code.classconstant().setClass(
+ _meta.getPCSuperclassMetaData().getDescribedType());
code.putstatic().setField(SUPER, Class.class);
}
@@ -2698,7 +2732,7 @@ public class PCEnhancer {
// PCRegistry.register (cls,
// pcFieldNames, pcFieldTypes, pcFieldFlags,
// pcPCSuperclass, alias, new XXX ());
- code.classconstant().setClass(_managedType);
+ code.classconstant().setClass(_meta.getDescribedType());
code.getstatic().setField(PRE + "FieldNames", String[].class);
code.getstatic().setField(PRE + "FieldTypes", Class[].class);
code.getstatic().setField(PRE + "FieldFlags", byte[].class);
@@ -3627,19 +3661,19 @@ public class PCEnhancer {
// first, see if we can convert the attribute name to a field name
String fieldName = toBackingFieldName(attrName);
- // next, find the field in the managed type.
- BCField[] fields = (BCField[]) AccessController
- .doPrivileged(J2DoPrivHelper.getBCClassFieldsAction(_managedType,
- fieldName));
+ // next, find the field in the managed type hierarchy
BCField field = null;
- for (int i = 0; i < fields.length; i++) {
- field = fields[i];
- // if we reach a field declared in this type, then this is the
- // most-masking field, and is the one that we want.
- // ##### probably should walk up the hierarchy, or check that
- // ##### serp does that.
- if (fields[i].getDeclarer() == declarer) {
- break;
+ outer: for (BCClass bc = _pc; bc != null; bc = bc.getSuperclassBC()) {
+ BCField[] fields = (BCField[]) AccessController
+ .doPrivileged(J2DoPrivHelper.getBCClassFieldsAction(bc,
+ fieldName));
+ for (int i = 0; i < fields.length; i++) {
+ field = fields[i];
+ // if we reach a field declared in this type, then this is the
+ // most-masking field, and is the one that we want.
+ if (fields[i].getDeclarer() == declarer) {
+ break outer;
+ }
}
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
index d69efafec..454e16efb 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
@@ -28,6 +28,7 @@ import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.ReferenceMap;
import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap;
import org.apache.openjpa.util.UserException;
+import org.apache.openjpa.util.InvalidStateException;
/**
* Tracks registered persistence-capable classes.
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
index 339220863..e945eba5a 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
@@ -557,7 +557,11 @@ public interface Broker
* an interface or an abstract class whose abstract methods follow the
* JavaBeans convention, this method will create a concrete implementation
* according to the metadata that defines the class.
- * Otherwise, this will return an instance of the specified class.
+ * Otherwise, if cls
is a managed type, this will return an
+ * instance of the specified class.
+ *
+ * @throws IllegalArgumentException if cls
is not a managed
+ * type or interface.
*/
public Object newInstance(Class cls);
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
index 2cf9b8915..65af58107 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
@@ -2641,8 +2641,6 @@ 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
@@ -4152,11 +4150,7 @@ public class BrokerImpl
public Object newInstance(Class cls) {
assertOpen();
- if (cls.isInterface()) {
- ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
- getMetaData(cls, _loader, true);
- cls = meta.getInterfaceImpl();
- } else if (Modifier.isAbstract(cls.getModifiers()))
+ if (!cls.isInterface() && Modifier.isAbstract(cls.getModifiers()))
throw new UnsupportedOperationException(_loc.get
("new-abstract", cls).getMessage());
@@ -4169,7 +4163,14 @@ public class BrokerImpl
} catch (Throwable t) {
}
}
- return PCRegistry.newInstance(cls, null, false);
+ try {
+ return PCRegistry.newInstance(cls, null, false);
+ } catch (IllegalStateException ise) {
+ IllegalArgumentException iae =
+ new IllegalArgumentException(ise.getMessage());
+ iae.setStackTrace(ise.getStackTrace());
+ throw iae;
+ }
}
public Object getObjectId(Object obj) {
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
index c11fa18f6..eb33507a7 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
@@ -260,8 +260,6 @@ public class StateManagerImpl
}
_meta = sub;
}
- if (cls.isInterface())
- cls = _meta.getInterfaceImpl();
PersistenceCapable inst = PCRegistry.newInstance(cls, this, _oid, true);
if (inst == null) {
@@ -814,7 +812,8 @@ public class StateManagerImpl
SaveFieldManager saved = getSaveFieldManager();
if (saved == null)
- throw new InternalException(_loc.get("no-saved-fields"));
+ throw new InternalException(_loc.get("no-saved-fields",
+ getMetaData().getDescribedType().getName()));
FieldMetaData[] fmds = getMetaData().getFields();
for (int i = 0; i < fmds.length; i++) {
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 22396bde1..762eee919 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
@@ -180,6 +180,8 @@ public abstract class AbstractMetaDataDefaults
*/
private boolean populateFromPCRegistry(ClassMetaData meta) {
Class cls = meta.getDescribedType();
+ if (!PCRegistry.isRegistered(cls))
+ return false;
try {
String[] fieldNames = PCRegistry.getFieldNames(cls);
Class[] fieldTypes = PCRegistry.getFieldTypes(cls);
@@ -198,9 +200,6 @@ public abstract class AbstractMetaDataDefaults
populate(fmd);
}
return true;
- } catch (IllegalStateException iae) {
- // thrown by registry when no metadata available
- return false;
} catch (OpenJPAException ke) {
throw ke;
} catch (Exception e) {
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 b746af8c0..43c097b8d 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
@@ -732,6 +732,11 @@ public class ClassMetaData
if (!_type.isInterface())
throw new MetaDataException(_loc.get("not-interface", _type));
_interface = managedInterface ? Boolean.TRUE : Boolean.FALSE;
+
+ // managed interfaces always do proper interception; OpenJPA generates
+ // the implementations.
+ if (isManagedInterface())
+ setIntercepting(true);
}
/**
@@ -2235,6 +2240,7 @@ public class ClassMetaData
_extent = (meta.getRequiresExtent()) ? Boolean.TRUE : Boolean.FALSE;
_embedded = (meta.isEmbeddedOnly()) ? Boolean.TRUE : Boolean.FALSE;
_interface = (meta.isManagedInterface()) ? Boolean.TRUE : Boolean.FALSE;
+ setIntercepting(meta.isIntercepting());
_impl = meta.getInterfaceImpl();
_identity = meta.getIdentityType();
_idStrategy = meta.getIdentityStrategy();
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
index e67ef031b..5f6fdd2b0 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java
@@ -114,14 +114,13 @@ class InterfaceImplGenerator {
// copy the BCClass into the enhancer project.
bc = _enhProject.loadClass(new ByteArrayInputStream(bc.toByteArray()),
loader);
- PCEnhancer enhancer = new PCEnhancer(_repos.getConfiguration(), bc,
- meta);
+ PCEnhancer enhancer = new PCEnhancer(_repos, bc, meta);
int result = enhancer.run();
if (result != PCEnhancer.ENHANCE_PC)
throw new InternalException(_loc.get("interface-badenhance",
iface)).setFatal(true);
- try{
+ try {
// load the class for real.
impl = Class.forName(bc.getName(), true, enhLoader);
} catch (Throwable t) {
@@ -229,4 +228,18 @@ class InterfaceImplGenerator {
} catch (PrivilegedActionException pae) {}
return true;
}
+
+ boolean isImplType(Class cls) {
+ return (cls.getName().endsWith(POSTFIX)
+ && cls.getName().indexOf('$') != -1);
+ }
+
+ public Class toManagedInterface(Class cls) {
+ Class[] ifaces = cls.getInterfaces();
+ for (int i = 0; i < ifaces.length; i++) {
+ if (_impls.get(ifaces[i]) == cls)
+ return ifaces[i];
+ }
+ throw new IllegalArgumentException(cls.getName());
+ }
}
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 80938fb64..9cd2f0e2d 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
@@ -287,6 +287,11 @@ public class MetaDataRepository
DynamicPersistenceCapable.class.isAssignableFrom(cls))
cls = cls.getSuperclass();
+ // if cls is a generated interface, use the user interface
+ // to locate metadata
+ if (cls != null && _implGen.isImplType(cls))
+ cls = _implGen.toManagedInterface(cls);
+
ClassMetaData meta = getMetaDataInternal(cls, envLoader);
if (meta == null && mustExist) {
if (cls != null &&
@@ -952,7 +957,6 @@ public class MetaDataRepository
throw new MetaDataException(_loc.get("not-managed-interface",
meta, impl));
_ifaces.put(meta.getDescribedType(), impl);
- _metas.put(impl, meta);
addDeclaredInterfaceImpl(meta, meta.getDescribedType());
ClassMetaData sup = meta.getPCSuperclassMetaData();
while (sup != null) {
@@ -964,9 +968,7 @@ public class MetaDataRepository
}
}
- synchronized InterfaceImplGenerator getImplGenerator() {
- if (_implGen == null)
- _implGen = new InterfaceImplGenerator(this);
+ InterfaceImplGenerator getImplGenerator() {
return _implGen;
}
@@ -1278,6 +1280,11 @@ public class MetaDataRepository
cls = classForName((String) itr.next(), clsLoader);
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);
}
return classes;
}
@@ -1548,6 +1555,8 @@ public class MetaDataRepository
public void endConfiguration() {
initializeMetaDataFactory();
+ if (_implGen == null)
+ _implGen = new InterfaceImplGenerator(this);
}
private void initializeMetaDataFactory() {
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java
index b0314014d..de67d40ae 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ApplicationIds.java
@@ -301,9 +301,7 @@ public class ApplicationIds {
// oid instance
if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())
&& !hasPCPrimaryKeyFields(meta)) {
- Class type = meta.getInterfaceImpl();
- if (type == null)
- type = meta.getDescribedType();
+ Class type = meta.getDescribedType();
PersistenceCapable pc = PCRegistry.newInstance(type, null, oid,
false);
Object copy = pc.pcNewObjectIdInstance();
diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
index 1cc80eecb..55970dfc0 100644
--- a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
+++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
@@ -388,11 +388,11 @@ multi-threaded-access: Multiple concurrent threads attempted to access a \
single broker. By default brokers are not thread safe; if you require \
and/or intend a broker to be accessed by more than one thread, set the \
openjpa.Multithreaded property to true to override the default behavior.
-no-saved-fields: No state snapshot is available for "{0}", but this instance \
- uses state-comparison for dirty detection.
+no-saved-fields: No state snapshot is available for instance of type "{0}", \
+ but this instance uses state-comparison for dirty detection.
cant-serialize-flushed-broker: Serialization not allowed once a broker has \
been flushed.
cant-serialize-pessimistic-broker: Serialization not allowed for brokers with \
an active datastore (pessimistic) transaction.
cant-serialize-connected-broker: Serialization not allowed for brokers with \
- an active connection to the database.
\ No newline at end of file
+ an active connection to the database.
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java
index bcf01c97a..d95680c4d 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/EmbedValue.java
@@ -40,6 +40,9 @@ public class EmbedValue {
@JoinColumn(name = "EMB_REL")
protected EmbedOwner owner;
+ @Transient
+ private int transientField;
+
public void setBasic(String basic) {
this.basic = basic;
}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java
index b3fff4dc8..8e664029d 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestEJBEmbedded.java
@@ -29,6 +29,9 @@ import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedClobFieldStrategy;
import org.apache.openjpa.jdbc.meta.strats.StringFieldStrategy;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
/**
* Test for embedded
@@ -78,6 +81,15 @@ public class TestEJBEmbedded extends SingleEMFTestCase {
em.close();
}
+ public void testEmbeddedMetaData() {
+ ClassMetaData ownerMeta =
+ JPAFacadeHelper.getMetaData(emf, EmbedOwner.class);
+ FieldMetaData fmd = ownerMeta.getField("embed");
+ ClassMetaData embeddedMeta = fmd.getDefiningMetaData();
+ assertNotNull(embeddedMeta);
+ assertNull(embeddedMeta.getField("transientField"));
+ }
+
public void testNull() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedIface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedIface.java
new file mode 100644
index 000000000..b41b7f926
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedIface.java
@@ -0,0 +1,47 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+
+import java.util.*;
+
+import javax.persistence.Entity;
+import javax.persistence.Embedded;
+import javax.persistence.OneToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.CascadeType;
+
+import org.apache.openjpa.persistence.PersistentCollection;
+import org.apache.openjpa.persistence.ManagedInterface;
+import org.apache.openjpa.persistence.query.SimpleEntity;
+
+@ManagedInterface
+@Entity
+public interface ManagedIface extends ManagedInterfaceSup {
+ public int getIntField();
+ public void setIntField(int i);
+
+ @Embedded
+ public ManagedInterfaceEmbed getEmbed();
+ public void setEmbed(ManagedInterfaceEmbed embed);
+
+ @OneToOne(cascade=CascadeType.PERSIST)
+ public ManagedIface getSelf();
+ public void setSelf(ManagedIface iface);
+
+ @PersistentCollection
+ public Set getSetInteger();
+ public void setSetInteger(Set collection);
+
+ @OneToMany(cascade=CascadeType.PERSIST)
+ public Set getSetPC();
+ public void setSetPC(Set collection);
+
+ @OneToMany(cascade=CascadeType.PERSIST)
+ public Set getSetI();
+ public void setSetI(Set collection);
+
+ @OneToOne(cascade=CascadeType.PERSIST)
+ public SimpleEntity getPC();
+ public void setPC(SimpleEntity pc);
+
+ public void unimplemented();
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceEmbed.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceEmbed.java
new file mode 100644
index 000000000..a35bcecb0
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceEmbed.java
@@ -0,0 +1,13 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import javax.persistence.Embeddable;
+import javax.persistence.Basic;
+
+import org.apache.openjpa.persistence.ManagedInterface;
+
+@ManagedInterface
+@Embeddable
+public interface ManagedInterfaceEmbed {
+ public int getEmbedIntField();
+ public void setEmbedIntField(int i);
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceOwner.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceOwner.java
new file mode 100644
index 000000000..8b4b27031
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceOwner.java
@@ -0,0 +1,46 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import javax.persistence.OneToOne;
+import javax.persistence.Id;
+import javax.persistence.Embedded;
+import javax.persistence.Entity;
+import javax.persistence.CascadeType;
+
+@Entity
+public class ManagedInterfaceOwner {
+
+ @Id
+ private int id;
+
+ private int intField;
+
+ @OneToOne(cascade=CascadeType.PERSIST)
+ private ManagedInterfaceSup iface;
+
+ @Embedded
+ private ManagedInterfaceEmbed embed;
+
+ public int getIntField() {
+ return intField;
+ }
+
+ public void setIntField(int i) {
+ intField = i;
+ }
+
+ public ManagedInterfaceSup getIFace() {
+ return iface;
+ }
+
+ public void setIFace(ManagedInterfaceSup iface) {
+ this.iface = iface;
+ }
+
+ public ManagedInterfaceEmbed getEmbed() {
+ return embed;
+ }
+
+ public void setEmbed(ManagedInterfaceEmbed embed) {
+ this.embed = embed;
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceSup.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceSup.java
new file mode 100644
index 000000000..873746475
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/ManagedInterfaceSup.java
@@ -0,0 +1,18 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.apache.openjpa.persistence.ManagedInterface;
+
+@ManagedInterface
+@Entity
+public interface ManagedInterfaceSup {
+ @Id @GeneratedValue
+ public int getId();
+ public void setId(int id);
+
+ public int getIntFieldSup();
+ public void setIntFieldSup(int i);
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterface.java
new file mode 100644
index 000000000..8befa73c6
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterface.java
@@ -0,0 +1,20 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.apache.openjpa.persistence.ManagedInterface;
+
+@ManagedInterface
+@Entity
+public interface MixedInterface {
+
+ @Id
+ @GeneratedValue
+ public int getId();
+ public void setId(int id);
+
+ public int getIntField();
+ public void setIntField(int i);
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterfaceImpl.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterfaceImpl.java
new file mode 100644
index 000000000..1380f21ac
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/MixedInterfaceImpl.java
@@ -0,0 +1,30 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+@Entity
+public class MixedInterfaceImpl implements MixedInterface {
+ @Id
+ @GeneratedValue
+ private int id;
+
+ private int intField;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getIntField() {
+ return intField;
+ }
+
+ public void setIntField(int i) {
+ intField = i;
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterface.java
new file mode 100644
index 000000000..bfc1eef73
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterface.java
@@ -0,0 +1,8 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+
+public interface NonMappedInterface {
+ public int getIntField();
+
+ public void setIntField(int i);
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterfaceImpl.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterfaceImpl.java
new file mode 100644
index 000000000..e8bd3e33f
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/NonMappedInterfaceImpl.java
@@ -0,0 +1,17 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import javax.persistence.Entity;
+
+@Entity
+public class NonMappedInterfaceImpl
+ implements NonMappedInterface {
+ private int mismatch;
+
+ public int getIntField() {
+ return mismatch;
+ }
+
+ public void setIntField(int i) {
+ mismatch = i;
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/SimpleManagedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/SimpleManagedInterface.java
new file mode 100644
index 000000000..2a4af537f
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/SimpleManagedInterface.java
@@ -0,0 +1,36 @@
+/*
+ * 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.persistence.managedinterface;
+
+import javax.persistence.Id;
+import javax.persistence.Entity;
+
+import org.apache.openjpa.persistence.ManagedInterface;
+
+@ManagedInterface
+@Entity
+public interface SimpleManagedInterface {
+
+ @Id
+ public int getId();
+ public void setId(int id);
+
+ public String getString();
+ public void setString(String s);
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestManagedInterfaces.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestManagedInterfaces.java
new file mode 100644
index 000000000..6a37616c6
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestManagedInterfaces.java
@@ -0,0 +1,449 @@
+package org.apache.openjpa.persistence.managedinterface;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import javax.persistence.Query;
+import javax.persistence.EntityNotFoundException;
+
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.Extent;
+import org.apache.openjpa.persistence.query.SimpleEntity;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.persistence.PersistenceException;
+
+public class TestManagedInterfaces extends SingleEMFTestCase {
+
+ @Override
+ public void setUp() {
+ super.setUp(SimpleEntity.class, ManagedInterfaceEmbed.class,
+ ManagedInterfaceSup.class, ManagedIface.class,
+ ManagedInterfaceOwner.class, MixedInterface.class,
+ MixedInterfaceImpl.class, NonMappedInterfaceImpl.class,
+ CLEAR_TABLES);
+ }
+
+ public void testEmbeddedMetaData() {
+ emf.createEntityManager().close();
+ ClassMetaData ownerMeta = JPAFacadeHelper.getMetaData(emf,
+ ManagedIface.class);
+ ClassMetaData embeddedMeta = ownerMeta.getField("embed")
+ .getDefiningMetaData();
+ assertTrue(embeddedMeta.isManagedInterface());
+ assertTrue(embeddedMeta.isIntercepting());
+
+ ClassMetaData embeddableMeta = JPAFacadeHelper.getMetaData(emf,
+ ManagedInterfaceEmbed.class);
+ assertTrue(embeddableMeta.isManagedInterface());
+ assertTrue(embeddableMeta.isIntercepting());
+ }
+
+ public void testManagedInterface() throws Exception {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ ManagedIface pc = em.createInstance(ManagedIface.class);
+ pc.setIntFieldSup(3);
+ pc.setIntField(4);
+ pc.setEmbed(em.createInstance(ManagedInterfaceEmbed.class));
+
+ pc.getEmbed().setEmbedIntField(5);
+ assertEquals(5, pc.getEmbed().getEmbedIntField());
+ em.persist(pc);
+ Object oid = em.getObjectId(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ assertEquals(3, pc.getIntFieldSup());
+ assertEquals(4, pc.getIntField());
+ assertEquals(5, pc.getEmbed().getEmbedIntField());
+ em.getTransaction().begin();
+ pc.setIntField(14);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager ();
+ em.getTransaction().begin();
+ Query query = em.createQuery("select o from ManagedIface o " +
+ "where o.intField = 14");
+ pc = (ManagedIface) query.getSingleResult();
+ assertEquals(14, pc.getIntField());
+ em.remove(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ try {
+ assertNull(em.find(ManagedIface.class, oid));
+ } catch (EntityNotFoundException onfe) {}
+
+ em.close();
+ }
+
+ public void testInterfaceOwner() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ ManagedInterfaceOwner pc = new ManagedInterfaceOwner();
+ pc.setIFace(em.createInstance(ManagedInterfaceSup.class));
+ pc.setEmbed(em.createInstance(ManagedInterfaceEmbed.class));
+ pc.getIFace().setIntFieldSup(3);
+ pc.getEmbed().setEmbedIntField(5);
+
+ em.getTransaction().begin();
+ em.persist(pc);
+ Object oid = em.getObjectId(pc);
+ em.getTransaction().commit();
+ pc = em.find(ManagedInterfaceOwner.class, oid);
+ assertEquals(3, pc.getIFace().getIntFieldSup());
+ assertEquals(5, pc.getEmbed().getEmbedIntField());
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedInterfaceOwner.class, oid);
+ assertEquals(3, pc.getIFace().getIntFieldSup());
+ assertEquals(5, pc.getEmbed().getEmbedIntField());
+ em.close();
+
+ em = emf.createEntityManager();
+ em.getTransaction().begin();
+ Query q = em.createQuery("select o from ManagedInterfaceOwner o " +
+ "where o.iface.intFieldSup = 3 and o.embed.embedIntField = 5");
+ pc = (ManagedInterfaceOwner) q.getSingleResult();
+ assertEquals(3, pc.getIFace().getIntFieldSup());
+ assertEquals(5, pc.getEmbed().getEmbedIntField());
+
+ pc.getIFace().setIntFieldSup(13);
+ pc.getEmbed().setEmbedIntField(15);
+ assertEquals(13, pc.getIFace().getIntFieldSup());
+ assertEquals(15, pc.getEmbed().getEmbedIntField());
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedInterfaceOwner.class, oid);
+ assertEquals(13, pc.getIFace().getIntFieldSup());
+ assertEquals(15, pc.getEmbed().getEmbedIntField());
+ em.close();
+ }
+
+ public void testCollection() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ ManagedIface pc = em.createInstance(ManagedIface.class);
+ Set set = new HashSet();
+ set.add(new Integer(3));
+ set.add(new Integer(4));
+ set.add(new Integer(5));
+ pc.setSetInteger(set);
+ em.persist(pc);
+ Object oid = em.getObjectId(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetInteger();
+ assertEquals(3, set.size());
+ assertTrue(set.contains(new Integer(3)));
+ assertTrue(set.contains(new Integer(4)));
+ assertTrue(set.contains(new Integer(5)));
+ em.getTransaction().begin();
+ set.remove(new Integer(4));
+ set.add(new Integer(14));
+ set.add(new Integer(15));
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetInteger();
+ assertEquals(4, set.size());
+ assertTrue(set.contains(new Integer(3)));
+ assertTrue(set.contains(new Integer(5)));
+ assertTrue(set.contains(new Integer(14)));
+ assertTrue(set.contains(new Integer(15)));
+ em.getTransaction().begin();
+ pc.setSetInteger(null);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetInteger();
+ assertTrue (set == null || set.size() == 0);
+ em.close();
+ }
+
+ public void testCollectionPC() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ ManagedIface pc = em.createInstance(ManagedIface.class);
+ Set set = new HashSet();
+ set.add(new SimpleEntity("a", "3"));
+ set.add(new SimpleEntity("b", "4"));
+ set.add(new SimpleEntity("c", "5"));
+ pc.setSetPC(set);
+ em.persist(pc);
+ Object oid = em.getObjectId(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetPC();
+ assertEquals(3, set.size());
+ Collection seen = new ArrayList();
+ SimpleEntity rel;
+ SimpleEntity toRem = null;
+ for (Iterator it = set.iterator(); it.hasNext();) {
+ rel = (SimpleEntity) it.next();
+ seen.add(rel.getName());
+ if (rel.getValue().equals("4"))
+ toRem = rel;
+ }
+ assertEquals(3, seen.size());
+ assertTrue(seen.contains("a"));
+ assertTrue(seen.contains("b"));
+ assertTrue(seen.contains("c"));
+ em.getTransaction().begin();
+ assertNotNull(toRem);
+ set.remove(toRem);
+ set.add(new SimpleEntity("x", "14"));
+ set.add(new SimpleEntity("y", "15"));
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetPC();
+ assertEquals(4, set.size());
+ seen.clear();
+ for (Iterator it = set.iterator(); it.hasNext();) {
+ rel = (SimpleEntity) it.next();
+ seen.add(rel.getName());
+ }
+ assertEquals(4, seen.size());
+ assertTrue(seen.contains("a"));
+ assertTrue(seen.contains("c"));
+ assertTrue(seen.contains("x"));
+ assertTrue(seen.contains("y"));
+ em.getTransaction().begin();
+ pc.setSetPC(null);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetPC();
+ assertTrue (set == null || set.size() == 0);
+ em.close();
+ }
+
+ public void testCollectionInterfaces() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ ManagedIface pc = em.createInstance(ManagedIface.class);
+ Set set = new HashSet();
+ set.add(createInstance(em, 3));
+ set.add(createInstance(em, 4));
+ set.add(createInstance(em, 5));
+ pc.setSetI(set);
+ em.persist(pc);
+ Object oid = em.getObjectId(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetI();
+ assertEquals(3, set.size());
+ Collection seen = new ArrayList();
+ ManagedIface rel = null;
+ ManagedIface toRem = null;
+ for (Iterator it = set.iterator(); it.hasNext();) {
+ rel = (ManagedIface) it.next();
+ seen.add(new Integer(rel.getIntField()));
+ if (rel.getIntField() == 4)
+ toRem = rel;
+ }
+ assertEquals(3, seen.size());
+ assertTrue(seen.contains(new Integer(3)));
+ assertTrue(seen.contains(new Integer(4)));
+ assertTrue(seen.contains(new Integer(5)));
+ em.getTransaction().begin();
+ assertNotNull(toRem);
+ set.remove(toRem);
+ set.add(createInstance(em, 14));
+ set.add(createInstance(em, 15));
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetI();
+ assertEquals(4, set.size());
+ seen.clear();
+ for (Iterator it = set.iterator(); it.hasNext();) {
+ rel = (ManagedIface) it.next();
+ seen.add(new Integer(rel.getIntField()));
+ }
+ assertEquals(4, seen.size());
+ assertTrue(seen.contains(new Integer(3)));
+ assertTrue(seen.contains(new Integer(5)));
+ assertTrue(seen.contains(new Integer(14)));
+ assertTrue(seen.contains(new Integer(15)));
+ em.getTransaction().begin();
+ pc.setSetPC(null);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ set = pc.getSetPC();
+ assertTrue (set == null || set.size() == 0);
+ em.close();
+ }
+
+ public void testMixedQuery() {
+ createMixed();
+
+ OpenJPAEntityManager em = emf.createEntityManager();
+ try {
+ Query q = em.createQuery("select o from MixedInterface o " +
+ "where o.intField = 4");
+ Collection c = q.getResultList();
+ Set seen = new HashSet();
+ assertEquals(2, c.size());
+ MixedInterface pc;
+ for (Iterator it = c.iterator(); it.hasNext();) {
+ pc = (MixedInterface) it.next();
+ assertEquals(4, pc.getIntField());
+ seen.add(pc.getClass());
+ }
+ assertEquals(2, seen.size());
+
+ fail("OPENJPA-481");
+ } catch (PersistenceException e) {
+ // expected
+ } finally {
+ em.close();
+ }
+ }
+
+ public void testQueryForMixedInterfaceImpls() {
+ createMixed();
+
+ OpenJPAEntityManager em = emf.createEntityManager();
+ Query q = em.createQuery("select o from MixedInterfaceImpl o " +
+ "where o.intField = 4");
+ MixedInterface pc = (MixedInterface) q.getSingleResult();
+ assertEquals(4, pc.getIntField());
+ assertTrue(pc instanceof MixedInterfaceImpl);
+ em.close();
+ }
+
+ public void testMixedExtent() {
+ createMixed();
+
+ OpenJPAEntityManager em = emf.createEntityManager();
+ Extent e = em.createExtent(MixedInterface.class, true);
+ Set seen = new HashSet();
+ int size = 0;
+ for (Iterator it = e.iterator(); it.hasNext();) {
+ seen.add(it.next().getClass());
+ size++;
+ }
+ assertEquals(3, size);
+ assertEquals(2, seen.size());
+
+ e = em.createExtent(MixedInterface.class, false);
+ seen = new HashSet();
+ size = 0;
+ for (Iterator it = e.iterator(); it.hasNext();) {
+ seen.add(it.next().getClass());
+ size++;
+ }
+ assertEquals(1, size);
+ assertNotEquals(MixedInterfaceImpl.class, seen.iterator().next());
+ em.close();
+ }
+
+ private void createMixed() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ MixedInterface pc = em.createInstance(MixedInterface.class);
+ pc.setIntField(4);
+ em.persist(pc);
+ pc = new MixedInterfaceImpl();
+ pc.setIntField(4);
+ em.persist(pc);
+ pc = new MixedInterfaceImpl();
+ pc.setIntField(8);
+ em.persist(pc);
+ em.getTransaction().commit();
+ em.close();
+ }
+
+ public void testUnimplementedThrowsException() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ ManagedIface pc = createInstance(em, 1);
+ try {
+ pc.unimplemented();
+ fail("Exception expected.");
+ } catch (UnsupportedOperationException uoe) {} // good
+ em.close();
+ }
+
+ public void testNonMappedCreateInstanceException() {
+ // OpenJPA's support of non-mapped interfaces differs from JDO support;
+ // there is no special query or relation support for non-mapped
+ // interfaces in OpenJPA at this time.
+ OpenJPAEntityManager em = null;
+ try {
+ em = emf.createEntityManager();
+ em.createInstance(NonMappedInterface.class);
+ fail("IllegalArgumentException expected");
+ } catch (IllegalArgumentException e) {} // good
+ if (em != null)
+ em.close();
+ }
+
+ public void testDetach() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ ManagedIface pc = createInstance(em, 4);
+ em.persist(pc);
+ Object oid = em.getObjectId(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ ManagedIface pcx = em.find(ManagedIface.class, oid);
+ pc = em.detach(pcx);
+ em.close();
+
+ assertTrue(em.isDetached(pc));
+ pc.setIntField(7);
+
+ em = emf.createEntityManager();
+ em.getTransaction().begin();
+ em.merge(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(ManagedIface.class, oid);
+ assertEquals(7, pc.getIntField());
+ em.close();
+ }
+
+ private ManagedIface createInstance(OpenJPAEntityManager em, int i) {
+ ManagedIface pc = em.createInstance(ManagedIface.class);
+ pc.setIntField(i);
+ return pc;
+ }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestSimpleManagedInterface.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestSimpleManagedInterface.java
new file mode 100644
index 000000000..da567bb1f
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/managedinterface/TestSimpleManagedInterface.java
@@ -0,0 +1,92 @@
+/*
+ * 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.persistence.managedinterface;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.query.SimpleEntity;
+import org.apache.openjpa.kernel.AbstractBrokerFactory;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.MetaDataRepository;
+
+public class TestSimpleManagedInterface
+ extends SingleEMFTestCase {
+
+ public void setUp() {
+ setUp(SimpleManagedInterface.class, SimpleEntity.class, CLEAR_TABLES);
+ }
+
+ public void testMetaDataRepository() {
+ AbstractBrokerFactory bf =
+ (AbstractBrokerFactory) JPAFacadeHelper.toBrokerFactory(emf);
+ bf.makeReadOnly();
+ MetaDataRepository repos = bf.getConfiguration()
+ .getMetaDataRepositoryInstance();
+ ClassMetaData meta = repos.getMetaData(SimpleManagedInterface.class,
+ null, false);
+ assertNotNull(meta);
+ assertTrue(meta.isManagedInterface());
+ assertEquals(SimpleManagedInterface.class, meta.getDescribedType());
+ }
+
+ public void testInterfaceImplGeneration() {
+ ((AbstractBrokerFactory) JPAFacadeHelper.toBrokerFactory(emf))
+ .makeReadOnly();
+ // load metadata to trigger instance creation
+ ClassMetaData meta = JPAFacadeHelper.getMetaData(emf,
+ SimpleManagedInterface.class);
+ assertEquals(SimpleManagedInterface.class, meta.getDescribedType());
+ }
+
+ public void testBasicOperations() {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ SimpleManagedInterface pc =
+ em.createInstance(SimpleManagedInterface.class);
+ pc.setId(17);
+ pc.setString("hello!");
+ em.getTransaction().begin();
+ em.persist(pc);
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ pc = em.find(SimpleManagedInterface.class, 17);
+ assertNotNull(pc);
+ em.getTransaction().begin();
+ pc.setString("updated");
+ em.getTransaction().commit();
+ em.close();
+
+ em = emf.createEntityManager();
+ em.getTransaction().begin();
+ em.remove(em.getReference(SimpleManagedInterface.class, 17));
+ em.getTransaction().commit();
+ em.close();
+ }
+
+ public void testJPQL() {
+ EntityManager em = emf.createEntityManager();
+ assertEquals(0, em.createQuery("select o from SimpleManagedInterface o")
+ .getResultList().size());
+ em.close();
+ }
+}
\ No newline at end of file
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
index e188fc2f6..b12c818c8 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java
@@ -172,6 +172,7 @@ public class AnnotationPersistenceMetaDataParser
_tags.put(KeyType.class, KEY_TYPE);
_tags.put(LoadFetchGroup.class, LOAD_FETCH_GROUP);
_tags.put(LRS.class, LRS);
+ _tags.put(ManagedInterface.class, MANAGED_INTERFACE);
_tags.put(ReadOnly.class, READ_ONLY);
_tags.put(Type.class, TYPE);
}
@@ -572,6 +573,10 @@ public class AnnotationPersistenceMetaDataParser
if (isMetaDataMode())
fgs = ((FetchGroups) anno).value();
break;
+ case MANAGED_INTERFACE:
+ if (isMetaDataMode())
+ parseManagedInterface(meta, (ManagedInterface) anno);
+ break;
default:
throw new UnsupportedException(_loc.get("unsupported", _cls,
anno.toString()));
@@ -595,7 +600,8 @@ public class AnnotationPersistenceMetaDataParser
// scan possibly non-PC hierarchy for callbacks.
// redundant for PC superclass but we don't know that yet
// so let LifecycleMetaData determine that
- if (!Object.class.equals(_cls.getSuperclass())) {
+ if (_cls.getSuperclass() != null &&
+ !Object.class.equals(_cls.getSuperclass())) {
recordCallbacks(meta, parseCallbackMethods(_cls.getSuperclass(),
null, true, false, getRepository()), null, true);
}
@@ -750,6 +756,11 @@ public class AnnotationPersistenceMetaDataParser
meta.setDataCacheName(null);
}
+ private void parseManagedInterface(ClassMetaData meta,
+ ManagedInterface iface) {
+ meta.setManagedInterface(true);
+ }
+
/**
* Parse @DetachedState. The annotation may be null.
*/
@@ -798,6 +809,10 @@ public class AnnotationPersistenceMetaDataParser
public static Collection[] parseCallbackMethods
(Class cls, Collection[] callbacks, boolean sups,
boolean listener, MetaDataRepository repos) {
+
+ if (cls == null)
+ throw new IllegalArgumentException("cls cannot be null");
+
// first sort / filter based on inheritance
Set methods = new TreeSet(MethodComparator.
getInstance());
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/ManagedInterface.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/ManagedInterface.java
new file mode 100644
index 000000000..999b8c095
--- /dev/null
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/ManagedInterface.java
@@ -0,0 +1,41 @@
+/*
+ * 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.persistence;
+
+import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * The annotated interface should be treated as a managed interface by OpenJPA.
+ * New instances of this type can be created by invoking
+ * {@link OpenJPAEntityManager#createInstance(Class)}.
+ * Interfaces with this annotation should also be annotated with one of the JPA
+ * entity annotations ({@link javax.persistence.Entity @Entity},
+ * {@link javax.persistence.MappedSuperclass @MappedSuperclass},
+ * or {@link javax.persistence.Embeddable @Embeddable}).
+ *
+ * @since 1.1.0
+ * @published
+ */
+@Target({ TYPE })
+@Retention(RUNTIME)
+public @interface ManagedInterface {
+}
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java
index 8c4199e3e..f10e07d03 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/MetaDataTag.java
@@ -77,6 +77,7 @@ public enum MetaDataTag {
KEY_TYPE,
LOAD_FETCH_GROUP,
LRS,
+ MANAGED_INTERFACE,
READ_ONLY,
TYPE,
}
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java
index d66c7b1c9..aba1c3ead 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManager.java
@@ -780,8 +780,11 @@ public interface OpenJPAEntityManager
* create a subclass of the type that does implement
* {@link org.apache.openjpa.enhance.PersistenceCapable}, and will attempt
* to redefine the methods in cls
to enable persistent
- * attribute tracking. Otherwise, this will return an instance of the
- * specified class.
+ * attribute tracking. Otherwise, if cls
is a managed type,
+ * this will return an instance of the specified class.
+ *
+ * @throws IllegalArgumentException if cls
is not a managed
+ * type or interface.
*/
public T createInstance(Class cls);
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 83e00b7e1..124055b96 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
@@ -261,7 +261,8 @@ public class PersistenceMetaDataDefaults
J2DoPriv5Helper.getDeclaredFieldsAction(cls))))
access |= ClassMetaData.ACCESS_FIELD;
if (usesAccess((Method[]) AccessController.doPrivileged(
- J2DoPriv5Helper.getDeclaredMethodsAction(cls))))
+ J2DoPriv5Helper.getDeclaredMethodsAction(cls)))
+ || cls.isInterface()) // OpenJPA managed ifaces must use prop access
access |= ClassMetaData.ACCESS_PROPERTY;
return (access == 0) ? getAccessType(cls.getSuperclass()) : access;
}
diff --git a/openjpa-project/src/doc/manual/ref_guide_pc.xml b/openjpa-project/src/doc/manual/ref_guide_pc.xml
index 1b3ebea8b..f83dcb79f 100644
--- a/openjpa-project/src/doc/manual/ref_guide_pc.xml
+++ b/openjpa-project/src/doc/manual/ref_guide_pc.xml
@@ -483,6 +483,43 @@ a javaagent or in a Java 6 environment.
+
+ Managed Interfaces
+
+ interfaces
+ managed
+
+
+OpenJPA's managed interface feature allows you to define your object model
+entirely in terms of interfaces, instead of concrete classes. To use this
+feature, you must annotate your managed interfaces with the
+ManagedInterface annotation, and use the
+OpenJPAEntityManager.createInstance(Class) method to
+create new records. Note that createInstance() returns
+unmanaged instances; you must pass them to
+EntityManager.persist() to store them in the database.
+
+
+@ManagedInterface
+public interface PersonIface {
+ @Id @GeneratedValue
+ int getId();
+ void setId(int id);
+
+ // implicitly persistent per JPA property rules
+ String getName();
+ void setName(String name);
+}
+
+
+OpenJPAEntityManager em = ...;
+PersonIface person = em.createInstance(PersonIface.class);
+person.setName("Homer Simpson");
+em.getTransaction().begin();
+em.persist(person);
+em.getTransaction().commit();
+
+