[OLINGO-366,OLINGO-367] provided navigation link lazy loading (including collection) + fix for select query option support on collection and antity set

This commit is contained in:
fmartelli 2014-07-19 09:08:03 +02:00
parent cdc98eacd8
commit f22cbfda97
13 changed files with 93 additions and 51 deletions

View File

@ -56,6 +56,8 @@ public abstract class AbstractEntityCollectionInvocationHandler<
protected final URI baseURI; protected final URI baseURI;
protected URI targetEntitySetURI;
protected CommonURIBuilder<?> uri; protected CommonURIBuilder<?> uri;
private boolean isSingleton = false; private boolean isSingleton = false;
@ -69,6 +71,7 @@ public abstract class AbstractEntityCollectionInvocationHandler<
this.uri = uri; this.uri = uri;
this.baseURI = uri.build(); this.baseURI = uri.build();
this.targetEntitySetURI = uri.build();
this.isSingleton = AbstractSingleton.class.isAssignableFrom(ref); this.isSingleton = AbstractSingleton.class.isAssignableFrom(ref);
final Type[] entitySetParams = final Type[] entitySetParams =
@ -83,6 +86,7 @@ public abstract class AbstractEntityCollectionInvocationHandler<
final Class<?> itemRef, final Class<?> itemRef,
final Class<EC> collItemRef, final Class<EC> collItemRef,
final Service<?> service, final Service<?> service,
final URI targetEntitySetURI,
final CommonURIBuilder<?> uri) { final CommonURIBuilder<?> uri) {
super(service); super(service);
@ -90,6 +94,7 @@ public abstract class AbstractEntityCollectionInvocationHandler<
this.baseURI = uri == null ? null : uri.build(); this.baseURI = uri == null ? null : uri.build();
this.itemRef = (Class<T>) itemRef; this.itemRef = (Class<T>) itemRef;
this.collItemRef = collItemRef; this.collItemRef = collItemRef;
this.targetEntitySetURI = targetEntitySetURI;
} }
protected Class<T> getTypeRef() { protected Class<T> getTypeRef() {
@ -116,7 +121,7 @@ public abstract class AbstractEntityCollectionInvocationHandler<
} }
final EntityCollectionInvocationHandler<S> entityCollectionHandler = final EntityCollectionInvocationHandler<S> entityCollectionHandler =
new EntityCollectionInvocationHandler<S>(service, items, typeRef, uriBuilder); new EntityCollectionInvocationHandler<S>(service, items, typeRef, targetEntitySetURI, uriBuilder);
entityCollectionHandler.setAnnotations(annotations); entityCollectionHandler.setAnnotations(annotations);
return (SEC) Proxy.newProxyInstance( return (SEC) Proxy.newProxyInstance(
@ -167,7 +172,7 @@ public abstract class AbstractEntityCollectionInvocationHandler<
typeRef) typeRef)
: EntityInvocationHandler.getInstance( : EntityInvocationHandler.getInstance(
entity, entity,
null, targetEntitySetURI,
typeRef, typeRef,
service); service);

View File

