/* * Copyright (c) 2009-2012 Clark & Parsia, LLC. <http://www.clarkparsia.com> * * 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.clarkparsia.empire.lazyload; import java.io.File; import java.net.URI; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.FetchType; import javax.persistence.OneToMany; import javax.persistence.Persistence; import com.clarkparsia.empire.Empire; import com.clarkparsia.empire.SupportsRdfId; import com.clarkparsia.empire.SupportsRdfId.RdfKey; import com.clarkparsia.empire.annotation.RdfGenerator; import com.clarkparsia.empire.codegen.InstanceGenerator; import com.clarkparsia.empire.lazyload.Event.Status; import com.clarkparsia.empire.util.TestModule; import com.clarkparsia.empire.util.TestUtil; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class TestLazyCollectionLoad { private final static String TITLE = "Test"; @BeforeClass public static void beforeClass() { TestUtil.setConfigSystemProperty( "test.empire.config.properties" ); Empire.init(new TestModule()); RdfGenerator.init(Sets.<Class<?>>newHashSet(BusinessObjectImpl.class, EventImpl.class, Child.class, Parent.class)); } /** * Test case based on Laurent's bug report where lazy loaded collections where getting mangled over * repeated merges. * * @throws Exception test error */ @Test public void testLazyLoad() throws Exception { String id = "http://localhost:8080/empire/foo"; EntityManagerFactory f = Persistence.createEntityManagerFactory("test-data-source2"); // First transaction (web service call). final EntityManager aEntityManager = f.createEntityManager(); BusinessObject aBusinessObject = newBusiness(id, aEntityManager); assertEquals(TITLE, aBusinessObject.getTitle()); assertEquals(id, aBusinessObject.getUri()); assertEquals(1, aBusinessObject.getEvents().size()); assertEvent1(aBusinessObject.getEvents().iterator().next()); // Second transaction (web service call). aBusinessObject = addEvent(id, "Event #2", aEntityManager); assertEquals(TITLE, aBusinessObject.getTitle()); assertEquals(id, aBusinessObject.getUri()); assertEquals(2, aBusinessObject.getEvents().size()); Iterator<Event> aIter = aBusinessObject.getEvents().iterator(); assertEvent1(aIter.next()); assertEvent2(aIter.next()); // Third transaction (web service call). aBusinessObject = addEvent(id, "Event #3", aEntityManager); assertEquals(TITLE, aBusinessObject.getTitle()); assertEquals(id, aBusinessObject.getUri()); assertEquals(3, aBusinessObject.getEvents().size()); List<Event> aEvents = Lists.newArrayList(aBusinessObject.getEvents()); Collections.sort(aEvents, new Comparator<Event>() { public int compare(final Event theEvent, final Event theEvent2) { return theEvent.getParameters().compareTo(theEvent2.getParameters()); } }); aIter = aEvents.iterator(); assertEvent1(aIter.next()); assertEvent2(aIter.next()); assertEvent3(aIter.next()); // Read object final state. aBusinessObject = addEvent(id, null, aEntityManager); assertEquals(TITLE, aBusinessObject.getTitle()); assertEquals(id, aBusinessObject.getUri()); assertEquals(3, aBusinessObject.getEvents().size()); aEvents = Lists.newArrayList(aBusinessObject.getEvents()); Collections.sort(aEvents, new Comparator<Event>() { public int compare(final Event theEvent, final Event theEvent2) { return theEvent.getParameters().compareTo(theEvent2.getParameters()); } }); aIter = aEvents.iterator(); assertEvent1(aIter.next()); assertEvent2(aIter.next()); assertEvent3(aIter.next()); } /** * Test that annotating just the getter with '@OneToMany(fetch=FetchType.LAZY)' * will result in a lazy fetch (issue #106). As currently implemented, this is * testing that BeanReflectUtil#isFetchTypeLazy returns true to * RdfGenerator#getProxyOrDbObject so that that returns a proxy object. * * @throws Exception test error */ @Test public void testLazyLoadGetterAnnotation() throws Exception { EntityManagerFactory f = Persistence.createEntityManagerFactory("test-data-source2"); final EntityManager aEntityManager = f.createEntityManager(); URI aChildUri = new URI("http://example.org/c1"); URI aParentUri = new URI("http://example.org/p1"); newChildWithParent(aChildUri, aParentUri, aEntityManager); // Child has just the getter annotated with: // @OneToMany(fetch=FetchType.LAZY) Child aChild = aEntityManager.find(Child.class, aChildUri); Object itsParent = aChild.getIsChildOf().get(0); assertTrue("Expected a javassist.util.proxy.Proxy object indicating a lazy load", itsParent instanceof javassist.util.proxy.ProxyObject); } /** * Test that the proxy returned for a lazily-fetched interface-valued property * has the expected class hierarchy (Issue #107) * * @throws Exception test error */ @Test public void testLazyLoadInterfaceProxyHierarchy() throws Exception { EntityManagerFactory f = Persistence.createEntityManagerFactory("test-data-source2"); final EntityManager aEntityManager = f.createEntityManager(); URI aChildUri = new URI("http://example.org/c2"); URI aParentUri = new URI("http://example.org/p2"); newChildWithParent(aChildUri, aParentUri, aEntityManager); Child aChild = aEntityManager.find(Child.class, aChildUri); Object itsParent = aChild.getIsChildOf().get(0); // we require instanceof Proxy also because if isChildOf is not correctly lazily-loaded then // what we get back will be instanceof Parent. If using TestNG we could make this test // dependent on testLazyLoadGetterAnnotation assertTrue("Expected a javassist.util.proxy.Proxy object with Parent as an interface", itsParent instanceof javassist.util.proxy.ProxyObject && itsParent instanceof Parent); } private void assertEvent1(final Event theEvent) { assertEquals(Status.Complete, theEvent.getStatus()); assertEquals("Event #1", theEvent.getParameters()); } private void assertEvent2(final Event theEvent) { assertEquals(Status.Complete, theEvent.getStatus()); assertEquals("Event #2", theEvent.getParameters()); } private void assertEvent3(final Event theEvent) { assertEquals(Status.Complete, theEvent.getStatus()); assertEquals("Event #3", theEvent.getParameters()); } private static BusinessObject newBusiness(String uri, EntityManager m) { BusinessObject b = new BusinessObjectImpl(uri); b.setTitle(TITLE); b.add(new EventImpl(b.getUri(), "Event #1", Status.Complete, null)); m.persist(b); m.flush(); return b; } private static BusinessObject addEvent(String uri, String title, EntityManager m) { BusinessObject b = m.find(BusinessObjectImpl.class, uri); if (title != null) { Event e = new EventImpl(uri, title, Status.Complete, null); m.persist(e); b.add(e); b = m.merge(b); m.flush(); } return b; } /* * Construct data used by Child & Parent tests */ private static void newChildWithParent(URI childUri, URI parentUri, EntityManager m) throws InstantiationException, IllegalAccessException, Exception { Child child = InstanceGenerator.generateInstanceClass(Child.class).newInstance(); Parent parent = InstanceGenerator.generateInstanceClass(Parent.class).newInstance(); child.setRdfId(new SupportsRdfId.URIKey(childUri)); parent.setRdfId(new SupportsRdfId.URIKey(parentUri)); child.setIsChildOf(Lists.newArrayList(parent)); parent.setIsParentOf(Lists.newArrayList(child)); m.persist(child); m.persist(parent); } }