package org.springframework.flex.hibernate4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.LazyInitializationException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.flex.core.io.SpringPropertyProxy;
import org.springframework.flex.hibernate4.domain.Address;
import org.springframework.flex.hibernate4.domain.ContactInfo;
import org.springframework.flex.hibernate4.domain.EmbeddedAddress;
import org.springframework.flex.hibernate4.domain.EmbeddedFloor;
import org.springframework.flex.hibernate4.domain.EmbeddedFloorAttributes;
import org.springframework.flex.hibernate4.domain.MaritalStatus;
import org.springframework.flex.hibernate4.domain.Person;
import org.springframework.flex.hibernate4.config.JpaHibernateConfigProcessor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import flex.messaging.io.PropertyProxy;
import flex.messaging.io.PropertyProxyRegistry;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfMessageDeserializer;
import flex.messaging.io.amf.AmfMessageSerializer;
import flex.messaging.io.amf.AmfTrace;
import flex.messaging.io.amf.MessageBody;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "hibernate-jpa-context.xml")
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class,
SpringPropertyProxyHibernateJPATests.InternalDbTestExecutionListener.class})
public class SpringPropertyProxyHibernateJPATests {
private static final Log log = LogFactory.getLog(SpringPropertyProxyHibernateJPATests.class);
@Autowired
EntityManagerFactory emf;
@PersistenceContext
EntityManager em;
AmfMessageSerializer serializer;
AmfMessageDeserializer deserializer;
AmfTrace serializerTrace;
AmfTrace deserializerTrace;
MockHttpServletResponse response;
MockHttpServletRequest request;
boolean isTransactional = false;
@BeforeTransaction
public void setTxFlag() {
isTransactional = true;
}
@Before
public void init() throws Exception {
JpaHibernateConfigProcessor processor = new JpaHibernateConfigProcessor();
processor.setEntityManagerFactory(this.emf);
processor.afterPropertiesSet();
processor.processAfterStartup(null);
this.serializer = new AmfMessageSerializer();
this.serializerTrace = new AmfTrace();
this.response = new MockHttpServletResponse();
this.serializer.initialize(new SerializationContext(), response.getOutputStream(), serializerTrace);
this.deserializer = new AmfMessageDeserializer();
this.deserializerTrace = new AmfTrace();
this.request = new MockHttpServletRequest();
}
@After
public void trace() throws UnsupportedEncodingException {
log.info("Serializer Trace:\n" + serializerTrace);
log.info("Deserializer Trace:\n" + deserializerTrace);
}
@Test
public void serializationOutsideTransactionAfterFind() throws IOException, ClassNotFoundException {
assertTrue(!isTransactional);
EntityManager em = getEntityManager();
Person person = em.find(Person.class, 1);
em.close();
serialize(person);
Person result = (Person) deserialize();
assertNotSame(person, result);
assertEquals(new Integer(1), result.getId());
assertEquals("Dad", result.getName());
assertNotNull(result.getAddress());
assertNull(result.getSpouse());
assertNull(result.getChildren());
}
@Test
@Transactional
public void serializationInsideTransactionAfterFind() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
EntityManager em = getEntityManager();
Person person = em.find(Person.class, 1);
serialize(person);
Person result = (Person) deserialize();
assertNotSame(person, result);
assertEquals(new Integer(1), result.getId());
assertEquals("Dad", result.getName());
assertNotNull(result.getAddress());
assertNull(result.getSpouse());
assertNull(result.getChildren());
}
@Test
public void serializationOutsideTransactionAfterFindAndInitializedCollection() throws IOException, ClassNotFoundException {
assertTrue(!isTransactional);
EntityManager em = getEntityManager();
Person person = em.find(Person.class, 1);
person.getChildren().iterator();
em.close();
serialize(person);
Person result = (Person) deserialize();
assertNotSame(person, result);
assertEquals(new Integer(1), result.getId());
assertEquals("Dad", result.getName());
assertNotNull(result.getAddress());
assertNull(result.getSpouse());
assertNotNull(result.getChildren());
assertEquals(2, result.getChildren().size());
for (Person childResult : result.getChildren()) {
assertNotNull(childResult.getId());
assertTrue(StringUtils.hasText(childResult.getName()));
assertNull(childResult.getSpouse());
assertNull(childResult.getChildren());
}
}
@Test
@Transactional
public void serializationInsideTransactionAfterFindAndInitializedCollection() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
EntityManager em = getEntityManager();
Person person = em.find(Person.class, 1);
person.getChildren().iterator();
serialize(person);
Person result = (Person) deserialize();
assertNotSame(person, result);
assertEquals(new Integer(1), result.getId());
assertEquals("Dad", result.getName());
assertNotNull(result.getAddress());
assertNull(result.getSpouse());
assertNotNull(result.getChildren());
assertEquals(2, result.getChildren().size());
for (Person childResult : result.getChildren()) {
assertNotNull(childResult.getId());
assertTrue(StringUtils.hasText(childResult.getName()));
assertNull(childResult.getSpouse());
assertNull(childResult.getChildren());
}
}
@Test
public void serializationOutsideTransactionAfterGetReference() throws IOException {
assertTrue(!isTransactional);
EntityManager em = getEntityManager();
Person person = em.getReference(Person.class, 1);
em.close();
try {
serialize(person);
fail("Expected a LazyInitializationException");
} catch (LazyInitializationException ex) {
// expected
}
}
@Test
@Transactional
public void serializationInsideTransactionAfterGetReference() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
EntityManager em = getEntityManager();
Person person = em.getReference(Person.class, 1);
serialize(person);
Person result = (Person) deserialize();
assertNotSame(person, result);
assertEquals(new Integer(1), result.getId());
assertEquals("Dad", result.getName());
assertNotNull(result.getAddress());
assertNull(result.getSpouse());
assertNull(result.getChildren());
}
@SuppressWarnings("unchecked")
@Test
public void serializationOutsideTransactionAfterQuery() throws IOException, ClassNotFoundException {
assertTrue(!isTransactional);
EntityManager em = getEntityManager();
List<Person> people = em.createQuery("from Person").getResultList();
em.close();
serialize(people);
List<Person> results = (List<Person>) deserialize();
for (Person result : results) {
assertNotNull(result.getId());
assertTrue(StringUtils.hasText(result.getName()));
if (result.getSpouse() != null) {
assertTrue(results.contains(result.getSpouse()));
}
assertNull(result.getChildren());
}
}
@SuppressWarnings("unchecked")
@Test
@Transactional
public void serializationInsideTransactionAfterHibernateQuery() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
EntityManager em = getEntityManager();
List<Person> people = em.createQuery("from Person").getResultList();
serialize(people);
List<Person> results = (List<Person>) deserialize();
for (Person result : results) {
assertNotNull(result.getId());
assertTrue(StringUtils.hasText(result.getName()));
if (result.getSpouse() != null) {
assertTrue(results.contains(result.getSpouse()));
}
assertNull(result.getChildren());
}
}
@Test
@Transactional
public void setValueAndPersistNewEntityWithNumericAutogeneratedId() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
SpringPropertyProxy proxy = (SpringPropertyProxy) PropertyProxyRegistry.getRegistry().getProxy(Person.class);
Person person = new Person();
proxy.setValue(person, "id", Double.NaN);
proxy.setValue(person, "name", "Bob");
assertEquals(null, person.getId());
EntityManager em = getEntityManager();
em.persist(person);
assertNotNull(person.getId());
assertTrue(person.getId() > 0);
}
@Test
public void serializeNewEntityWithNumericNullId() throws IOException, ClassNotFoundException {
Person person = new Person();
person.setId(null);
person.setName("Bob");
serialize(person);
Person result = (Person) deserialize();
assertEquals(null, result.getId());
}
@Test
@Transactional
public void deserializeAndPersistNewEntityWithPrimitiveNumericAutogeneratedId() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
SpringPropertyProxy proxy = (SpringPropertyProxy) PropertyProxyRegistry.getRegistry().getProxy(ContactInfo.class);
ContactInfo contactInfo = new ContactInfo();
proxy.setValue(contactInfo, "id", 0);
proxy.setValue(contactInfo, "email", "bob@foo.com");
proxy.setValue(contactInfo, "phone", "5555551234");
assertEquals(0, contactInfo.getId());
EntityManager em = getEntityManager();
em.persist(contactInfo);
assertNotNull(contactInfo.getId());
assertTrue(contactInfo.getId() > 0);
}
@Test
@Transactional
public void deserializeAndPersistNewEntityWithVersion() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
SpringPropertyProxy proxy = (SpringPropertyProxy) PropertyProxyRegistry.getRegistry().getProxy(Person.class);
Person person = new Person();
proxy.setValue(person, "id", Double.NaN);
proxy.setValue(person, "version", Double.NaN);
proxy.setValue(person, "name", "Bob");
proxy.setValue(person, "maritalStatus", "DIVORCED");
assertNull(person.getVersion());
EntityManager em = getEntityManager();
em.persist(person);
assertNotNull(person.getId());
assertTrue(person.getId() > 0);
assertNotNull(person.getVersion());
assertTrue(person.getVersion() == 0);
assertEquals(MaritalStatus.DIVORCED, person.getMaritalStatus());
person.setName("Robert");
em.flush();
assertTrue(person.getVersion() > 0);
}
@Test
@Transactional
public void deserializeAndPersistNewEntityWithPrimitiveVersion() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
SpringPropertyProxy proxy = (SpringPropertyProxy) PropertyProxyRegistry.getRegistry().getProxy(ContactInfo.class);
ContactInfo contactInfo = new ContactInfo();
proxy.setValue(contactInfo, "id", 0);
proxy.setValue(contactInfo, "version", 0);
proxy.setValue(contactInfo, "email", "bob@foo.com");
proxy.setValue(contactInfo, "phone", "5555551234");
assertEquals(0, contactInfo.getId());
EntityManager em = getEntityManager();
em.persist(contactInfo);
assertNotNull(contactInfo.getId());
assertTrue(contactInfo.getId() > 0);
assertTrue(contactInfo.getVersion() == 0);
contactInfo.setEmail("bob@foobar.com");
em.flush();
assertTrue(contactInfo.getVersion() > 0);
}
@Test
@Transactional
public void persistNewEntityWithVersion() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
Person person = new Person();
person.setId(5000);
person.setVersion(null);
person.setName("Bob");
EntityManager em = getEntityManager();
em.persist(person);
assertNotNull(person.getId());
assertTrue(person.getId() > 0 && person.getId() < 5000);
assertNotNull(person.getVersion());
assertTrue(person.getVersion() == 0);
person.setName("Robert");
em.flush();
assertTrue(person.getVersion() > 0);
}
@Test
@Transactional
public void persistNewEntityWithPrimitiveVersion() throws IOException, ClassNotFoundException {
assertTrue(isTransactional);
ContactInfo contactInfo = new ContactInfo();
contactInfo.setId(0);
contactInfo.setVersion(-1);
contactInfo.setEmail("bob@foo.com");
contactInfo.setPhone("5555551234");
serialize(contactInfo);
ContactInfo deserializedContactInfo = (ContactInfo) deserialize();
assertEquals(0, deserializedContactInfo.getId());
EntityManager em = getEntityManager();
em.persist(deserializedContactInfo);
assertNotNull(deserializedContactInfo.getId());
assertTrue(deserializedContactInfo.getId() > 0);
assertTrue(deserializedContactInfo.getVersion() == 0);
deserializedContactInfo.setEmail("bob@foobar.com");
em.flush();
assertTrue(deserializedContactInfo.getVersion() > 0);
}
@Test
public void propertyProxyRegisteredForEmbeddedClass() {
PropertyProxy proxy = PropertyProxyRegistry.getRegistry().getProxy(EmbeddedAddress.class);
assertNotNull(proxy);
assertTrue(proxy instanceof SpringPropertyProxy);
}
@Test
public void propertyProxyRegisteredForElementCollection() {
PropertyProxy proxy = PropertyProxyRegistry.getRegistry().getProxy(EmbeddedFloor.class);
assertNotNull(proxy);
assertTrue(proxy instanceof SpringPropertyProxy);
}
@Test
public void propertyProxyRegisteredForNestedEmbeddable() {
PropertyProxy proxy = PropertyProxyRegistry.getRegistry().getProxy(EmbeddedFloorAttributes.class);
assertNotNull(proxy);
assertTrue(proxy instanceof SpringPropertyProxy);
}
private EntityManager getEntityManager() {
if (!isTransactional) {
return emf.createEntityManager();
} else {
return this.em;
}
}
private void serialize(Object data) throws IOException {
MessageBody body = new MessageBody();
body.setData(data);
serializer.writeBody(body);
}
private Object deserialize() throws ClassNotFoundException, IOException {
this.request.setContent(this.response.getContentAsByteArray());
this.deserializer.initialize(new SerializationContext(), this.request.getInputStream(), deserializerTrace);
MessageBody body = new MessageBody();
this.deserializer.readBody(body, 0);
return body.getData();
}
public static class InternalDbTestExecutionListener extends AbstractTestExecutionListener {
public void beforeTestClass(TestContext testContext) throws Exception {
EntityManagerFactory emf = testContext.getApplicationContext().getBean(EntityManagerFactory.class);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Person father = new Person();
father.setName("Dad");
father.setMaritalStatus(MaritalStatus.MARRIED);
em.persist(father);
Address address = new Address();
address.setStreet("777 Techwood Drive");
address.setCity("Atlanta");
address.setState("GA");
address.setZipcode("30022");
address.setRooms(5);
address.setMoveInDate(new Date());
em.persist(address);
father.setAddress(address);
Person mother = new Person();
mother.setName("Mom");
mother.setSpouse(father);
mother.setMaritalStatus(MaritalStatus.MARRIED);
em.persist(mother);
father.setSpouse(mother);
Person child1 = new Person();
child1.setName("Jack");
child1.setMaritalStatus(MaritalStatus.MARRIED);
em.persist(child1);
Person daughterInLaw = new Person();
daughterInLaw.setName("Lisa");
daughterInLaw.setSpouse(child1);
daughterInLaw.setMaritalStatus(MaritalStatus.MARRIED);
em.persist(daughterInLaw);
child1.setSpouse(daughterInLaw);
Person child2 = new Person();
child2.setName("Jill");
child2.setMaritalStatus(MaritalStatus.SINGLE);
em.persist(child2);
Set<Person> children = new HashSet<Person>();
children.add(child1);
children.add(child2);
father.setChildren(children);
mother.setChildren(children);
em.flush();
em.getTransaction().commit();
em.close();
}
}
}