@ -97,21 +97,23 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
protected Object getEntityCollectionProxy( protected Object getEntityCollectionProxy(
final Class<?> typeRef, final Class<?> typeRef,
final Class<?> typeCollectionRef, final Class<?> typeCollectionRef,
final String entityContainerName, final URI targetEntitySetURI,
final CommonODataEntitySet entitySet, final CommonODataEntitySet entitySet,
final URI uri, final URI uri,
final boolean checkInTheContext) { final boolean checkInTheContext) {
final List<Object> items = new ArrayList<Object>(); final List<Object> items = new ArrayList<Object>();
if (entitySet != null) {
for (CommonODataEntity entityFromSet : entitySet.getEntities()) { for (CommonODataEntity entityFromSet : entitySet.getEntities()) {
items.add(getEntityProxy(entityFromSet, entityContainerName, uri, typeRef, null, checkInTheContext)); items.add(getEntityProxy(entityFromSet, uri, typeRef, null, checkInTheContext));
}
} }
return Proxy.newProxyInstance( return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(), Thread.currentThread().getContextClassLoader(),
new Class<?>[] {typeCollectionRef}, new Class<?>[] {typeCollectionRef},
new EntityCollectionInvocationHandler(service, items, typeRef, new EntityCollectionInvocationHandler(service, items, typeRef, targetEntitySetURI,
uri == null ? null : getClient().newURIBuilder(uri.toASCIIString()))); uri == null ? null : getClient().newURIBuilder(uri.toASCIIString())));
} }
@ -127,7 +129,6 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
protected Object getEntityProxy( protected Object getEntityProxy(
final CommonODataEntity entity, final CommonODataEntity entity,
final String entityContainerName,
final URI entitySetURI, final URI entitySetURI,
final Class<?> type, final Class<?> type,
final String eTag, final String eTag,
@ -238,7 +239,6 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
return getEntityProxy( return getEntityProxy(
(CommonODataEntity) result, (CommonODataEntity) result,
null, null,
null,
method.getReturnType(), method.getReturnType(),
null, null,
false); false);

View File

@ -135,7 +135,6 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
return invokeSelfMethod(method, args); return invokeSelfMethod(method, args);
} else if ("load".equals(method.getName()) && ArrayUtils.isEmpty(args)) { } else if ("load".equals(method.getName()) && ArrayUtils.isEmpty(args)) {
load(); load();
attach(); // attach the current handler
return proxy; return proxy;
} else if ("operations".equals(method.getName()) && ArrayUtils.isEmpty(args)) { } else if ("operations".equals(method.getName()) && ArrayUtils.isEmpty(args)) {
final Class<?> returnType = method.getReturnType(); final Class<?> returnType = method.getReturnType();
@ -232,13 +231,14 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
final Object navPropValue; final Object navPropValue;
URI targetEntitySetURI = CoreUtils.getTargetEntitySetURI(getClient(), property);
final ODataLink link = ((ODataLinked) internal).getNavigationLink(property.name()); final ODataLink link = ((ODataLinked) internal).getNavigationLink(property.name());
if (link instanceof ODataInlineEntity) { if (link instanceof ODataInlineEntity) {
// return entity // return entity
navPropValue = getEntityProxy( navPropValue = getEntityProxy(
((ODataInlineEntity) link).getEntity(), ((ODataInlineEntity) link).getEntity(),
property.targetContainer(), targetEntitySetURI,
getClient().newURIBuilder().appendEntitySetSegment(property.targetEntitySet()).build(),
type, type,
null, null,
false); false);
@ -247,28 +247,26 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
navPropValue = getEntityCollectionProxy( navPropValue = getEntityCollectionProxy(
collItemType, collItemType,
type, type,
property.targetContainer(), targetEntitySetURI,
((ODataInlineEntitySet) link).getEntitySet(), ((ODataInlineEntitySet) link).getEntitySet(),
link.getLink(), targetEntitySetURI,
false); false);
} else { } else {
// navigate // navigate
final URI uri = URIUtils.getURI(getEntityHandler().getEntityURI(), property.name()); final URI targetURI = URIUtils.getURI(getEntityHandler().getEntityURI(), property.name());
if (EntityCollection.class.isAssignableFrom(type)) { if (EntityCollection.class.isAssignableFrom(type)) {
navPropValue = getEntityCollectionProxy( navPropValue = getEntityCollectionProxy(
collItemType, collItemType,
type, type,
property.targetContainer(), targetEntitySetURI,
getClient().getRetrieveRequestFactory().getEntitySetRequest(uri).execute().getBody(), null,
uri, targetURI,
true); true);
} else if (AbstractEntitySet.class.isAssignableFrom(type)) { } else if (AbstractEntitySet.class.isAssignableFrom(type)) {
navPropValue = getEntitySetProxy(type, uri); navPropValue = getEntitySetProxy(type, targetURI); // cannot be used standard target entity set URI
} else { } else {
URI entitySetURI = CoreUtils.getTargetEntitySetURI(getClient(), property); final EntityUUID uuid = new EntityUUID(targetEntitySetURI, collItemType, null);
final EntityUUID uuid = new EntityUUID(entitySetURI, collItemType, null);
LOG.debug("Ask for '{}({})'", collItemType.getSimpleName(), null); LOG.debug("Ask for '{}({})'", collItemType.getSimpleName(), null);
EntityInvocationHandler handler = getContext().entityContext().getEntity(uuid); EntityInvocationHandler handler = getContext().entityContext().getEntity(uuid);
@ -278,7 +276,11 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
collItemType.getAnnotation(Namespace.class).value(), ClassUtils.getEntityTypeName(collItemType))); collItemType.getAnnotation(Namespace.class).value(), ClassUtils.getEntityTypeName(collItemType)));
handler = EntityInvocationHandler.getInstance( handler = EntityInvocationHandler.getInstance(
entity, URIUtils.getURI(this.uri.build(), property.name()), entitySetURI, collItemType, service); entity,
URIUtils.getURI(this.uri.build(), property.name()),
targetEntitySetURI,
collItemType,
service);
} else if (getContext().entityContext().getStatus(handler) == AttachedEntityStatus.DELETED) { } else if (getContext().entityContext().getStatus(handler) == AttachedEntityStatus.DELETED) {
// object deleted // object deleted

View File

@ -56,14 +56,20 @@ public class EntityCollectionInvocationHandler<T extends StructuredType>
new HashMap<Class<? extends AbstractTerm>, Object>(); new HashMap<Class<? extends AbstractTerm>, Object>();
public EntityCollectionInvocationHandler( public EntityCollectionInvocationHandler(
final Service<?> service, final Collection<T> items, final Class<T> itemRef) { final Service<?> service,
this(service, items, itemRef, null); final Collection<T> items,
final Class<T> itemRef) {
this(service, items, itemRef, null, null);
} }
public EntityCollectionInvocationHandler( public EntityCollectionInvocationHandler(
final Service<?> service, final Collection<T> items, final Class<T> itemRef, final CommonURIBuilder<?> uri) { final Service<?> service,
final Collection<T> items,
final Class<T> itemRef,
final URI targetEntitySetURI,
final CommonURIBuilder<?> uri) {
super(itemRef, null, service, uri); super(itemRef, null, service, targetEntitySetURI, uri);
this.items = items; this.items = items;
} }
@ -120,6 +126,7 @@ public class EntityCollectionInvocationHandler<T extends StructuredType>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public EntityCollection<T> execute() { public EntityCollection<T> execute() {
if (this.uri != null) {
final Triple<List<T>, URI, List<ODataAnnotation>> entitySet = fetchPartialEntitySet(this.uri.build(), itemRef); final Triple<List<T>, URI, List<ODataAnnotation>> entitySet = fetchPartialEntitySet(this.uri.build(), itemRef);
this.nextPageURI = entitySet.getMiddle(); this.nextPageURI = entitySet.getMiddle();
@ -132,6 +139,7 @@ public class EntityCollectionInvocationHandler<T extends StructuredType>
annotations.clear(); annotations.clear();
annotations.addAll(entitySet.getRight()); annotations.addAll(entitySet.getRight());
}
return this; return this;
} }

View File

@ -74,6 +74,8 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
protected final Map<NavigationProperty, Object> linkChanges = new HashMap<NavigationProperty, Object>(); protected final Map<NavigationProperty, Object> linkChanges = new HashMap<NavigationProperty, Object>();
protected final Map<NavigationProperty, Object> linkChache = new HashMap<NavigationProperty, Object>();
protected int propertiesTag = 0; protected int propertiesTag = 0;
protected int linksTag = 0; protected int linksTag = 0;
@ -233,6 +235,7 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
this.streamedPropertyChanges.clear(); this.streamedPropertyChanges.clear();
this.propertyChanges.clear(); this.propertyChanges.clear();
this.linkChanges.clear(); this.linkChanges.clear();
this.linkChache.clear();
this.propertiesTag = 0; this.propertiesTag = 0;
this.linksTag = 0; this.linksTag = 0;
this.annotations.clear(); this.annotations.clear();
@ -465,6 +468,8 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
if (linkChanges.containsKey(property)) { if (linkChanges.containsKey(property)) {
navPropValue = linkChanges.get(property); navPropValue = linkChanges.get(property);
} else if (linkChache.containsKey(property)) {
navPropValue = linkChache.get(property);
} else { } else {
navPropValue = retrieveNavigationProperty(property, getter); navPropValue = retrieveNavigationProperty(property, getter);
} }
@ -490,13 +495,17 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
@Override @Override
protected void addLinkChanges(final NavigationProperty navProp, final Object value) { protected void addLinkChanges(final NavigationProperty navProp, final Object value) {
linkChanges.put(navProp, value);
}
protected void cacheLink(final NavigationProperty navProp, final Object value) {
final int checkpoint = linkChanges.hashCode(); final int checkpoint = linkChanges.hashCode();
linkChanges.put(navProp, value); linkChanges.put(navProp, value);
updateLinksTag(checkpoint); updateLinksTag(checkpoint);
if (linkChache.containsKey(navProp)) {
linkChache.remove(navProp);
}
}
protected void cacheLink(final NavigationProperty navProp, final Object value) {
linkChache.put(navProp, value);
} }
@Override @Override

View File

@ -79,7 +79,7 @@ class EntitySetInvocationHandler<
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
static EntitySetInvocationHandler getInstance( static EntitySetInvocationHandler getInstance(
final Class<?> ref, final Service<?> service, final URI uri) {; final Class<?> ref, final Service<?> service, final URI uri) {
return new EntitySetInvocationHandler( return new EntitySetInvocationHandler(
ref, ref,
@ -139,7 +139,7 @@ class EntitySetInvocationHandler<
final String entitySetName, final String entitySetName,
final CommonURIBuilder<?> uri) { final CommonURIBuilder<?> uri) {
super(itemRef, collItemRef, service, uri); super(itemRef, collItemRef, service, uri.build(), uri);
} }
@Override @Override
@ -246,7 +246,7 @@ class EntitySetInvocationHandler<
annotations.addAll(entitySet.getRight()); annotations.addAll(entitySet.getRight());
final EntityCollectionInvocationHandler<S> entityCollectionHandler = final EntityCollectionInvocationHandler<S> entityCollectionHandler =
new EntityCollectionInvocationHandler<S>(service, entitySet.getLeft(), ref, uriBuilder); new EntityCollectionInvocationHandler<S>(service, entitySet.getLeft(), ref, this.baseURI, uriBuilder);
entityCollectionHandler.setAnnotations(annotations); entityCollectionHandler.setAnnotations(annotations);
entityCollectionHandler.setNextPage(entitySet.getMiddle()); entityCollectionHandler.setNextPage(entitySet.getMiddle());

View File

@ -163,7 +163,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
Customer actual = readCustomer(container, id); Customer actual = readCustomer(container, id);
checkSampleCustomerProfile(actual, id, sampleName); checkSampleCustomerProfile(actual, id, sampleName);
assertEquals(1, actual.getOrders().size()); assertEquals(1, actual.getOrders().execute().size());
assertEquals(id, actual.getOrders().iterator().next().getOrderId()); assertEquals(id, actual.getOrders().iterator().next().getOrderId());
assertEquals(id, actual.getOrders().iterator().next().getCustomerId()); assertEquals(id, actual.getOrders().iterator().next().getCustomerId());

View File

@ -160,7 +160,7 @@ public class EntityRetrieveTestITCase extends AbstractTestITCase {
@Test @Test
public void withInlineFeed() { public void withInlineFeed() {
final Customer customer = readCustomer(getContainer(), -10); final Customer customer = readCustomer(getContainer(), -10);
final OrderCollection orders = customer.getOrders(); final OrderCollection orders = customer.getOrders().execute();
assertFalse(orders.isEmpty()); assertFalse(orders.isEmpty());
} }

View File

@ -92,7 +92,7 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
customer = container.getCustomer().getByKey(-9); customer = container.getCustomer().getByKey(-9);
assertEquals(2, customer.getOrders().size()); assertEquals(2, customer.getOrders().execute().size());
int count = 0; int count = 0;
for (Order inside : customer.getOrders()) { for (Order inside : customer.getOrders()) {

View File

@ -32,6 +32,7 @@ import java.sql.Timestamp;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.TimeZone; import java.util.TimeZone;
import static org.apache.olingo.fit.proxy.v4.AbstractTestITCase.service;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -60,6 +61,7 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
assertNull(customer.getPersonID()); assertNull(customer.getPersonID());
assertEquals(1, customer.load().getPersonID(), 0); assertEquals(1, customer.load().getPersonID(), 0);
service.getContext().detachAll();
} }
@Test @Test
@ -74,6 +76,7 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
} }
assertEquals(2, pageCount); assertEquals(2, pageCount);
service.getContext().detachAll(); // avoid influences
} }
@Test @Test
@ -93,6 +96,7 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
order.load(); order.load();
assertNotNull(order.getOrderDate()); assertNotNull(order.getOrderDate());
assertNotNull(order.getOrderID()); assertNotNull(order.getOrderID());
service.getContext().detachAll(); // avoid influences
} }
@Test @Test
@ -104,6 +108,19 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
customer.load(); customer.load();
assertEquals(1, customer.getOrders().size()); assertEquals(1, customer.getOrders().size());
service.getContext().detachAll(); // avoid influences
}
@Test
public void navigateLinks() {
final Customer customer = getContainer().getCustomers().getByKey(1); // no query
assertNotNull(customer.getCompany().load().getCompanyID()); // singleton: single query
assertEquals(1, customer.getOrders().execute().size()); // collection: single query
final Order order = getContainer().getOrders().getByKey(8); // no querys
assertNotNull(order.getCustomerForOrder().load().getPersonID()); // entity type: single query
service.getContext().detachAll(); // avoid influences
} }
@Test @Test
@ -147,5 +164,6 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
fail(); fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
service.getContext().detachAll(); // avoid influences
} }
} }

