You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2016/03/01 14:04:05 UTC

olingo-odata4 git commit: [OLINGO-890] fix expanding to a contained entity set results in an error

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 6c2676452 -> d566b186a


[OLINGO-890] fix expanding to a contained entity set results in an error

Signed-off-by: Christian Amend <ch...@sap.com>


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/d566b186
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/d566b186
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/d566b186

Branch: refs/heads/master
Commit: d566b186aeef42a20e02de0f176f054e27960733
Parents: 6c26764
Author: Frederik Zimmer <fr...@partake.de>
Authored: Mon Feb 22 18:21:11 2016 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Tue Mar 1 14:03:23 2016 +0100

----------------------------------------------------------------------
 .../commons/AbstractInvocationHandler.java      |   5 +-
 .../AbstractStructuredInvocationHandler.java    |  30 ++--
 .../InlineEntitySetInvocationHandler.java       | 163 +++++++++++++++++++
 .../olingo/ext/proxy/utils/ProxyUtils.java      |  53 ++++--
 .../main/resources/V40/Accounts/103/entity.xml  |  55 +++++++
 .../Accounts/103/links/MyPaymentInstruments.xml |  24 +++
 .../V40/MyPaymentInstruments/101901/entity.xml  |  41 +++++
 .../fit/proxy/APIBasicDesignTestITCase.java     |  48 ++++--
 8 files changed, 383 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
index ff4663e..d3aa3fc 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
@@ -20,6 +20,7 @@ package org.apache.olingo.ext.proxy.commons;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.UndeclaredThrowableException;
@@ -31,9 +32,9 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.olingo.client.api.EdmEnabledODataClient;
-import org.apache.olingo.client.api.uri.URIBuilder;
 import org.apache.olingo.client.api.domain.ClientEntity;
 import org.apache.olingo.client.api.domain.ClientValue;
+import org.apache.olingo.client.api.uri.URIBuilder;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.ext.proxy.AbstractService;
 import org.apache.olingo.ext.proxy.api.ComplexType;
