/**
* == @Spearal ==>
*
* Copyright (C) 2014 Franck WOLFF & William DRAI (http://www.spearal.io)
*
* Licensed 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.spearal.jpa2;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.Hibernate;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.spearal.DefaultSpearalFactory;
import org.spearal.SpearalDecoder;
import org.spearal.SpearalEncoder;
import org.spearal.SpearalFactory;
import org.spearal.configuration.PartialObjectFactory.PartialObjectProxy;
import org.spearal.configuration.PartialObjectFactory.UndefinedPropertyException;
import org.spearal.configuration.PropertyFactory.Property;
import org.spearal.impl.partial.JavassistPartialObjectFactory;
import org.spearal.impl.property.AnyProperty;
import org.spearal.impl.property.StringProperty;
import org.spearal.jpa2.impl.PartialEntityResolver;
import org.spearal.jpa2.impl.PartialEntityResolver.PartialEntityMap;
import org.spearal.jpa2.model.AbstractEntity;
import org.spearal.jpa2.model.Contact;
import org.spearal.jpa2.model.EntityWithEmbeddedId;
import org.spearal.jpa2.model.EntityWithEmbeddedId.EntityEmbeddedId;
import org.spearal.jpa2.model.EntityWithIdClass;
import org.spearal.jpa2.model.EntityWithIdClass.EntityIdClass;
import org.spearal.jpa2.model.Person;
/**
* @author Franck WOLFF
*/
@WithPersistenceUnit(dbName="testdb", persistenceUnitName="hibernate4-merge-pu", wrapped=true)
public class TestMergePartial extends AbstractHibernate4TestUnit {
@Before
public void setUp() throws Exception {
super.setUp();
}
@After
public void tearDown() throws Exception {
super.tearDown();
}
@Test
public void testIsProxy() throws IOException {
SpearalFactory clientFactory = new DefaultSpearalFactory(false);
SpearalFactory serverFactory = new DefaultSpearalFactory();
Person serverPerson1 = new Person("Bla", "Bla");
EntityManager entityManager = entityManagerFactory.createEntityManager();
PartialEntityResolver resolver = new PartialEntityResolver(entityManager);
Assert.assertTrue("Simple object", resolver.introspect(serverPerson1).isEmpty());
Contact serverContact1 = new Contact(serverPerson1, "bla", "06");
serverPerson1.getContacts().add(serverContact1);
Assert.assertTrue("Graph object", resolver.introspect(serverPerson1).isEmpty());
Person clientPerson2 = new Person("Bruce", "Willis");
Person serverPerson2 = clientEncodeServerDecode(clientFactory, serverFactory, clientPerson2, Person.class, "id", "version", "firstName");
PartialEntityMap map = resolver.introspect(serverPerson2);
Assert.assertFalse("Proxy object", map.isEmpty());
serverPerson2 = (Person)resolver.resolve(serverPerson2, map);
map = resolver.introspect(serverPerson2);
Assert.assertTrue("Non Proxy object", map.isEmpty());
Person clientPerson3 = new Person("Bruce", "Willis");
Contact clientContact3 = new Contact(clientPerson3, "blo", "06");
clientPerson3.getContacts().add(clientContact3);
Person serverPerson3 = clientEncodeServerDecode(clientFactory, serverFactory, clientPerson3, Contact.class, "id", "version", "person", "mobile");
Assert.assertFalse("Non proxy graph root", serverPerson3 instanceof PartialObjectProxy);
map = resolver.introspect(serverPerson3);
Assert.assertFalse("Proxy graph", map.isEmpty());
serverPerson3 = (Person)resolver.resolve(serverPerson3, map);
map = resolver.introspect(serverPerson3);
Assert.assertTrue("Non Proxy graph", map.isEmpty());
entityManager.close();
}
@Test
public void testIsProxy2() throws Exception {
SpearalFactory serverFactory = new DefaultSpearalFactory();
Person serverPerson2 = new Person("Blo", "Blo");
serverPerson2 = mergeEntity(serverPerson2);
Person serverPerson1 = new Person("Bla", "Bla");
serverPerson1.setBestFriend(serverPerson2);
serverPerson1 = mergeEntity(serverPerson1);
serverPerson1 = findEntity(Person.class, serverPerson1.getId());
EntityManager entityManager = entityManagerFactory.createEntityManager();
PartialEntityResolver resolver = new PartialEntityResolver(entityManager);
Assert.assertTrue("Simple object", resolver.introspect(serverPerson1).isEmpty());
entityManager.close();
Person incomingPerson1 = new Person("Bla", "Bla");
incomingPerson1.setId(serverPerson1.getId());
Property[] properties = new Property[2];
properties[0] = new AnyProperty("id", AbstractEntity.class.getDeclaredField("id"), Person.class.getMethod("getId"), Person.class.getMethod("setId", Long.class));
properties[1] = new StringProperty("lastName", Person.class.getDeclaredField("lastName"), Person.class.getMethod("getLastName"), Person.class.getMethod("setLastName", String.class));
Person incomingPerson2 = (Person)new JavassistPartialObjectFactory().instantiatePartial(serverFactory.getContext(), Person.class, properties);
incomingPerson2.setId(serverPerson2.getId());
incomingPerson2.setLastName("Toto");
incomingPerson1.setBestFriend(incomingPerson2);
mergeEntity(incomingPerson1);
serverPerson1 = findEntity(Person.class, serverPerson1.getId());
Assert.assertEquals("Value updated", "Toto", serverPerson1.getBestFriend().getLastName());
}
@Test
public void testMergeProxy() throws Exception {
Assert.assertNotNull(entityManagerFactory);
Person clientPerson = new Person("Bruce", "Willis");
// Do not load any services here (pseudo-client application).
SpearalFactory clientFactory = new DefaultSpearalFactory(false);
SpearalFactory serverFactory = new DefaultSpearalFactory();
Person serverPerson = clientEncodeServerDecodeMerge(clientFactory, serverFactory, clientPerson, Person.class, "id", "version", "firstName");
Assert.assertNotNull("Id not null", serverPerson.getId());
Assert.assertEquals("Version 0", Long.valueOf(0L), serverPerson.getVersion());
serverPerson = findEntity(Person.class, serverPerson.getId());
Assert.assertEquals("Person firstName", clientPerson.getFirstName(), serverPerson.getFirstName());
clientPerson.setId(serverPerson.getId());
clientPerson.backdoorSetVersion(serverPerson.getVersion());
clientPerson.setFirstName("John");
clientPerson.setLastName("McLane");
serverPerson = clientEncodeServerDecodeMerge(clientFactory, serverFactory, clientPerson, Person.class, "id", "version", "firstName");
Assert.assertEquals("Person firstName", clientPerson.getFirstName(), serverPerson.getFirstName());
serverPerson = findEntity(Person.class, serverPerson.getId());
Assert.assertEquals("Person firstName", clientPerson.getFirstName(), serverPerson.getFirstName());
Assert.assertEquals("Version 1", Long.valueOf(1L), serverPerson.getVersion());
Person clientFriend = new Person("Hans", "Gruber");
clientPerson.backdoorSetVersion(serverPerson.getVersion());
clientPerson.setBestFriend(clientFriend);
serverPerson = clientEncodeServerDecodeMerge(clientFactory, serverFactory, clientPerson, Person.class, "id", "version", "firstName", "bestFriend");
Assert.assertEquals("Friend firstName", clientFriend.getFirstName(), serverPerson.getBestFriend().getFirstName());
Assert.assertEquals("Version 0", Long.valueOf(0L), serverPerson.getBestFriend().getVersion());
serverPerson = findEntity(Person.class, serverPerson.getId());
Assert.assertEquals("Version 2", Long.valueOf(2L), serverPerson.getVersion());
}
@Test
public void testEmbeddedId() throws Exception {
Assert.assertNotNull(entityManagerFactory);
EntityWithEmbeddedId entity = new EntityWithEmbeddedId();
entity.setId(new EntityEmbeddedId("Joe", "Smith"));
entity.setAge(12);
entity.setPhones(Arrays.asList("0123456789", "9876543210"));
persistEntity(entity);
entity = new EntityWithEmbeddedId();
entity.setId(new EntityEmbeddedId("Joe", "Smith"));
entity.setAge(24);
SpearalFactory clientFactory = new DefaultSpearalFactory(false);
SpearalFactory serverFactory = new DefaultSpearalFactory();
entity = clientEncodeServerDecode(clientFactory, serverFactory, entity, EntityWithEmbeddedId.class, "id", "age");
Assert.assertTrue(entity instanceof PartialObjectProxy);
Assert.assertFalse(((PartialObjectProxy)entity).$isDefined("phones"));
try {
entity.getPhones();
Assert.fail("Should throw a UndefinedPropertyException");
}
catch (UndefinedPropertyException e) {
}
entity = mergeEntity(entity);
Assert.assertFalse(entity instanceof PartialObjectProxy);
Assert.assertNotNull(entity.getPhones());
Assert.assertFalse(entityManagerFactory.getPersistenceUnitUtil().isLoaded(entity.getPhones()));
entity = findEntity(EntityWithEmbeddedId.class, new EntityEmbeddedId("Joe", "Smith"), "getPhones");
Assert.assertEquals(24, entity.getAge());
Assert.assertEquals(Arrays.asList("0123456789", "9876543210"), entity.getPhones());
}
@Test
public void testIdClass() throws Exception {
Assert.assertNotNull(entityManagerFactory);
ManagedType<?> managedType = entityManagerFactory.getMetamodel().managedType(EntityWithIdClass.class);
int idsCount = 0;
for (SingularAttribute<?, ?> attribute : managedType.getSingularAttributes()) {
if (attribute.isId())
idsCount++;
}
Assert.assertEquals(2, idsCount);
EntityWithIdClass entity = new EntityWithIdClass();
entity.setFirstName("Jim");
entity.setLastName("Hall");
entity.setAge(12);
entity.setPhones(Arrays.asList("0123456789", "9876543210"));
persistEntity(entity);
entity = new EntityWithIdClass();
entity.setFirstName("Jim");
entity.setLastName("Hall");
entity.setAge(24);
SpearalFactory clientFactory = new DefaultSpearalFactory(false);
SpearalFactory serverFactory = new DefaultSpearalFactory();
entity = clientEncodeServerDecode(clientFactory, serverFactory, entity, EntityWithIdClass.class, "firstName", "lastName", "age");
Assert.assertTrue(entity instanceof PartialObjectProxy);
Assert.assertFalse(((PartialObjectProxy)entity).$isDefined("phones"));
try {
entity.getPhones();
Assert.fail("Should throw a UndefinedPropertyException");
}
catch (UndefinedPropertyException e) {
}
entity = mergeEntity(entity);
Assert.assertFalse(entity instanceof PartialObjectProxy);
Assert.assertNotNull(entity.getPhones());
Assert.assertFalse(entityManagerFactory.getPersistenceUnitUtil().isLoaded(entity.getPhones()));
entity = findEntity(EntityWithIdClass.class, new EntityIdClass("Jim", "Hall"), "getPhones");
Assert.assertEquals(24, entity.getAge());
Assert.assertEquals(Arrays.asList("0123456789", "9876543210"), entity.getPhones());
}
// @Test
// public void testEmbedded() throws Exception {
// Assert.assertNotNull(entityManagerFactory);
//
// EntityWithEmbedded entity = new EntityWithEmbedded();
// entity.setEmbedded(new EntityEmbedded("Jack", "Dalton"));
// entity.setAge(12);
// entity.setPhones(Arrays.asList("0123456789", "9876543210"));
//
// persistEntity(entity);
//
// entity = findEntity(EntityWithEmbedded.class, entity.getId());
//
// ManagedType<?> managedType = entityManagerFactory.getMetamodel().managedType(EntityWithEmbedded.class);
// for (Attribute<?, ?> attribute : managedType.getAttributes()) {
// System.out.println(attribute);
// }
// }
private <T> T clientEncodeServerDecodeMerge(SpearalFactory clientFactory, SpearalFactory serverFactory, Object clientEntity, Class<?> filterClass, String... properties) throws IOException {
T serverEntity = clientEncodeServerDecode(clientFactory, serverFactory, clientEntity, filterClass, properties);
return mergeEntity(serverEntity);
}
private <T> T clientEncodeServerDecode(SpearalFactory clientFactory, SpearalFactory serverFactory, Object clientEntity, Class<?> filterClass, String... properties) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
SpearalEncoder clientEncoder = clientFactory.newEncoder(out);
try {
clientEncoder.getPropertyFilter().add(filterClass, properties);
clientEncoder.writeAny(clientEntity);
}
catch (IOException e) {
Assert.fail(e.toString());
}
byte[] buf = out.toByteArray();
SpearalDecoder serverDecoder = serverFactory.newDecoder(new ByteArrayInputStream(buf));
return serverDecoder.readAny(clientEntity.getClass());
}
private <T> T findEntity(Class<T> serverEntityClass, Object id) {
return findEntity(serverEntityClass, id, null);
}
private <T> T findEntity(Class<T> serverEntityClass, Object id, String getterToInitialize) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
T savedEntity = entityManager.find(serverEntityClass, id);
if (savedEntity != null && getterToInitialize != null) {
try {
Hibernate.initialize(serverEntityClass.getMethod(getterToInitialize).invoke(savedEntity));
}
catch (Exception e) {
throw new RuntimeException("Could not initialize: " + getterToInitialize, e);
}
}
entityManager.flush();
tx.commit();
return savedEntity;
}
private void persistEntity(Object entity) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.persist(entity);
entityManager.flush();
tx.commit();
entityManager.close();
}
private <T> T mergeEntity(T serverEntity) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
T savedEntity = entityManager.merge(serverEntity);
entityManager.flush();
tx.commit();
entityManager.close();
return savedEntity;
}
}