View File

@ -150,7 +150,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
Customer actual = readCustomer(getContainer(), id); Customer actual = readCustomer(getContainer(), id);
assertEquals(homeAddress.getCity(), actual.getHomeAddress().getCity()); assertEquals(homeAddress.getCity(), actual.getHomeAddress().getCity());
assertEquals(1, actual.getOrders().size()); assertEquals(1, actual.getOrders().execute().size());
assertEquals(8, actual.getOrders().iterator().next().getOrderID(), 0); assertEquals(8, actual.getOrders().iterator().next().getOrderID(), 0);
getContainer().getCustomers().delete(actual.getPersonID()); getContainer().getCustomers().delete(actual.getPersonID());
@ -218,7 +218,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
Customer actual = readCustomer(getContainer(), id); Customer actual = readCustomer(getContainer(), id);
assertEquals(homeAddress.getCity(), actual.getHomeAddress().getCity()); assertEquals(homeAddress.getCity(), actual.getHomeAddress().getCity());
assertEquals(1, actual.getOrders().size()); assertEquals(1, actual.getOrders().execute().size());
assertEquals(id, actual.getOrders().iterator().next().getOrderID()); assertEquals(id, actual.getOrders().iterator().next().getOrderID());
order = getContainer().getOrders().getByKey(id); order = getContainer().getOrders().getByKey(id);
@ -308,7 +308,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
product = getContainer().getProducts().getByKey(12).load(); product = getContainer().getProducts().getByKey(12).load();
assertEquals("Latte", product.getName()); assertEquals("Latte", product.getName());
assertEquals(12, product.getDetails().iterator().next().getProductDetailID(), 0); assertEquals(12, product.getDetails().execute().iterator().next().getProductDetailID(), 0);
} }
@Test @Test

View File

@ -135,7 +135,7 @@ public class EntityRetrieveTestITCase extends AbstractTestITCase {
public void withInlineFeed() { public void withInlineFeed() {
final Customer customer = readCustomer(getContainer(), 1); final Customer customer = readCustomer(getContainer(), 1);
final OrderCollection orders = customer.getOrders(); final OrderCollection orders = customer.getOrders();
assertEquals(1, orders.size()); assertEquals(1, orders.execute().size());
assertEquals(8, orders.iterator().next().getOrderID(), 0); assertEquals(8, orders.iterator().next().getOrderID(), 0);
} }

View File

@ -148,7 +148,7 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
// assertEquals(1, customer.getOrders().size()); // assertEquals(1, customer.getOrders().size());
int count = 0; int count = 0;
for (Order inside : customer.getOrders()) { for (Order inside : customer.getOrders().execute()) {
if (inside.getOrderID() == orderId) { if (inside.getOrderID() == orderId) {
count++; count++;
} }