@@ -89,6 +90,8 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
       return getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(this, args);
     } catch (UndeclaredThrowableException e) {
       throw e.getCause();
+    } catch(InvocationTargetException e) {
+      throw e.getTargetException();
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
index 6f2527c..3a3020f 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
@@ -38,9 +38,6 @@ import java.util.concurrent.Callable;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.olingo.client.api.uri.QueryOption;
-import org.apache.olingo.client.api.uri.URIBuilder;
-import org.apache.olingo.client.core.uri.URIUtils;
 import org.apache.olingo.client.api.domain.ClientEntity;
 import org.apache.olingo.client.api.domain.ClientInlineEntity;
 import org.apache.olingo.client.api.domain.ClientInlineEntitySet;
@@ -48,6 +45,9 @@ import org.apache.olingo.client.api.domain.ClientLink;
 import org.apache.olingo.client.api.domain.ClientLinked;
 import org.apache.olingo.client.api.domain.ClientProperty;
 import org.apache.olingo.client.api.domain.ClientValue;
+import org.apache.olingo.client.api.uri.QueryOption;
+import org.apache.olingo.client.api.uri.URIBuilder;
+import org.apache.olingo.client.core.uri.URIUtils;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.ext.proxy.AbstractService;
@@ -457,15 +457,21 @@ public abstract class AbstractStructuredInvocationHandler extends AbstractInvoca
               null,
               false);
     } else if (link instanceof ClientInlineEntitySet) {
-      // return entity set
-      navPropValue = ProxyUtils.getEntityCollectionProxy(
-              service,
-              collItemType,
-              type,
-              targetEntitySetURI,
-              ((ClientInlineEntitySet) link).getEntitySet(),
-              targetEntitySetURI,
-              false);
+      if (AbstractEntitySet.class.isAssignableFrom(type)) {
+        navPropValue =
+            ProxyUtils.getEntitySetProxy(service, type, ((ClientInlineEntitySet) link).getEntitySet(),
+                targetEntitySetURI, false);
+      } else {
+        // return entity set
+        navPropValue = ProxyUtils.getEntityCollectionProxy(
+                service,
+                collItemType,
+                type,
+                targetEntitySetURI,
+                ((ClientInlineEntitySet) link).getEntitySet(),
+                targetEntitySetURI,
+                false);
+      }
     } else {
       // navigate
       final URI targetURI = URIUtils.getURI(getEntityHandler().getEntityURI(), property.name());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/InlineEntitySetInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/InlineEntitySetInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/InlineEntitySetInvocationHandler.java
new file mode 100644
index 0000000..53e9501
--- /dev/null
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/InlineEntitySetInvocationHandler.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.olingo.ext.proxy.commons;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+import org.apache.olingo.client.api.uri.URIBuilder;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.ext.proxy.AbstractService;
+import org.apache.olingo.ext.proxy.api.AbstractEntitySet;
+import org.apache.olingo.ext.proxy.api.AbstractSingleton;
+import org.apache.olingo.ext.proxy.api.EntityCollection;
+import org.apache.olingo.ext.proxy.api.EntityType;
+import org.apache.olingo.ext.proxy.api.Search;
+import org.apache.olingo.ext.proxy.utils.ClassUtils;
+
+public class InlineEntitySetInvocationHandler<
+    T extends EntityType<?>, KEY extends Serializable, EC extends EntityCollection<T, ?, ?>>
+    extends AbstractEntityCollectionInvocationHandler<T, EC>
+    implements AbstractEntitySet<T, KEY, EC> {
+
+  private static final long serialVersionUID = 2629912294765040027L;
+
+  @SuppressWarnings({ "rawtypes", "unchecked" })
+  public static InlineEntitySetInvocationHandler getInstance(final Class<?> ref, final AbstractService<?> service,
+      final URI uri,
+      final List<Object> items) {
+    return new InlineEntitySetInvocationHandler(ref, service, service.getClient().newURIBuilder(uri.toASCIIString()),
+        items);
+  }
+
+  protected InlineEntitySetInvocationHandler(
+      final Class<?> ref,
+      final AbstractService<?> service,
+      final URIBuilder uri,
+      final Collection<T> items) {
+
+    super(ref, service, uri);
+
+    this.items = items;
+  }
+
+  @Override
+  public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+    if ("filter".equals(method.getName())
+        || "orderBy".equals(method.getName())
+        || "top".equals(method.getName())
+        || "skip".equals(method.getName())
+        || "expand".equals(method.getName())
+        || "select".equals(method.getName())) {
+
+      invokeSelfMethod(method, args);
+      return proxy;
+    } else if (isSelfMethod(method)) {
+      return invokeSelfMethod(method, args);
+    } else {
+      throw new NoSuchMethodException(method.getName());
+    }
+  }
+
+  @Override
+  public Long count() {
+    return Long.valueOf(size());
+  }
+
+  @Override
+  public Boolean exists(final KEY key) throws IllegalArgumentException {
+    throw new UnsupportedOperationException("exists not supported on inline entity set");
+  }
+
+  @Override
+  public T getByKey(final KEY key) throws IllegalArgumentException {
+    return getByKey(key, itemRef);
+  }
+
+  @Override
+  public <S extends T> S getByKey(final KEY key, final Class<S> typeRef) throws IllegalArgumentException {
+    throw new UnsupportedOperationException("getByKey not supported on inline entity set");
+  }
+
+  @Override
+  public EC execute() {
+    return execute(collItemRef);
+  }
+
+  public <S extends T, SEC extends EntityCollection<S, ?, ?>> Future<SEC> executeAsync(final Class<SEC> collTypeRef) {
+    return service.getClient().getConfiguration().getExecutor().submit(new Callable<SEC>() {
+      @Override
+      public SEC call() throws Exception {
+        return execute(collTypeRef);
+      }
+    });
+  }
+
+  @SuppressWarnings("unchecked")
+  public <S extends T, SEC extends EntityCollection<S, ?, ?>> SEC execute(final Class<SEC> collTypeRef) {
+    final Class<S> ref = (Class<S>) ClassUtils.extractTypeArg(collTypeRef,
+        AbstractEntitySet.class, AbstractSingleton.class, EntityCollection.class);
+    final Class<S> oref = (Class<S>) ClassUtils.extractTypeArg(collItemRef,
+        AbstractEntitySet.class, AbstractSingleton.class, EntityCollection.class);
+
+    if (!oref.equals(ref)) {
+      uri.appendDerivedEntityTypeSegment(new FullQualifiedName(
+          ClassUtils.getNamespace(ref), ClassUtils.getEntityTypeName(ref)).toString());
+    }
+
+    final EntityCollectionInvocationHandler<S> entityCollectionHandler = new EntityCollectionInvocationHandler<S>(
+        service, (Collection<S>) items, collTypeRef, baseURI, uri);
+
+    return (SEC) Proxy.newProxyInstance(
+        Thread.currentThread().getContextClassLoader(),
+        new Class<?>[] { collTypeRef },
+        entityCollectionHandler);
+  }
+
+  @Override
+  public Search<T, EC> createSearch() {
+    throw new UnsupportedOperationException("Search not supported on inline entity set");
+  }
+
+  @Override
+  public <S extends T, SEC extends EntityCollection<S, ?, ?>> Search<S, SEC> createSearch(final Class<SEC> reference) {
+    throw new UnsupportedOperationException("Search not supported on inline entity set");
+  }
+
+  @Override
+  public void delete(final KEY key) throws IllegalArgumentException {
+    throw new UnsupportedOperationException("Delete not supported on inline entity set");
+  }
+
+  @Override
+  public <S extends T> void delete(final S entity) {
+    throw new UnsupportedOperationException("Delete not supported on inline entity set");
+  }
+
+  @Override
+  public <S extends T> void delete(final Iterable<S> entities) {
+    throw new UnsupportedOperationException("Delete not supported on inline entity set");
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/utils/ProxyUtils.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/utils/ProxyUtils.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/utils/ProxyUtils.java
index 1800b1f..59a9537 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/utils/ProxyUtils.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/utils/ProxyUtils.java
@@ -21,28 +21,42 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.lang3.StringUtils;
-import org.apache.olingo.client.api.uri.URIBuilder;
 import org.apache.olingo.client.api.domain.ClientEntity;
 import org.apache.olingo.client.api.domain.ClientEntitySet;
 import org.apache.olingo.client.api.domain.ClientValue;
+import org.apache.olingo.client.api.uri.URIBuilder;
 import org.apache.olingo.ext.proxy.AbstractService;
+import org.apache.olingo.ext.proxy.api.AbstractEntitySet;
+import org.apache.olingo.ext.proxy.api.AbstractSingleton;
 import org.apache.olingo.ext.proxy.commons.ComplexInvocationHandler;
 import org.apache.olingo.ext.proxy.commons.EntityCollectionInvocationHandler;
 import org.apache.olingo.ext.proxy.commons.EntityInvocationHandler;
 import org.apache.olingo.ext.proxy.commons.EntitySetInvocationHandler;
+import org.apache.olingo.ext.proxy.commons.InlineEntitySetInvocationHandler;
 
 public class ProxyUtils {
 
-  @SuppressWarnings({"unchecked", "rawtypes"})
+  @SuppressWarnings({ "unchecked", "rawtypes" })
   public static Object getEntityCollectionProxy(
-          final AbstractService<?> service,
-          final Class<?> typeRef,
-          final Class<?> typeCollectionRef,
-          final URI targetEntitySetURI,
-          final ClientEntitySet entitySet,
-          final URI uri,
-          final boolean checkInTheContext) {
+      final AbstractService<?> service,
+      final Class<?> typeRef,
+      final Class<?> typeCollectionRef,
+      final URI targetEntitySetURI,
+      final ClientEntitySet entitySet,
+      final URI uri,
+      final boolean checkInTheContext) {
+
+    final List<Object> items = extractItems(service, typeRef, entitySet, uri, checkInTheContext);
 
+    return Proxy.newProxyInstance(
+        Thread.currentThread().getContextClassLoader(),
+        new Class<?>[] { typeCollectionRef },
+        new EntityCollectionInvocationHandler(service, items, typeCollectionRef, targetEntitySetURI,
+            uri == null ? null : service.getClient().newURIBuilder(uri.toASCIIString())));
+  }
+
+  private static List<Object> extractItems(final AbstractService<?> service, final Class<?> typeRef,
+      final ClientEntitySet entitySet, final URI uri, final boolean checkInTheContext) {
     final List<Object> items = new ArrayList<Object>();
 
     if (entitySet != null) {
@@ -50,12 +64,25 @@ public class ProxyUtils {
         items.add(getEntityProxy(service, entityFromSet, uri, typeRef, null, checkInTheContext));
       }
     }
+    return items;
+  }
+
+  public static Object getEntitySetProxy(
+      final AbstractService<?> service,
+      final Class<?> typeRef,
+      final ClientEntitySet entitySet,
+      final URI uri,
+      final boolean checkInTheContext) {
+
+    final Class<?> entityTypeRef = ClassUtils.extractTypeArg(typeRef, AbstractEntitySet.class,
+        AbstractSingleton.class);
+
+    final List<Object> items = extractItems(service, entityTypeRef, entitySet, uri, checkInTheContext);
 
     return Proxy.newProxyInstance(
-            Thread.currentThread().getContextClassLoader(),
-            new Class<?>[] {typeCollectionRef},
-            new EntityCollectionInvocationHandler(service, items, typeCollectionRef, targetEntitySetURI,
-                    uri == null ? null : service.getClient().newURIBuilder(uri.toASCIIString())));
+        Thread.currentThread().getContextClassLoader(),
+        new Class<?>[] { typeRef },
+        InlineEntitySetInvocationHandler.getInstance(typeRef, service, uri, items));
   }
 
   public static Object getEntitySetProxy(

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/fit/src/main/resources/V40/Accounts/103/entity.xml
----------------------------------------------------------------------
diff --git a/fit/src/main/resources/V40/Accounts/103/entity.xml b/fit/src/main/resources/V40/Accounts/103/entity.xml
new file mode 100644
index 0000000..bfa67ff
--- /dev/null
+++ b/fit/src/main/resources/V40/Accounts/103/entity.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
+<entry xml:base="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://docs.oasis-open.org/odata/ns/data" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:context="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/$metadata#Accounts/$entity">
+  <id>http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)</id>
+  <category term="#Microsoft.Test.OData.Services.ODataWCFService.Account" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
+  <link rel="edit" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/MyGiftCard" type="application/atom+xml;type=entry" title="MyGiftCard" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/MyGiftCard"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/MyPaymentInstruments" type="application/atom+xml;type=feed" title="MyPaymentInstruments" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/MyPaymentInstruments"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/ActiveSubscriptions" type="application/atom+xml;type=feed" title="ActiveSubscriptions" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/ActiveSubscriptions"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/AvailableSubscriptionTemplatess" type="application/atom+xml;type=feed" title="AvailableSubscriptionTemplatess" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/AvailableSubscriptionTemplatess"/>
+  <title/>
+  <updated>2014-04-14T12:45:00Z</updated>
+  <author>
+    <name/>
+  </author>
+  <m:action metadata="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/$metadata#Microsoft.Test.OData.Services.ODataWCFService.RefreshDefaultPI" 
+            title="Microsoft.Test.OData.Services.ODataWCFService.RefreshDefaultPI" 
+            target="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/Microsoft.Test.OData.Services.ODataWCFService.RefreshDefaultPI"/>
+  <m:action metadata="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/$metadata#Microsoft.Test.OData.Services.ODataWCFService.GetDefaultPI" 
+            title="Microsoft.Test.OData.Services.ODataWCFService.GetDefaultPI" 
+            target="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/Microsoft.Test.OData.Services.ODataWCFService.GetDefaultPI"/>
+  <m:action metadata="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/$metadata#Microsoft.Test.OData.Services.ODataWCFService.GetAccountInfo" 
+            title="Microsoft.Test.OData.Services.ODataWCFService.GetAccountInfo" 
+            target="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(103)/Microsoft.Test.OData.Services.ODataWCFService.GetAccountInfo"/>
+  <content type="application/xml">
+    <m:properties>
+      <d:AccountID m:type="Int32">103</d:AccountID>
+      <d:Country>US</d:Country>
+      <d:AccountInfo m:type="#Microsoft.Test.OData.Services.ODataWCFService.AccountInfo">
+        <d:FirstName>Alex</d:FirstName>
+        <d:LastName>Green</d:LastName>
+        <d:MiddleName>Hood</d:MiddleName>
+      </d:AccountInfo>
+    </m:properties>
+  </content>
+</entry>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/fit/src/main/resources/V40/Accounts/103/links/MyPaymentInstruments.xml
----------------------------------------------------------------------
diff --git a/fit/src/main/resources/V40/Accounts/103/links/MyPaymentInstruments.xml b/fit/src/main/resources/V40/Accounts/103/links/MyPaymentInstruments.xml
new file mode 100644
index 0000000..953bf62
--- /dev/null
+++ b/fit/src/main/resources/V40/Accounts/103/links/MyPaymentInstruments.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
+<links xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">
+  <uri>http://localhost:${tomcat.servlet.port}/stub/StaticService/V30/Static.svc/Accounts(103)/MyPaymentInstruments(101901)</uri>
+</links>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/fit/src/main/resources/V40/MyPaymentInstruments/101901/entity.xml
----------------------------------------------------------------------
diff --git a/fit/src/main/resources/V40/MyPaymentInstruments/101901/entity.xml b/fit/src/main/resources/V40/MyPaymentInstruments/101901/entity.xml
new file mode 100644
index 0000000..fa6a14a
--- /dev/null
+++ b/fit/src/main/resources/V40/MyPaymentInstruments/101901/entity.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
+<entry xml:base="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://docs.oasis-open.org/odata/ns/data" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:context="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/$metadata#Accounts(101)/MyPaymentInstruments/$entity">
+  <category term="#Microsoft.Test.OData.Services.ODataWCFService.PaymentInstrument" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
+  <link rel="edit" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(101)/MyPaymentInstruments(101901)"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/TheStoredPI" type="application/atom+xml;type=entry" title="TheStoredPI" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(101)/MyPaymentInstruments(101901)/TheStoredPI"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/BillingStatements" type="application/atom+xml;type=feed" title="BillingStatements" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(101)/MyPaymentInstruments(101901)/BillingStatements"/>
+  <link rel="http://docs.oasis-open.org/odata/ns/related/BackupStoredPI" type="application/atom+xml;type=entry" title="BackupStoredPI" href="http://localhost:${tomcat.servlet.port}/stub/StaticService/V40/Static.svc/Accounts(101)/MyPaymentInstruments(101901)/BackupStoredPI"/>
+  <id/>
+  <title/>
+  <updated>2014-04-14T12:47:37Z</updated>
+  <author>
+    <name/>
+  </author>
+  <content type="application/xml">
+    <m:properties>
+      <d:PaymentInstrumentID m:type="Int32">101901</d:PaymentInstrumentID>
+      <d:FriendlyName>101 first PI</d:FriendlyName>
+      <d:CreatedDate m:type="DateTimeOffset">2014-04-09T00:00:00Z</d:CreatedDate>
+    </m:properties>
+  </content>
+</entry>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d566b186/fit/src/test/java/org/apache/olingo/fit/proxy/APIBasicDesignTestITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/APIBasicDesignTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/APIBasicDesignTestITCase.java
index a66192b..d395268 100644
--- a/fit/src/test/java/org/apache/olingo/fit/proxy/APIBasicDesignTestITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/proxy/APIBasicDesignTestITCase.java
@@ -44,28 +44,31 @@ import org.apache.olingo.ext.proxy.commons.AbstractCollectionInvocationHandler;
 import org.apache.olingo.fit.proxy.demo.Service;
 import org.apache.olingo.fit.proxy.demo.odatademo.DemoService;
 import org.apache.olingo.fit.proxy.demo.odatademo.types.PersonDetail;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.InMemoryEntities;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.AccessLevel;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Account;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Account.MyPaymentInstruments;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Address;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.AddressCollection;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Color;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Customer;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.CustomerCollection;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.HomeAddress;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Order;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.OrderCollection;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.PaymentInstrument;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.PaymentInstrumentCollection;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Person;
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.PersonCollection;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.PersonComposableInvoker;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Product;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.ProductCollection;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types
         .ProductCollectionComposableInvoker;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types
-        .ProductDetailCollectionComposableInvoker;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.InMemoryEntities;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.AccessLevel;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Address;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.HomeAddress;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.OrderCollection;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.Person;
-import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.PersonCollection;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.ProductDetail;
 import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types.ProductDetailCollection;
-
+import org.apache.olingo.fit.proxy.staticservice.microsoft.test.odata.services.odatawcfservice.types
+        .ProductDetailCollectionComposableInvoker;
 // CHECKSTYLE:ON (Maven checkstyle)
 import org.junit.Test;
 
@@ -96,6 +99,31 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
       assertNotNull(customer.getLastName());
     }
   }
