blob: 7d8d44b02ae7ac654b5ab2ec197c5f3d6489d793 [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.kernel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import org.apache.openjpa.event.AbstractLifecycleListener;
import org.apache.openjpa.event.AbstractTransactionListener;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.TransactionEvent;
import org.apache.openjpa.persistence.InvalidStateException;
import org.apache.openjpa.persistence.JPAFacadeHelper;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan;
import org.apache.openjpa.persistence.jdbc.JoinSyntax;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/*
* To test:
* - managed transactions
* - converting non-enhanced classes to enhanced subclasses
* (maybe an ugly ThreadLocal, maybe through PCData?)
*/
public abstract class AbstractBrokerSerializationTest<T>
extends SingleEMFTestCase {
private static LifeListener deserializedLifeListener;
private static int testGlobalRefreshCount = 0;
private static TxListener deserializedTxListener;
private static int testGlobalBeginCount = 0;
private Object id;
public void setUp() {
testGlobalRefreshCount = 0;
deserializedLifeListener = null;
testGlobalBeginCount = 0;
deserializedTxListener = null;
setUp(getManagedType(), getSecondaryType(), CLEAR_TABLES,
"openjpa.EntityManagerFactoryPool", "true");
T e = newManagedInstance();
OpenJPAEntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(e);
em.getTransaction().commit();
id = em.getObjectId(e);
em.close();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
testGlobalRefreshCount = 0;
deserializedLifeListener = null;
testGlobalBeginCount = 0;
deserializedTxListener = null;
}
public void testEmptyBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
assertTrue(em != em2);
assertTrue(
JPAFacadeHelper.toBroker(em) != JPAFacadeHelper.toBroker(em2));
assertSame(em.getEntityManagerFactory(), em2.getEntityManagerFactory());
assertSame(em2, JPAFacadeHelper.toBroker(em2)
.getUserObject(JPAFacadeHelper.EM_KEY));
em.close();
assertTrue(em2.isOpen());
em2.close();
}
public void testNontransactionalBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
T e = em.find(getManagedType(), id);
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
assertFalse(em2.getTransaction().isActive());
assertFalse(em2.contains(e));
assertEquals(1*graphSize(), em2.getManagedObjects().size());
T e2 = em2.find(getManagedType(), id);
assertEquals(em.getObjectId(e), em2.getObjectId(e2));
em.close();
em2.close();
}
public void testUnflushedOptimisticTxBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
T e = em.find(getManagedType(), id);
OpenJPAEntityManager em2 = null;
OpenJPAEntityManager em3 = null;
try {
em.getTransaction().begin();
modifyInstance(e);
T newe = newManagedInstance();
em.persist(newe);
em2 = deserializeEM(serialize(em));
assertTrue(em2.getTransaction().isActive());
assertFalse(em2.contains(e));
T e2 = em2.find(getManagedType(), id);
assertEquals(em.getObjectId(e), em2.getObjectId(e2));
assertEquals("modified", getModifiedValue(e2));
em.getTransaction().rollback();
assertTrue(em2.getTransaction().isActive());
em2.getTransaction().commit();
em3 = emf.createEntityManager();
T e3 = em3.find(getManagedType(), id);
assertEquals(getModifiedValue(e2), getModifiedValue(e3));
assertTrue(1 < ((Number) em3.createQuery("select count(o) from "
+ getManagedType().getName() + " o").getSingleResult())
.intValue());
} finally {
close(em);
close(em2);
close(em3);
}
}
public void testFlushedOptimisticTxBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
T e = em.find(getManagedType(), id);
em.getTransaction().begin();
modifyInstance(e);
em.flush();
try {
serialize(em);
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("flushed"));
} finally {
em.getTransaction().rollback();
em.close();
}
}
public void testConnectedOptimisticTxBrokerSerialization() {
Map m = new HashMap();
m.put("openjpa.ConnectionRetainMode", "always");
OpenJPAEntityManager em = emf.createEntityManager(m);
try {
serialize(em);
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("connected"));
} finally {
em.close();
}
}
public void testEmptyPessimisticTxBrokerSerialization() {
Map m = new HashMap();
m.put("openjpa.Optimistic", "false");
OpenJPAEntityManager em = emf.createEntityManager(m);
em.getTransaction().begin();
try {
serialize(em);
fail("should not be able to serialize");
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("datastore (pessimistic)"));
} finally {
em.getTransaction().rollback();
em.close();
}
}
public void testNonEmptyPessimisticTxBrokerSerialization() {
Map m = new HashMap();
m.put("openjpa.Optimistic", "false");
OpenJPAEntityManager em = emf.createEntityManager(m);
T e = em.find(getManagedType(), id);
em.getTransaction().begin();
try {
serialize(em);
fail("should not be able to serialize");
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("datastore (pessimistic)"));
} finally {
em.getTransaction().rollback();
em.close();
}
}
public void testFetchConfigurationMutations() {
OpenJPAEntityManager em = emf.createEntityManager();
JDBCFetchPlan plan = (JDBCFetchPlan) em.getFetchPlan();
assertNotEquals(17, plan.getLockTimeout());
assertNotEquals(JoinSyntax.TRADITIONAL, plan.getJoinSyntax());
plan.setLockTimeout(17);
plan.setJoinSyntax(JoinSyntax.TRADITIONAL);
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
JDBCFetchPlan plan2 = (JDBCFetchPlan) em2.getFetchPlan();
assertEquals(17, plan2.getLockTimeout());
assertEquals(JoinSyntax.TRADITIONAL, plan2.getJoinSyntax());
}
public void testInMemorySavepointsWithNewInstances() {
emf.close();
OpenJPAEntityManagerFactory emf = createEMF(
getManagedType(), getSecondaryType(),
"openjpa.EntityManagerFactoryPool", "true",
"openjpa.SavepointManager", "in-mem");
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = null;
try {
em.getTransaction().begin();
T t = newManagedInstance();
Object orig = getModifiedValue(t);
em.persist(t);
Object id = em.getObjectId(t);
em.setSavepoint("foo");
modifyInstance(t);
assertNotEquals(orig, getModifiedValue(t));
em2 = deserializeEM(serialize(em));
T t2 = em2.find(getManagedType(), id);
assertNotEquals(orig, getModifiedValue(t2));
em.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t));
em2.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t2));
} finally {
close(em);
close(em2);
}
}
public void testInMemorySavepointsWithModifiedInstances() {
emf.close();
OpenJPAEntityManagerFactory emf = createEMF(
getManagedType(), getSecondaryType(),
"openjpa.EntityManagerFactoryPool", "true",
"openjpa.SavepointManager", "in-mem");
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = null;
try {
em.getTransaction().begin();
T t = em.find(getManagedType(), id);
Object orig = getModifiedValue(t);
em.setSavepoint("foo");
modifyInstance(t);
assertNotEquals(orig, getModifiedValue(t));
em2 = deserializeEM(serialize(em));
T t2 = em2.find(getManagedType(), id);
assertNotEquals(orig, getModifiedValue(t2));
em.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t));
em2.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t2));
} finally {
close(em);
close(em2);
}
}
public void testEventManagers() {
TxListener txListener = new TxListener();
emf.addTransactionListener(txListener);
LifeListener lifeListener = new LifeListener();
emf.addLifecycleListener(lifeListener, null);
OpenJPAEntityManager em = emf.createEntityManager();
T t = em.find(getManagedType(), id);
assertEquals(0, lifeListener.refreshCount);
em.refresh(t);
assertEquals(1*graphSize(), lifeListener.refreshCount);
em.getTransaction().begin();
em.getTransaction().commit();
em.getTransaction().begin();
em.getTransaction().commit();
assertEquals(2, txListener.beginCount);
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
assertNotNull(deserializedLifeListener);
assertEquals(1* graphSize(),
deserializedLifeListener.refreshCount);
assertNotSame(lifeListener, deserializedLifeListener);
T t2 = em2.find(getManagedType(), id);
em2.refresh(t2);
assertEquals(2* graphSize(),
deserializedLifeListener.refreshCount);
// if this is 3*refreshMultiplier(), that means that there are
// extra registered listeners
assertEquals(2* graphSize(), testGlobalRefreshCount);
assertNotNull(deserializedTxListener);
assertEquals(2, deserializedTxListener.beginCount);
assertNotSame(txListener, deserializedTxListener);
em2.getTransaction().begin();
em2.getTransaction().rollback();
assertEquals(3, deserializedTxListener.beginCount);
// if this is 4, that means that there are extra registered listeners
assertEquals(3, testGlobalBeginCount);
}
byte[] serialize(Object o) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.flush();
return baos.toByteArray();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
OpenJPAEntityManager deserializeEM(byte[] bytes) {
return (OpenJPAEntityManager) deserialize(bytes);
}
private Object deserialize(byte[] bytes) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
void close(EntityManager em) {
if (em != null && em.isOpen() && em.getTransaction().isActive())
em.getTransaction().rollback();
if (em != null && em.isOpen())
em.close();
}
protected abstract Class<T> getManagedType();
protected abstract T newManagedInstance();
protected abstract void modifyInstance(T t);
protected abstract Object getModifiedValue(T t);
/**
* The number of instances in the graph created
* by {@link #newManagedInstance()} of type T.
*/
protected int graphSize() {
return 1;
}
/**
* An additional type that must be available in this PC. May be null.
*/
protected Class getSecondaryType() {
return null;
}
private static class TxListener
extends AbstractTransactionListener
implements Serializable {
private int beginCount = 0;
public TxListener() {
}
@Override
public void afterBegin(TransactionEvent event) {
beginCount++;
testGlobalBeginCount++;
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
in.defaultReadObject();
deserializedTxListener = this;
}
}
private static class LifeListener
extends AbstractLifecycleListener
implements Serializable {
private int refreshCount = 0;
@Override
public void afterRefresh(LifecycleEvent event) {
refreshCount++;
testGlobalRefreshCount++;
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
in.defaultReadObject();
deserializedLifeListener = this;
}
}
}