blob: d6d82947a970b04c47d1ab955b3317ab64911162 [file] [log] [blame]
/*
* 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.enhance;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.event.AbstractLifecycleListener;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.meta.AccessCode;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.persistence.JPAFacadeHelper;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
import org.apache.openjpa.util.ImplHelper;
public abstract class AbstractUnenhancedClassTest
extends SingleEMFTestCase {
Log _log;
// ##### To do:
// - clearing in pnew property-access without redefinition
// - figure out how to auto-test the redefinition code, either in Java 5
// or in Java 6
// - run CTS in the following combinations:
// * Java 6
// * Java 5 with javaagent
// * Java 5 without javaagent
@Override
public void setUp() {
setUp(getUnenhancedClass(), getUnenhancedSubclass(), CLEAR_TABLES);
// trigger class redefinition
emf.createEntityManager().close();
_log = emf.getConfiguration().getLog(OpenJPAConfiguration.LOG_ENHANCE);
}
protected abstract Class<? extends UnenhancedType> getUnenhancedClass();
protected abstract UnenhancedType newUnenhancedInstance();
protected abstract Class<? extends UnenhancedSubtype>
getUnenhancedSubclass();
protected abstract UnenhancedSubtype newUnenhancedSubclassInstance();
private UnenhancedType newInstance(boolean sub) {
return sub ? newUnenhancedSubclassInstance()
: newUnenhancedInstance();
}
public void testMetaData() {
ClassMetaData meta = JPAFacadeHelper.getMetaData(emf,
getUnenhancedClass());
assertEquals(ClassRedefiner.canRedefineClasses(_log), meta
.isIntercepting());
}
public void testImplHelperCalls() {
assertTrue(ImplHelper.isManagedType(null, getUnenhancedClass()));
UnenhancedType un = newUnenhancedInstance();
assertFalse(un instanceof PersistenceCapable);
PersistenceCapable pc = ImplHelper.toPersistenceCapable(un,
emf.getConfiguration());
assertNotNull(pc);
assertTrue(ImplHelper.isManageable(un));
}
public void testBasicPersistenceCapableBehavior() {
UnenhancedType un = newUnenhancedInstance();
un.setStringField("bar");
PersistenceCapable pc = ImplHelper.toPersistenceCapable(un,
emf.getConfiguration());
assertFalse(pc.pcIsDeleted());
assertFalse(pc.pcIsDirty());
assertFalse(pc.pcIsNew());
assertFalse(pc.pcIsPersistent());
assertFalse(pc.pcIsTransactional());
}
public void testPCRegistry() {
assertTrue(PCRegistry.isRegistered(getUnenhancedClass()));
PersistenceCapable pc = PCRegistry.newInstance(
getUnenhancedClass(), null, false);
assertNotNull(pc);
assertEquals(pc.getClass(), PCRegistry.getPCType(getUnenhancedClass()));
}
public void testClearingOnSubtypeInstance() {
// the boolean at the end of newInstance will cause clear to be invoked
UnenhancedType un = (UnenhancedType)
PCRegistry.newInstance(getUnenhancedClass(), null, true);
assertEquals(null, un.getStringField());
}
public void testGetObjectIdOnOpenJPAType() {
getObjectIdHelper(true, false);
}
public void testGetObjectIdOnOpenJPATypeSubclass() {
getObjectIdHelper(false, false);
}
public void testGetObjectIdOnUserDefined() {
getObjectIdHelper(true, true);
}
public void testGetObjectIdOnUserDefinedSubclass() {
getObjectIdHelper(false, true);
}
private void getObjectIdHelper(boolean sub, boolean userDefined) {
OpenJPAEntityManager em = emf.createEntityManager();
em.getTransaction().begin();
UnenhancedType un = newInstance(sub);
em.persist(un);
em.getTransaction().commit();
if (!userDefined) {
em.close();
em = emf.createEntityManager();
un = em.find(getUnenhancedClass(), un.getId());
}
assertNotNull(em.getObjectId(un));
}
public void testOperationsOnUserDefined() {
opsHelper(false, true);
}
public void testSubclassOperationsOnUserDefined() {
opsHelper(true, true);
}
public void testOperationsOnOpenJPADefined() {
opsHelper(false, false);
}
public void testSubclassOperationsOnOpenJPADefined() {
opsHelper(true, false);
}
private void opsHelper(boolean sub, boolean userDefined) {
OpenJPAEntityManager em = null;
try {
UnenhancedType un = newInstance(sub);
em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(un);
un.setStringField("bar");
assertEquals("bar", un.getStringField());
assertPersistenceContext(em, un, true, true, sub);
em.flush();
assertPersistenceContext(em, un, true, true, sub);
assertTrue(un.getId() != 0);
UnenhancedType un2 = em.find(getUnenhancedClass(), un.getId());
assertSame(un, un2);
em.getTransaction().commit();
assertPersistenceContext(em, un, false, false, sub);
un2 = em.find(getUnenhancedClass(), un.getId());
assertSame(un, un2);
if (!userDefined) {
em.close();
em = emf.createEntityManager();
}
un = em.find(getUnenhancedClass(), un.getId());
assertNotNull(un);
if (!userDefined)
assertTrue(un instanceof PersistenceCapable);
assertEquals("bar", un.getStringField());
em.getTransaction().begin();
un.setStringField("baz");
assertPersistenceContext(em, un, true, true, sub);
assertEquals("baz", un.getStringField());
if (sub)
((UnenhancedSubtype) un).setIntField(17);
assertTrue(em.isDirty(un));
em.getTransaction().commit();
// make sure that the values are still up-to-date after
// the commit happens
assertEquals("baz", un.getStringField());
em.close();
em = emf.createEntityManager();
un = em.find(getUnenhancedClass(), un.getId());
assertNotNull(un);
assertTrue(un instanceof PersistenceCapable);
assertEquals("baz", un.getStringField());
if (sub)
assertEquals(17, ((UnenhancedSubtype) un).getIntField());
em.close();
} finally {
if (em != null && em.getTransaction().isActive())
em.getTransaction().rollback();
if (em != null && em.isOpen())
em.close();
}
}
private void assertPersistenceContext(OpenJPAEntityManager em,
UnenhancedType un, boolean transactional, boolean dirty, boolean sub) {
assertEquals(transactional, em.getTransactionalObjects().contains(un));
assertEquals(dirty, em.getDirtyObjects().contains(un));
if (dirty) {
Class cls = sub ? getUnenhancedSubclass() : getUnenhancedClass();
assertTrue(em.getUpdatedClasses().contains(cls)
|| em.getPersistedClasses().contains(cls));
}
}
public void testRelations() {
OpenJPAEntityManager em = emf.createEntityManager();
em.getTransaction().begin();
UnenhancedSubtype un = newUnenhancedSubclassInstance();
em.persist(un);
un.setStringField("aoeu");
UnenhancedSubtype related = newUnenhancedSubclassInstance();
un.setRelated(related);
related.setStringField("snth");
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
un = (UnenhancedSubtype) em.find(getUnenhancedClass(), un.getId());
assertEquals("aoeu", un.getStringField());
assertNotNull(un.getRelated());
assertEquals("snth", un.getRelated().getStringField());
em.close();
}
public void testEnhancer() throws IOException {
List<Class<?>> subs = ManagedClassSubclasser.prepareUnenhancedClasses(
emf.getConfiguration(), Collections.singleton(getUnenhancedClass()), null);
Class sub = subs.get(0);
assertNotNull(sub);
assertEquals("org.apache.openjpa.enhance."
+ getUnenhancedClass().getName().replace('.', '$') + "$pcsubclass",
sub.getName());
assertTrue(PersistenceCapable.class.isAssignableFrom(sub));
assertTrue(getUnenhancedClass().isAssignableFrom(sub));
}
public void testPCSubclassName() {
assertEquals("org.apache.openjpa.enhance."
+ getUnenhancedClass().getName().replace('.', '$') + "$pcsubclass",
PCEnhancer.toPCSubclassName(getUnenhancedClass()));
}
public void testEvictionInUserCreatedInstance()
throws NoSuchFieldException, IllegalAccessException {
evictionHelper(true);
}
public void testEvictionInOpenJPACreatedInstance()
throws NoSuchFieldException, IllegalAccessException {
evictionHelper(false);
}
private void evictionHelper(boolean userDefined)
throws NoSuchFieldException, IllegalAccessException {
OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newUnenhancedInstance();
em.getTransaction().begin();
em.persist(un);
em.getTransaction().commit();
if (!userDefined) {
em.close();
em = emf.createEntityManager();
un = em.find(getUnenhancedClass(), un.getId());
assertTrue(getUnenhancedClass() != un.getClass());
}
em.evict(un);
OpenJPAStateManager sm = (OpenJPAStateManager)
ImplHelper.toPersistenceCapable(un, null).pcGetStateManager();
// we only expect lazy loading to work when we can redefine classes
// or when accessing a property-access record that OpenJPA created.
if (ClassRedefiner.canRedefineClasses(_log)
|| (!userDefined
&& AccessCode.isProperty(sm.getMetaData().getAccessType()))) {
assertFalse(sm.getLoaded()
.get(sm.getMetaData().getField("stringField").getIndex()));
// make sure that the value was cleared...
Field field = getUnenhancedClass().getDeclaredField(
isFieldAccessTest() ? "stringField" : "sf");
field.setAccessible(true);
assertEquals(null, field.get(un));
// ... and that it gets reloaded properly
assertEquals("foo", un.getStringField());
assertTrue(sm.getLoaded()
.get(sm.getMetaData().getField("stringField").getIndex()));
} else {
// unredefined properties with user-defined instance, or any
// unredefined field access
assertTrue(sm.getLoaded()
.get(sm.getMetaData().getField("stringField").getIndex()));
// make sure that the value was not cleared
Field field = getUnenhancedClass().getDeclaredField(
isFieldAccessTest() ? "stringField" : "sf");
field.setAccessible(true);
assertEquals("foo", field.get(un));
}
em.close();
}
protected abstract boolean isFieldAccessTest();
public void testLazyLoading()
throws NoSuchFieldException, IllegalAccessException {
OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newUnenhancedInstance();
em.getTransaction().begin();
em.persist(un);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
un = em.find(getUnenhancedClass(), un.getId());
assertTrue(getUnenhancedClass() != un.getClass());
OpenJPAStateManager sm = (OpenJPAStateManager)
ImplHelper.toPersistenceCapable(un, null).pcGetStateManager();
// we only expect lazy loading to work when we can redefine classes
// or when accessing a property-access record that OpenJPA created.
if (ClassRedefiner.canRedefineClasses(_log)
|| AccessCode.isProperty(sm.getMetaData().getAccessType())) {
assertFalse(sm.getLoaded()
.get(sm.getMetaData().getField("lazyField").getIndex()));
// make sure that the value was cleared
Field field = getUnenhancedClass().getDeclaredField("lazyField");
field.setAccessible(true);
assertEquals(null, field.get(un));
} else {
// unredefined field access
assertTrue(sm.getLoaded()
.get(sm.getMetaData().getField("lazyField").getIndex()));
// make sure that the value was loaded already
Field field = getUnenhancedClass().getDeclaredField("lazyField");
field.setAccessible(true);
assertEquals("lazy", field.get(un));
}
// make sure that the value is available, one way or another
assertEquals("lazy", un.getLazyField());
assertTrue(sm.getLoaded()
.get(sm.getMetaData().getField("lazyField").getIndex()));
em.close();
}
public void testSerializationOfUserDefinedInstance()
throws IOException, ClassNotFoundException {
serializationHelper(true, false);
}
public void testSerializationOfUserDefinedSubclassInstance()
throws IOException, ClassNotFoundException {
serializationHelper(true, true);
}
public void testSerializationOfOpenJPADefinedInstance()
throws IOException, ClassNotFoundException {
serializationHelper(false, false);
}
public void testSerializationOfOpenJPADefinedSubclassInstance()
throws IOException, ClassNotFoundException {
serializationHelper(false, true);
}
private void serializationHelper(boolean userDefined, boolean sub)
throws IOException, ClassNotFoundException {
OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newInstance(sub);
em.getTransaction().begin();
em.persist(un);
if (sub) {
UnenhancedType related = newInstance(false);
related.setStringField("related");
((UnenhancedSubtype) un).setRelated(related);
}
em.getTransaction().commit();
if (!userDefined) {
em.close();
em = emf.createEntityManager();
}
un = em.find(getUnenhancedClass(), un.getId());
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(out);
oout.writeObject(un);
oout.flush();
byte[] bytes = out.toByteArray();
ObjectInputStream oin = new ObjectInputStream(
new ByteArrayInputStream(bytes));
UnenhancedType deserialized = (UnenhancedType) oin.readObject();
copiedInstanceHelper(sub, em, un, deserialized, false);
em.close();
}
public void testCloningOfUserDefinedInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
cloneHelper(true, false);
}
public void testCloningOfUserDefinedSubclassInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
cloneHelper(true, true);
}
public void testCloningOfOpenJPADefinedInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
cloneHelper(false, false);
}
public void testCloningOfOpenJPADefinedSubclassInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
cloneHelper(false, true);
}
private void cloneHelper(boolean userDefined, boolean sub)
throws IOException, ClassNotFoundException, CloneNotSupportedException {
OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newInstance(sub);
em.getTransaction().begin();
em.persist(un);
if (sub) {
UnenhancedType related = newInstance(false);
related.setStringField("related");
((UnenhancedSubtype) un).setRelated(related);
}
em.getTransaction().commit();
if (!userDefined) {
em.close();
em = emf.createEntityManager();
}
un = em.find(getUnenhancedClass(), un.getId());
UnenhancedType cloned = (UnenhancedType) un.clone();
copiedInstanceHelper(sub, em, un, cloned, true);
em.close();
}
private void copiedInstanceHelper(boolean sub, OpenJPAEntityManager em,
UnenhancedType un, UnenhancedType copy, boolean viaClone) {
assertNotSame(un, copy);
if (!viaClone)
assertEquals(sub ? getUnenhancedSubclass() : getUnenhancedClass(),
copy.getClass());
assertEquals(un.getId(), copy.getId());
assertEquals(un.getStringField(), copy.getStringField());
if (sub) {
assertEquals(
((UnenhancedSubtype) un).getIntField(),
((UnenhancedSubtype) copy).getIntField());
assertNotSame(
((UnenhancedSubtype) un).getRelated(),
((UnenhancedSubtype) copy).getRelated());
assertEquals(
((UnenhancedSubtype) un).getRelated().getId(),
((UnenhancedSubtype) copy).getRelated().getId());
}
assertFalse(em.isDetached(un));
assertTrue(em.isDetached(copy));
/*
##### need to make detachment algorithm in ReflectingPC smarter
// ensure that remove() cannot be invoked on a detached instance
try {
em.getTransaction().begin();
em.remove(copy);
fail("remove() cannot be invoked on detached instance");
} catch (IllegalArgumentException e) {
em.getTransaction().rollback();
}
*/
copy.setStringField("offline update");
em.getTransaction().begin();
assertSame(un, em.merge(copy));
assertTrue(em.isDirty(un));
assertEquals("offline update", un.getStringField());
em.getTransaction().commit();
}
public void testListenersOnUserDefinedInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
listenerHelper(true, false);
}
public void testListenersOnUserDefinedSubclassInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
listenerHelper(true, true);
}
public void testListenersOnOpenJPADefinedInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
listenerHelper(false, false);
}
public void testListenersOnOpenJPADefinedSubclassInstance()
throws IOException, ClassNotFoundException, CloneNotSupportedException {
listenerHelper(false, true);
}
private void listenerHelper(boolean userDefined, boolean sub)
throws IOException, ClassNotFoundException, CloneNotSupportedException {
ListenerImpl listener = new ListenerImpl();
emf.addLifecycleListener(listener, (Class[]) null);
OpenJPAEntityManager em = emf.createEntityManager();
UnenhancedType un = newInstance(sub);
em.getTransaction().begin();
em.persist(un);
em.getTransaction().commit();
if (!userDefined) {
em.close();
em = emf.createEntityManager();
}
listener.invoked = false;
un = em.find(getUnenhancedClass(), un.getId());
em.getTransaction().begin();
un.setStringField("updated");
em.getTransaction().commit();
assertTrue(listener.invoked);
em.close();
assertEquals("updated", listener.stringField);
}
public void testGetMetaDataOfSubtype() {
ClassMetaData meta = JPAFacadeHelper.getMetaData(emf,
getUnenhancedClass());
List<Class<?>> subs = ManagedClassSubclasser.prepareUnenhancedClasses(
emf.getConfiguration(), Collections.singleton(getUnenhancedClass()),
null);
assertSame(meta, JPAFacadeHelper.getMetaData(emf, subs.get(0)));
meta = JPAFacadeHelper.getMetaData(emf, getUnenhancedSubclass());
subs = ManagedClassSubclasser.prepareUnenhancedClasses(
emf.getConfiguration(),
Collections.singleton(getUnenhancedSubclass()), null);
assertSame(meta, JPAFacadeHelper.getMetaData(emf, subs.get(0)));
}
private class ListenerImpl
extends AbstractLifecycleListener {
String stringField;
boolean invoked;
@Override
public void afterStore(LifecycleEvent event) {
invoked = true;
stringField = ((UnenhancedType) event.getSource()).getStringField();
}
}
}