/** * Copyright 2009-2013 Oy Vaadin Ltd * * 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 com.vaadin.addon.jpacontainer.integration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.OptimisticLockException; import javax.transaction.Transaction; import com.vaadin.addon.jpacontainer.BatchableEntityProvider; import com.vaadin.addon.jpacontainer.JPAContainerItem; import com.vaadin.addon.jpacontainer.MutableEntityProvider; import com.vaadin.addon.jpacontainer.provider.MutableLocalEntityProvider; import com.vaadin.v7.data.Buffered; import org.junit.Ignore; import org.junit.Test; import com.vaadin.addon.jpacontainer.EntityItem; import com.vaadin.addon.jpacontainer.EntityItemProperty; import com.vaadin.addon.jpacontainer.JPAContainer; import com.vaadin.addon.jpacontainer.JPAContainerFactory; import com.vaadin.addon.jpacontainer.fieldfactory.MultiSelectConverter; import com.vaadin.addon.jpacontainer.fieldfactory.SingleSelectConverter; import com.vaadin.addon.jpacontainer.provider.LocalEntityProvider; import com.vaadin.addon.jpacontainer.testdata.BeanWithLogic; import com.vaadin.addon.jpacontainer.testdata.DataGenerator; import com.vaadin.addon.jpacontainer.testdata.Department; import com.vaadin.addon.jpacontainer.testdata.Person; import com.vaadin.v7.data.Item; import com.vaadin.v7.data.Property; import com.vaadin.v7.data.util.filter.Compare.Equal; import com.vaadin.server.PaintException; import com.vaadin.v7.ui.ComboBox; import com.vaadin.ui.Component; import com.vaadin.v7.ui.DefaultFieldFactory; import com.vaadin.v7.ui.Field; import com.vaadin.v7.ui.Form; import com.vaadin.v7.ui.FormFieldFactory; import com.vaadin.v7.ui.Label; import com.vaadin.ui.Layout; import com.vaadin.v7.ui.ListSelect; import com.vaadin.v7.ui.Table; public abstract class AbstractComponentIntegrationTest extends AbstractIntegrationTest { private EntityManagerFactory emf; public abstract EntityManagerFactory getTestFactory(String dburl); public AbstractComponentIntegrationTest() throws IOException { setEmf(getTestFactory(AbstractIntegrationTest.getDatabaseUrl())); } protected AbstractComponentIntegrationTest(EntityManagerFactory emf) { setEmf(emf); } private void setEmf(EntityManagerFactory testFactory) { emf = testFactory; } @Override protected EntityManagerFactory createEntityManagerFactory() throws IOException { return emf; } @Test public void testTableRendering() throws PaintException { JPAContainer<Person> personContainer = getPersonContainer(); Table table = new Table("Person list", personContainer); Layout dl = createDummyLayout(); dl.addComponent(table); // dl.getWindow().paint(getFakePaintTarget()); Item item = table.getItem(table.firstItemId()); Property fnp = item.getItemProperty("firstName"); Person person = DataGenerator.getTestDataSortedByPrimaryKey().get(0); assertEquals(person.getFirstName(), fnp.getValue()); } @Test public void testReferenceTypeInComboBox() { final JPAContainer<Person> personContainer = getPersonContainer(); EntityItem<Person> item = personContainer.getItem(personContainer .firstItemId()); /* * Prepeare a combobox to be used to edit manager (reference to another * Person) */ final ComboBox comboBox = new ComboBox(); comboBox.setContainerDataSource(personContainer); comboBox.setCaption("Manager"); comboBox.setConverter(new SingleSelectConverter<Person>(comboBox)); // comboBox.setPropertyDataSource(new SingleSelectTranslator(comboBox)); Form form = new Form(); form.setFormFieldFactory(new FormFieldFactory() { public Field createField(Item item, Object propertyId, Component uiContext) { if (propertyId.equals("manager")) { return comboBox; } return DefaultFieldFactory.get().createField(item, propertyId, uiContext); } }); form.setItemDataSource(item, Arrays.asList("manager", "firstName")); createDummyLayout().addComponent(form); Object value = comboBox.getValue(); // by default the manager should not be unassigned assertNull(value); Object nextItemId = personContainer.nextItemId(item.getItemId()); EntityItem<Person> item2 = personContainer.getItem(nextItemId); // now assign next person (id) as a value for the combobox comboBox.setValue(nextItemId); Object propetyValue = item.getItemProperty("manager").getValue(); // ensure property value match assertEquals(item2.getEntity(), propetyValue); // ensure the first persons manager is the second person at bean level assertEquals(item2.getEntity(), item.getEntity().getManager()); } @Test public void testCollectionTypeInListSelectWithMultiSelectTranslator() throws IOException { JPAContainer<Person> personContainer = getPersonContainer(); Object personId = personContainer.firstItemId(); Person person = personContainer.getItem(personId).getEntity(); // TODO move creation of test data to DataGenerator EntityManager entityManager = getEntityManager(); Department department = null; try { department = (Department) entityManager.createQuery( "SELECT d from Department d where d.name='FOO'") .getSingleResult(); } catch (Exception e) { } if (department == null) { entityManager.getTransaction().begin(); department = new Department(); department.setName("FOO"); Set<Person> persons = new HashSet<Person>(); persons.add(person); department.setPersons(persons); entityManager.persist(department); Department emptyDepartment = new Department(); emptyDepartment.setName("BAR"); entityManager.persist(emptyDepartment); entityManager.getTransaction().commit(); } JPAContainer<Department> departmentContainer = getDepartmentContainer(); Object firstItemId = departmentContainer.firstItemId(); EntityItem<Department> item = departmentContainer.getItem(firstItemId); ListSelect listSelect = new ListSelect("foo", personContainer); listSelect.setMultiSelect(true); listSelect.setPropertyDataSource(item.getItemProperty("persons")); listSelect.setConverter(new MultiSelectConverter(listSelect)); Set<Object> value = (Set<Object>) listSelect.getValue(); boolean containsPersonId = value.contains(personId); assertFalse(!containsPersonId); assertEquals(1, value.size()); Object secondPersonId = personContainer.nextItemId(personId); Person secondPerson = personContainer.getItem(secondPersonId) .getEntity(); listSelect.select(secondPersonId); value = (Set<Object>) listSelect.getValue(); containsPersonId = value.contains(personId); assert (containsPersonId); containsPersonId = value.contains(secondPersonId); assert (containsPersonId); assertEquals(2, value.size()); Set<Person> persons2 = item.getEntity().getPersons(); assertEquals(2, persons2.size()); assert (persons2.contains(person)); assert (persons2.contains(secondPerson)); // Now test with initially empty department Object nextItemId = departmentContainer.nextItemId(firstItemId); EntityItem<Department> item2 = departmentContainer.getItem(nextItemId); Department department2 = item2.getEntity(); listSelect.setPropertyDataSource(item2.getItemProperty("persons")); listSelect.select(personId); assertNotNull(department2); assertNotNull(department2.getPersons()); assertEquals(1, department2.getPersons().size()); assertFalse(!department.getPersons().contains(person)); } protected JPAContainer<Department> getDepartmentContainer() throws IOException { return JPAContainerFactory.make(Department.class, getEntityManager()); } @Test public void testValueChangeEventsFromEntityProperty() { JPAContainer<Person> container = getPersonContainer(); Object firstItemId = container.firstItemId(); EntityItem<Person> item = container.getItem(firstItemId); Person entity = item.getEntity(); String initialFirstNameValue = entity.getFirstName(); EntityItemProperty property = item.getItemProperty("firstName"); final int[] valueChangeCalls = new int[] { 0 }; Label label = new Label(property) { @Override public void valueChange( com.vaadin.v7.data.Property.ValueChangeEvent event) { valueChangeCalls[0] = valueChangeCalls[0] + 1; super.valueChange(event); } }; createDummyLayout().addComponent(label); assertEquals(entity.getFirstName(), label.getValue()); property.setValue("foo"); // FIXME due to bug we get double value change events assertEquals(2, valueChangeCalls[0]); assertEquals(entity.getFirstName(), label.getValue()); entity.setFirstName("bar"); /* * Although event listener was not fired, the value should be equal as * we have read throught mode on */ assertEquals(2, valueChangeCalls[0]); assertEquals(entity.getFirstName(), label.getValue()); /* * Refresh the value from DB ("foo" is set to property) */ item.refresh(); assertEquals(3, valueChangeCalls[0]); assertEquals("foo", label.getValue()); /* * Save changes to DB so that Container don't know about that. TODO this * should be done with jdbc */ LocalEntityProvider<Person> entityProvider = (LocalEntityProvider<Person>) container .getEntityProvider(); EntityManager em = entityProvider.getEntityManager(); entity = em.find(Person.class, entity.getId()); entity.setFirstName("bar"); em.merge(entity); boolean shouldNotMatch = entity.getFirstName().equals(label.getValue()); assertFalse(shouldNotMatch); assertEquals(3, valueChangeCalls[0]); item.refresh(); assertEquals(4, valueChangeCalls[0]); assertEquals(entity.getFirstName(), label.getValue()); /* * Then same test with container level api */ entity = em.find(Person.class, entity.getId()); entity.setFirstName("bar3"); em.merge(entity); shouldNotMatch = entity.getFirstName().equals(label.getValue()); assertFalse(shouldNotMatch); assertEquals(4, valueChangeCalls[0]); container.refreshItem(entity.getId()); assertEquals(5, valueChangeCalls[0]); assertEquals(entity.getFirstName(), label.getValue()); } @Ignore(value = "Enable when fixing the double value change event issue") @Test public void testShouldNotFireDoubleValueChangeEvents() { JPAContainer<Person> container = getPersonContainer(); Object firstItemId = container.firstItemId(); EntityItem<Person> item = container.getItem(firstItemId); EntityItemProperty property = item.getItemProperty("firstName"); final int[] valueChangeCalls = new int[] { 0 }; new Label(property) { @Override public void valueChange( com.vaadin.v7.data.Property.ValueChangeEvent event) { valueChangeCalls[0] = valueChangeCalls[0] + 1; super.valueChange(event); } }; property.setValue("foo"); assertEquals(1, valueChangeCalls[0]); } @Test public void testNullFilter() { JPAContainer<Person> container = getPersonContainer(); container.addContainerFilter(new Equal("firstName", null)); Object firstItemId = container.firstItemId(); assertNull(firstItemId); } @Test public void ensureSetterLogicIsUsed() throws IOException { JPAContainer<BeanWithLogic> container = JPAContainerFactory.make( BeanWithLogic.class, getEntityManager()); BeanWithLogic beanWithLogic = new BeanWithLogic(); Object id = container.addEntity(beanWithLogic); EntityItem<BeanWithLogic> item = container.getItem(id); EntityItemProperty itemProperty = item.getItemProperty("name"); itemProperty.setValue("fin"); // setter method should uppercase the // string Object valueFromProperty = itemProperty.getValue(); assertEquals("FIN", valueFromProperty); BeanWithLogic beanFromEntityManager = getEntityManager().find( BeanWithLogic.class, id); assertEquals("FIN", beanFromEntityManager.getName()); } @Test public void testRefreshEntityWithCachingProvider() { JPAContainer<Person> personContainer = getPersonContainer(); Object itemId = personContainer.firstItemId(); EntityItem<Person> item = personContainer.getItem(itemId); EntityManager entityManager = personContainer.getEntityProvider() .getEntityManager(); Person p = entityManager.find(Person.class, itemId); final String firstNameChangedValue = "Foo1"; p.setFirstName(firstNameChangedValue); entityManager.getTransaction().begin(); entityManager.persist(p); entityManager.getTransaction().commit(); Object fn = item.getItemProperty("firstName").getValue(); boolean same = fn.equals(firstNameChangedValue); assertFalse("New value although should still have cached", same); EntityItem<Person> item2 = personContainer.getItem(itemId); fn = item2.getItemProperty("firstName").getValue(); boolean same2 = fn.equals(firstNameChangedValue); // This is still fine due to caching. If somebody caching logig further // it is ok to remove or invert this test. // commented out as Hbn actually returns new value here // assertFalse("New value although should still have cached", same2); personContainer.refreshItem(itemId); // now get a new item, should not hit chache. This used to be broken EntityItem<Person> item3 = personContainer.getItem(itemId); Object value = item.getItemProperty("firstName").getValue(); Object value2 = item2.getItemProperty("firstName").getValue(); Object value3 = item3.getItemProperty("firstName").getValue(); // now all should have the new value assertEquals(firstNameChangedValue, value); assertEquals(firstNameChangedValue, value2); assertEquals(firstNameChangedValue, value3); } @Test public void testRemoveWithTwoRefreshCalls() { JPAContainer<Person> personContainer = getPersonContainer(); Object firstItemId = personContainer.firstItemId(); EntityItem<Person> item = personContainer.getItem(firstItemId); personContainer.removeItem(firstItemId); personContainer.refresh(); personContainer.refresh(); } @Test public void testConcurrentModification_buffered_shouldNotPersistSecondEdit() throws IOException { JPAContainer<Person> container1 = JPAContainerFactory.makeBatchable(Person.class, getEntityManager()); JPAContainer<Person> container2 = JPAContainerFactory.makeBatchable(Person.class, getEntityManager()); container1.setBuffered(true); container2.setBuffered(true); EntityItem<Person> person1 = container1.getItem(container1.firstItemId()); EntityItem<Person> person2 = container2.getItem(container2.firstItemId()); person1.getItemProperty("firstName").setValue("First edit"); person1.getItemProperty("lastName").setValue("First edit"); person2.getItemProperty("firstName").setValue("Second edit"); person2.getItemProperty("lastName").setValue("Second edit"); // First store container1.commit(); // Second store try { container2.commit(); } catch (Buffered.SourceException e) { // expected optimistic lock assertEquals(OptimisticLockException.class, e.getCause().getClass()); } JPAContainer<Person> container3 = getPersonContainer(); EntityItem<Person> person3 = container3.getItem(container3.firstItemId()); assertEquals("First edit", person3.getItemProperty("firstName").getValue()); assertEquals("First edit", person3.getItemProperty("lastName").getValue()); } @Test public void testConcurrentModification_unbuffered_shouldPersistSecondEdit() throws IOException { JPAContainer<Person> container1 = JPAContainerFactory.makeBatchable(Person.class, getEntityManager()); JPAContainer<Person> container2 = JPAContainerFactory.makeBatchable(Person.class, getEntityManager()); container1.setBuffered(false); container2.setBuffered(false); EntityItem<Person> person1 = container1.getItem(container1.firstItemId()); EntityItem<Person> person2 = container2.getItem(container2.firstItemId()); person1.getItemProperty("firstName").setValue("First edit"); person1.getItemProperty("lastName").setValue("First edit"); person2.getItemProperty("firstName").setValue("Second edit"); person2.getItemProperty("lastName").setValue("Second edit"); JPAContainer<Person> container3 = getPersonContainer(); EntityItem<Person> person3 = container3.getItem(container3.firstItemId()); assertEquals("Second edit", person3.getItemProperty("firstName").getValue()); assertEquals("Second edit", person3.getItemProperty("lastName").getValue()); } private void runInTransaction(EntityManager em, Runnable runnable) { try { em.getTransaction().begin(); runnable.run(); em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); } } /* This test simulates what happens if you create an entity provider that handles transactions with EJB annotations (overrides runInTransaction and adds @TransactionRequired) */ @Test public void testConcurrentModification_unbufferedEntitiesNotDetachedManualTransactions_shouldPersistSecondEdit() throws IOException { JPAContainer<Person> container1 = JPAContainerFactory.makeBatchable(Person.class, getEntityManager()); JPAContainer<Person> container2 = JPAContainerFactory.makeBatchable(Person.class, getEntityManager()); container1.setBuffered(false); container2.setBuffered(false); container1.getEntityProvider().setEntitiesDetached(false); container2.getEntityProvider().setEntitiesDetached(false); ((MutableLocalEntityProvider<Person>)container1.getEntityProvider()).setTransactionsHandledByProvider(false); ((MutableLocalEntityProvider<Person>)container2.getEntityProvider()).setTransactionsHandledByProvider(false); final EntityItem<Person> person1 = container1.getItem(container1.firstItemId()); final EntityItem<Person> person2 = container2.getItem(container2.firstItemId()); runInTransaction(container1.getEntityProvider().getEntityManager(), new Runnable() { @Override public void run() { person1.getItemProperty("firstName").setValue("First edit"); } }); runInTransaction(container1.getEntityProvider().getEntityManager(), new Runnable() { @Override public void run() { person1.getItemProperty("lastName").setValue("First edit"); } }); runInTransaction(container2.getEntityProvider().getEntityManager(), new Runnable() { @Override public void run() { person2.getItemProperty("firstName").setValue("Second edit"); } }); runInTransaction(container2.getEntityProvider().getEntityManager(), new Runnable() { @Override public void run() { person2.getItemProperty("lastName").setValue("Second edit"); } }); JPAContainer<Person> container3 = getPersonContainer(); EntityItem<Person> person3 = container3.getItem(container3.firstItemId()); assertEquals("Second edit", person3.getItemProperty("firstName").getValue()); assertEquals("Second edit", person3.getItemProperty("lastName").getValue()); } }