+  
+  @Test
+  public void expandToContainedEntitySet() {
+    Account account = container.getAccounts().getByKey(103).expand("MyPaymentInstruments").load();
+    assertNotNull(account);
+    assertNotNull(account.getAccountID());
+    MyPaymentInstruments myPaymentInstruments = account.getMyPaymentInstruments();
+    assertNotNull(myPaymentInstruments);
+    PaymentInstrument paymentInstrument = myPaymentInstruments.iterator().next();
+    assertNotNull(paymentInstrument);
+    assertNotNull(paymentInstrument.getFriendlyName());
+
+    PaymentInstrumentCollection myPaymentInstrumentCol = myPaymentInstruments.execute();
+    assertNotNull(myPaymentInstrumentCol);
+    assertFalse(myPaymentInstrumentCol.isEmpty());
+    paymentInstrument = myPaymentInstrumentCol.iterator().next();
+    assertNotNull(paymentInstrument);
+    assertNotNull(paymentInstrument.getFriendlyName());
+  }
+
+  @Test(expected = UnsupportedOperationException.class)
+  public void expandToContainedEntitySetWithUnsupportedOperation() {
+    Account account = container.getAccounts().getByKey(103).expand("MyPaymentInstruments").load();
+    account.getMyPaymentInstruments().delete(101901);
+  }
 
   @Test
   public void readWithReferences() {