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 2015/04/07 08:40:59 UTC

[1/3] olingo-odata4 git commit: [OLINGO-545] TecSvc: Request validation added

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 97a017843 -> d4c2b89e4


[OLINGO-545] TecSvc: Request validation added


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

Branch: refs/heads/master
Commit: 583c4bd07886ce38dae7fa41bcfbd8404d1b6abf
Parents: 97a0178
Author: Christian Holzer <c....@sap.com>
Authored: Mon Apr 6 08:25:00 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Tue Apr 7 08:25:43 2015 +0200

----------------------------------------------------------------------
 .../olingo/fit/tecsvc/client/BasicITCase.java   |  38 +++
 .../olingo/fit/tecsvc/client/BindingITCase.java |  35 ++-
 .../fit/tecsvc/client/DeepInsertITCase.java     | 176 ++++++++++++-
 .../tecsvc/client/FilterSystemQueryITCase.java  |  25 +-
 .../olingo/server/tecsvc/data/DataProvider.java |  26 +-
 .../server/tecsvc/data/RequestValidator.java    | 255 +++++++++++++++++++
 .../processor/TechnicalEntityProcessor.java     |  27 +-
 7 files changed, 548 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
index b7bac00..5b8949a 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
@@ -30,8 +30,10 @@ import static org.junit.Assert.fail;
 import java.net.URI;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 
+import org.apache.olingo.client.api.EdmEnabledODataClient;
 import org.apache.olingo.client.api.ODataClient;
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
 import org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest;
@@ -266,6 +268,7 @@ public class BasicITCase extends AbstractBaseTestITCase {
     ODataEntity newEntity = factory.newEntity(new FullQualifiedName("olingo.odata.test1", "ETAllPrim"));
     newEntity.getProperties().add(factory.newPrimitiveProperty("PropertyInt64",
         factory.newPrimitiveValueBuilder().buildInt32(42)));
+    
     final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(32767)
         .build();
     final ODataEntityUpdateRequest<ODataEntity> request = client.getCUDRequestFactory().getEntityUpdateRequest(
@@ -373,6 +376,12 @@ public class BasicITCase extends AbstractBaseTestITCase {
     ODataEntity newEntity = factory.newEntity(new FullQualifiedName("olingo.odata.test1", "ETAllPrim"));
     newEntity.getProperties().add(factory.newPrimitiveProperty("PropertyInt64",
         factory.newPrimitiveValueBuilder().buildInt32(42)));
+    newEntity.addLink(factory.newEntityNavigationLink("NavPropertyETTwoPrimOne", 
+                          client.newURIBuilder(SERVICE_URI)
+                                .appendEntitySetSegment("ESTwoPrim")
+                                .appendKeySegment(32766)
+                                .build()));
+    
     final ODataEntityCreateRequest<ODataEntity> createRequest = client.getCUDRequestFactory().getEntityCreateRequest(
         client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").build(),
         newEntity);
@@ -518,6 +527,18 @@ public class BasicITCase extends AbstractBaseTestITCase {
                   .add(of.newPrimitiveProperty("PropertyInt16", of.newPrimitiveValueBuilder().buildInt16((short) 2)))
                   .add(of.newPrimitiveProperty("PropertySingle", of.newPrimitiveValueBuilder().buildSingle(2.0f))))))));
     
+    entity.addLink(of.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+        getClient().newURIBuilder(SERVICE_URI)
+            .appendEntitySetSegment("ESTwoKeyNav")
+            .appendKeySegment(new LinkedHashMap<String, Object>() {
+              private static final long serialVersionUID = 1L;
+
+              {
+                put("PropertyInt16", 1);
+                put("PropertyString", "1");
+              }
+            }).build()));
+    
     final ODataEntityCreateResponse<ODataEntity> response = getClient().getCUDRequestFactory().getEntityCreateRequest(
         getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESKeyNav").build(),
         entity).execute();
@@ -576,6 +597,23 @@ public class BasicITCase extends AbstractBaseTestITCase {
     assertNull(innerComplexProperty2.get("NotAvailableProperty"));
   }
   
+  @Test
+  public void testComplexPropertyWithNotNullablePrimitiveValue() {
+    final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+    final ODataObjectFactory of = client.getObjectFactory();
+    
+    // PropertyComp is null, but the primitive values in PropertyComp must not be null
+    final ODataEntity entity = of.newEntity(new FullQualifiedName("olingo.odata.test1", "ETMixPrimCollComp"));
+    final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESMixPrimCollComp").build();
+    
+    try {
+      client.getCUDRequestFactory().getEntityCreateRequest(targetURI, entity).execute();
+      fail("Expecting bad request");
+    } catch (ODataClientErrorException e) {
+      assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+    }
+  }
+  
   @Override
   protected ODataClient getClient() {
     ODataClient odata = ODataClientFactory.getClient();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
index 75cadd0..74b7718 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BindingITCase.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.fail;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 
 import org.apache.olingo.client.api.EdmEnabledODataClient;
 import org.apache.olingo.client.api.ODataClient;
@@ -69,9 +70,9 @@ public class BindingITCase extends AbstractBaseTestITCase {
   private static final String PROPERTY_COMP_ALL_PRIM = "PropertyCompAllPrim";
   private static final String NAV_PROPERTY_ET_KEY_NAV_ONE = "NavPropertyETKeyNavOne";
   private static final String NAV_PROPERTY_ET_KEY_NAV_MANY = "NavPropertyETKeyNavMany";
+  private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_ONE = "NavPropertyETTwoKeyNavOne";
   private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany";
 
-
   @Test
   public void testCreateBindingSimple() throws EdmPrimitiveTypeException {
     final ODataClient client = getClient();
@@ -101,6 +102,17 @@ public class BindingITCase extends AbstractBaseTestITCase {
                 .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 42)))))));
 
     // Bind existing entities via binding synatx
+    entity.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, 
+        client.newURIBuilder(SERVICE_URI)
+              .appendEntitySetSegment(ES_TWO_KEY_NAV)
+              .appendKeySegment(new LinkedHashMap<String, Object>() {
+                private static final long serialVersionUID = 3109256773218160485L;
+                {
+                  put(PROPERTY_INT16, 3);
+                  put(PROPERTY_STRING, "1");
+                }
+              }).build()));
+    
     final ODataLink navLinkOne =
         of.newEntityNavigationLink(NAV_PROPERTY_ET_KEY_NAV_ONE, client.newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment(
@@ -331,10 +343,31 @@ public class BindingITCase extends AbstractBaseTestITCase {
    innerEntity.getProperties().add(of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
         .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 1)))
         .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("2")))));
+   innerEntity.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, 
+       client.newURIBuilder(SERVICE_URI)
+             .appendEntitySetSegment(ES_TWO_KEY_NAV)
+             .appendKeySegment(new LinkedHashMap<String, Object>() {
+               private static final long serialVersionUID = 3109256773218160485L;
+               {
+                 put(PROPERTY_INT16, 3);
+                 put(PROPERTY_STRING, "1");
+               }
+             }).build()));
    
    final ODataInlineEntity inlineLink = of.newDeepInsertEntity(NAV_PROPERTY_ET_KEY_NAV_ONE, innerEntity);
    entity.addLink(inlineLink);
    
+   entity.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, 
+       client.newURIBuilder(SERVICE_URI)
+             .appendEntitySetSegment(ES_TWO_KEY_NAV)
+             .appendKeySegment(new LinkedHashMap<String, Object>() {
+               private static final long serialVersionUID = 3109256773218160485L;
+               {
+                 put(PROPERTY_INT16, 3);
+                 put(PROPERTY_STRING, "1");
+               }
+             }).build()));
+   
    final URI bindingURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV)
                                                            .appendKeySegment(3)
                                                            .build();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
index ba33e3f..5255712 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
@@ -20,6 +20,7 @@ package org.apache.olingo.fit.tecsvc.client;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
 import java.net.URI;
 import java.util.HashMap;
@@ -27,8 +28,12 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.apache.olingo.client.api.EdmEnabledODataClient;
 import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
 import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
 import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
 import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.client.core.ODataClientFactory;
@@ -62,8 +67,10 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
   private static final String CT_TWO_PRIM = "CTTwoPrim";
   private static final String CT_ALL_PRIM = "CTAllPrim";
   private static final String CT_NAV_FIVE_PROP = "CTNavFiveProp";
+  private static final String CT_BASE_PRIM_COMP_NAV = "CTBasePrimCompNav";
   private static final String PROPERTY_INT16 = "PropertyInt16";
   private static final String PROPERTY_STRING = "PropertyString";
+  private static final String PROPERTY_COMP = "PropertyComp";
   private static final String PROPERTY_COMP_NAV = "PropertyCompNav";
   private static final String PROPERTY_COMP_COMP_NAV = "PropertyCompCompNav";
   private static final String PROPERTY_COMP_TWO_PRIM = "PropertyCompTwoPrim";
@@ -71,6 +78,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
   private static final String NAV_PROPERTY_ET_KEY_NAV_ONE = "NavPropertyETKeyNavOne";
   private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_ONE = "NavPropertyETTwoKeyNavOne";
   private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany";
+  private static final String COL_PROPERTY_STRING = "CollPropertyString";
+  private static final String EDM_STRING = "Edm.String";
 
   @Test
   public void testDeepInsertExpandedResponse() {
@@ -96,6 +105,10 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 42)))
             .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
                 "String Property level 1, complex level 1")))));
+    firstLevelTwoKeyNav.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    firstLevelTwoKeyNav.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
     final ODataInlineEntity firstLevelTwoKeyOneInline =
         of.newDeepInsertEntity(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, firstLevelTwoKeyNav);
     entity.addLink(firstLevelTwoKeyOneInline);
@@ -107,7 +120,11 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 421)))
             .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
                 "String Property level 2, complex level 1")))));
-
+    secondLevelTwoKeyNav.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    secondLevelTwoKeyNav.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+    
     // Binding links
     secondLevelTwoKeyNav.addLink(of.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, client.newURIBuilder(
         SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).appendKeySegment(new LinkedHashMap<String, Object>() {
@@ -129,14 +146,22 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 431)))
             .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
                 "String Property level 3, complex level 1")))));
-
+    thirdLevelTwoKeyNavMany1.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    thirdLevelTwoKeyNavMany1.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+    
     final ODataEntity thirdLevelTwoKeyNavMany2 = of.newEntity(ET_TWO_KEY_NAV);
     thirdLevelTwoKeyNavMany2.getProperties().add(
         of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 432)))
             .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
                 "String Property level 3, complex level 1")))));
-
+    thirdLevelTwoKeyNavMany2.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    thirdLevelTwoKeyNavMany2.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+    
     final ODataEntitySet entitySetThirdLevelTwoKeyNavMany = of.newEntitySet();
     entitySetThirdLevelTwoKeyNavMany.getEntities().add(thirdLevelTwoKeyNavMany1);
     entitySetThirdLevelTwoKeyNavMany.getEntities().add(thirdLevelTwoKeyNavMany2);
@@ -150,7 +175,11 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 422)))
             .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString(
                 "String Property level 1, complex level 1")))));
-
+    firstLevelTwoKeyNavMany1.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    firstLevelTwoKeyNavMany1.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+    
     final ODataEntitySet entitySetfirstLevelTwoKeyNavMany = of.newEntitySet();
     entitySetfirstLevelTwoKeyNavMany.getEntities().add(firstLevelTwoKeyNavMany1);
     entity.addLink(of.newDeepInsertEntitySet(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY,
@@ -172,7 +201,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
         resultEntityFirstLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE).asInlineEntity().getEntity();
     assertEquals(421, resultEntitySecondLevel.getProperty(PROPERTY_COMP_TWO_PRIM).getComplexValue().get(PROPERTY_INT16)
         .getPrimitiveValue().toValue());
-    assertEquals("String Property level 2, complex level 1", resultEntitySecondLevel.getProperty(PROPERTY_COMP_TWO_PRIM)
+    assertEquals("String Property level 2, complex level 1", resultEntitySecondLevel
+        .getProperty(PROPERTY_COMP_TWO_PRIM)
         .getComplexValue().get(PROPERTY_STRING)
         .getPrimitiveValue().toValue());
 
@@ -198,7 +228,7 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
     assertEquals("String Property level 1, complex level 1", firstLevelEntitySetNavMany.getEntities().get(0)
         .getProperty(PROPERTY_COMP_TWO_PRIM).getComplexValue().get(PROPERTY_STRING).getPrimitiveValue().toValue());
   }
-  
+
   @Test
   public void testSimpleDeepInsert() throws EdmPrimitiveTypeException {
     final ODataClient client = getClient();
@@ -234,6 +264,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
         .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 43)));
     inlineEntitySingle.getProperties()
         .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("43")));
+    inlineEntitySingle.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
     inlineEntitySingle.getProperties()
         .add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_PRIM_COMP)
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 431)))));
@@ -253,6 +285,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
     inlineEntityCol1.getProperties()
         .add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_PRIM_COMP)
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 441)))));
+    inlineEntityCol1.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
     inlineEntityCol1.getProperties()
         .add(of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 442)))
@@ -266,6 +300,8 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
     inlineEntityCol2.getProperties()
         .add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_PRIM_COMP)
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 451)))));
+    inlineEntityCol2.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
     inlineEntityCol2.getProperties()
         .add(of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
             .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 452)))
@@ -412,6 +448,18 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
             .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("42")))
             .add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_NAV_FIVE_PROP)
                 .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 42)))))));
+    entity.addLink(of.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+        client.newURIBuilder(SERVICE_URI)
+            .appendEntitySetSegment(ES_TWO_KEY_NAV)
+            .appendKeySegment(new LinkedHashMap<String, Object>() {
+              private static final long serialVersionUID = 1L;
+
+              {
+                put(PROPERTY_INT16, 1);
+                put(PROPERTY_STRING, "1");
+              }
+            })
+            .build()));
 
     // Prepare inline entity(EntitySet: ESKeyNav, Type: ETKeyNav)
     final ODataEntity innerEntity = of.newEntity(ET_KEY_NAV);
@@ -436,7 +484,19 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
             .add(of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_NAV_FIVE_PROP)
                 .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder()
                     .buildInt16((short) 431)))))));
-
+    innerEntity.addLink(of.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+        client.newURIBuilder(SERVICE_URI)
+            .appendEntitySetSegment(ES_TWO_KEY_NAV)
+            .appendKeySegment(new LinkedHashMap<String, Object>() {
+              private static final long serialVersionUID = 1L;
+
+              {
+                put(PROPERTY_INT16, 1);
+                put(PROPERTY_STRING, "1");
+              }
+            })
+            .build()));
+    
     ODataInlineEntity inlineEntity = of.newDeepInsertEntity(NAV_PROPERTY_ET_KEY_NAV_ONE, innerEntity);
     entity.addLink(inlineEntity);
 
@@ -475,6 +535,108 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
         .getPrimitiveValue().toValue());
   }
 
+  @Test
+  public void testConsistency() throws EdmPrimitiveTypeException {
+    final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+    final ODataObjectFactory of = client.getObjectFactory();
+    final String cookie = getCookie();
+    
+    // Do not set PropertyString(Nullable=false)
+    final ODataEntity entity = of.newEntity(ET_KEY_NAV);
+    entity.getProperties().add(
+        of.newCollectionProperty(COL_PROPERTY_STRING,
+            of.newCollectionValue(EDM_STRING).add(
+                of.newPrimitiveValueBuilder().buildString("Test"))));
+
+    final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).build();
+
+    try {
+      ODataEntityCreateRequest<ODataEntity> request = client.getCUDRequestFactory()
+          .getEntityCreateRequest(targetURI, entity);
+      request.addCustomHeader(HttpHeader.COOKIE, cookie);
+      request.execute();
+      fail("Expecting bad request");
+    } catch (ODataClientErrorException e) {
+      assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+    }
+
+    // Entity must not be created
+    validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
+  }
+
+  @Test
+  public void testInvalidType() throws EdmPrimitiveTypeException {
+    final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+    final ODataObjectFactory of = client.getObjectFactory();
+    final String cookie = getCookie();
+
+    final ODataEntity entity = of.newEntity(ET_KEY_NAV);
+    entity.getProperties().add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildInt32(1)));
+    final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).build();
+
+    try {
+      ODataEntityCreateRequest<ODataEntity> request = client.getCUDRequestFactory()
+          .getEntityCreateRequest(targetURI, entity);
+      request.addCustomHeader(HttpHeader.COOKIE, cookie);
+      request.execute();
+    } catch (ODataClientErrorException e) {
+      assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+    }
+
+    validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
+
+    entity.getProperties().add(
+        of.newCollectionProperty(PROPERTY_STRING,
+            of.newCollectionValue(EDM_STRING).add(
+                of.newPrimitiveValueBuilder().buildString("Test"))));
+
+    try {
+      ODataEntityCreateRequest<ODataEntity> request = client.getCUDRequestFactory()
+          .getEntityCreateRequest(targetURI, entity);
+      request.addCustomHeader(HttpHeader.COOKIE, cookie);
+      request.execute();
+    } catch (ODataClientErrorException e) {
+      assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
+    }
+
+    validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
+  }
+
+  private String getCookie() {
+    final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+    final ODataRetrieveResponse<ODataEntitySet> response = client.getRetrieveRequestFactory()
+        .getEntitySetRequest(client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_KEY_NAV).build())
+        .execute();
+
+    return response.getHeader(HttpHeader.SET_COOKIE).iterator().next();
+  }
+
+  private void validateSet(final URI uri, final String cookie, final short... keys) throws EdmPrimitiveTypeException {
+    final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+    final ODataEntitySetRequest<ODataEntitySet> request = client.getRetrieveRequestFactory()
+        .getEntitySetRequest(uri);
+    request.addCustomHeader(HttpHeader.COOKIE, cookie);
+    final ODataRetrieveResponse<ODataEntitySet> response = request.execute();
+
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+    assertEquals(3, response.getBody().getEntities().size());
+
+    for (final ODataEntity responseEntity : response.getBody().getEntities()) {
+      short propertyInt16 = responseEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toCastValue(Short.class);
+
+      boolean found = false;
+      for (int i = 0; i < keys.length && !found; i++) {
+        if (propertyInt16 == keys[i]) {
+          found = true;
+        }
+      }
+
+      if (!found) {
+        fail("Invalid key " + propertyInt16);
+      }
+    }
+  }
+
   @Override
   protected ODataClient getClient() {
     ODataClient odata = ODataClientFactory.getClient();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
index 8cb249d..16529dd 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java
@@ -21,6 +21,7 @@ package org.apache.olingo.fit.tecsvc.client;
 import static org.junit.Assert.assertEquals;
 
 import java.net.URI;
+import java.util.LinkedHashMap;
 
 import org.apache.olingo.client.api.ODataClient;
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
@@ -289,7 +290,12 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase {
     entity.getProperties().add(
         objectFactory.newPrimitiveProperty("PropertyInt16", objectFactory.newPrimitiveValueBuilder()
             .buildInt16((short) 1)));
-
+    entity.addLink(objectFactory.newEntityNavigationLink("NavPropertyETTwoPrimOne", 
+        client.newURIBuilder(SERVICE_URI)
+              .appendEntitySetSegment("ESTwoPrim")
+              .appendKeySegment(32766)
+              .build()));
+    
     final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").build();
     ODataEntityCreateResponse<ODataEntity> createResponse =
         client.getCUDRequestFactory().getEntityCreateRequest(uri, entity).execute();
@@ -921,7 +927,7 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase {
   @Test
   public void testNullComplexProperty() {
     // Create a new entry.The complex property PropertyCompComp is set to null. So the structure of the property
-    // is still there, but filled is null value (primitive types)
+    // is still there, but filled is null values (primitive types)
     // We define a filter, which returns all entry where PropertyCompComp/PropertyComp/PropertyInt16 is equals to 1
 
     final ODataClient client = getClient();
@@ -948,7 +954,20 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase {
                 .add(factory.newPrimitiveProperty(
                     "PropertyString",
                     factory.newPrimitiveValueBuilder().buildString("Test2")))));
-
+    
+    newEntity.addLink(factory.newEntityNavigationLink("NavPropertyETTwoKeyNavOne",
+        client.newURIBuilder(SERVICE_URI)
+            .appendEntitySetSegment(ES_TWO_KEY_NAV)
+            .appendKeySegment(new LinkedHashMap<String, Object>() {
+              private static final long serialVersionUID = 1L;
+
+              {
+                put("PropertyInt16", 1);
+                put("PropertyString", "1");
+              }
+            })
+            .build()));
+    
     final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESKeyNav").build();
     ODataEntityCreateRequest<ODataEntity> request =
         client.getCUDRequestFactory().getEntityCreateRequest(uri, newEntity);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
index 96f0cd7..d47aa46 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
@@ -6,9 +6,9 @@
  * 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
@@ -152,7 +152,6 @@ public class DataProvider {
   private Map<String, Object> findFreeComposedKey(final List<Entity> entities, final EdmEntityType entityType)
       throws DataProviderException {
     // Weak key construction
-    // 3e € entity: (V k € keys: k !€ e.ki) => e.(k1, k2, k3) !€ entitySet
     final HashMap<String, Object> keys = new HashMap<String, Object>();
     for (final String keyName : entityType.getKeyPredicateNames()) {
       final EdmType type = entityType.getProperty(keyName).getType();
@@ -243,15 +242,15 @@ public class DataProvider {
             patch);
       }
     }
-    
-    // For insert operations collection navigation property bind operations and deep insert operations can be combined. 
+
+    // For insert operations collection navigation property bind operations and deep insert operations can be combined.
     // In this case, the bind operations MUST appear before the deep insert operations in the payload.
     // => Apply bindings first
     final boolean navigationBindingsAvailable = !changedEntity.getNavigationBindings().isEmpty();
     if (navigationBindingsAvailable) {
       applyNavigationBinding(rawBaseUri, edmEntitySet, entity, changedEntity.getNavigationBindings());
     }
-    
+
     // Deep insert (only if not an update)
     if (isInsert) {
       handleDeepInsert(rawBaseUri, edmEntitySet, entity, changedEntity);
@@ -311,10 +310,6 @@ public class DataProvider {
           .getKeyPredicatesFromEntityLink(edm, bindingLink, rawBaseUri);
       final Entity entity = read(edmEntitySetTarget, keys);
 
-      if (entity == null) {
-        throw new DataProviderException("Entity " + bindingLink + " not found", HttpStatusCode.NOT_FOUND);
-      }
-
       return entity;
     } catch (DeserializerException e) {
       throw new DataProviderException("Invalid entity binding link", HttpStatusCode.BAD_REQUEST);
@@ -400,9 +395,6 @@ public class DataProvider {
     if (edmProperty.isPrimitive()) {
       if (newProperty != null || !patch) {
         final Object value = newProperty == null ? null : newProperty.getValue();
-        if (value == null && !edmProperty.isNullable()) {
-          throw new DataProviderException("Cannot null non-nullable property!", HttpStatusCode.BAD_REQUEST);
-        }
         property.setValue(property.getValueType(), value);
       }
     } else if (edmProperty.isCollection()) {
@@ -440,7 +432,7 @@ public class DataProvider {
   private ComplexValue createComplexValue(final EdmProperty edmProperty, final ComplexValue complexValue,
       final boolean patch) throws DataProviderException {
     final ComplexValueImpl result = new ComplexValueImpl();
-    final EdmComplexType edmType =  (EdmComplexType) edmProperty.getType();
+    final EdmComplexType edmType = (EdmComplexType) edmProperty.getType();
     final List<Property> givenProperties = complexValue.getValue();
 
     // Create ALL properties, even if no value is given. Check if null is allowed
@@ -454,12 +446,10 @@ public class DataProvider {
         updateProperty(innerEdmProperty, newProperty, currentProperty, patch);
       } else {
         if (innerEdmProperty.isNullable()) {
-          // Check complex properties ... may be null is not allowed
-          if(edmProperty.getType().getKind() == EdmTypeKind.COMPLEX)  {
+          // Check complex properties ... maybe null is not allowed
+          if (edmProperty.getType().getKind() == EdmTypeKind.COMPLEX) {
             updateProperty(innerEdmProperty, newProperty, null, patch);
           }
-        } else {
-          throw new DataProviderException("Null is not allowed for property " + edmProperty.getName());
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java
new file mode 100644
index 0000000..113b252
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/RequestValidator.java
@@ -0,0 +1,255 @@
+/*
+ * 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.server.tecsvc.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Linked;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.uri.UriHelper;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.tecsvc.data.DataProvider.DataProviderException;
+
+public class RequestValidator {
+  private DataProvider provider;
+  private boolean isInsert;
+  private UriHelper uriHelper;
+  private Edm edm;
+  private String rawServiceRoot;
+
+  public RequestValidator(final DataProvider provider, final boolean isInsert, final UriHelper uriHelper,
+      final Edm edm, final String rawServiceRoot) {
+    this.provider = provider;
+    this.isInsert = isInsert;
+    this.uriHelper = uriHelper;
+    this.edm = edm;
+    this.rawServiceRoot = rawServiceRoot;
+  }
+
+  public void validate(final EdmBindingTarget edmBindingTarget, final Entity entity) 
+      throws DataProviderException {
+    final List<String> path = new ArrayList<String>();
+    
+    validateEntitySetProperties(entity.getProperties(), edmBindingTarget, edmBindingTarget.getEntityType(), path);
+    validateNavigationProperties(entity, edmBindingTarget, edmBindingTarget.getEntityType(), path); 
+  }
+  
+  private void validateNavigationProperties(final Linked entity, final EdmBindingTarget edmBindingTarget, 
+      final EdmStructuredType edmType, final List<String> path) throws DataProviderException {
+    for (final String navPropertyName : edmType.getNavigationPropertyNames()) {
+      final EdmNavigationProperty edmProperty = edmType.getNavigationProperty(navPropertyName);
+      if(entity == null && !edmProperty.isNullable()) {
+        throw new DataProviderException("Navigation property " + navPropertyName + " must not be null",
+            HttpStatusCode.BAD_REQUEST);
+      } else if(entity != null) {
+        final Link navigationBinding = entity.getNavigationBinding(navPropertyName);
+        final Link navigationLink = entity.getNavigationLink(navPropertyName);
+        final List<String> newPath = new ArrayList<String>(path);
+        newPath.add(edmProperty.getName());
+        final EdmBindingTarget target = edmBindingTarget.getRelatedBindingTarget(buildPath(newPath));
+  
+        final ValidatioResult bindingResult = validateBinding(navigationBinding, edmProperty, target);
+        final ValidatioResult linkResult = validateNavigationLink(navigationLink, 
+                                                                  edmProperty, 
+                                                                  target);
+  
+        if ((   isInsert  && !edmProperty.isNullable() && (bindingResult != ValidatioResult.FOUND 
+                                                        && linkResult != ValidatioResult.FOUND))
+            || (!isInsert && !edmProperty.isNullable() && linkResult == ValidatioResult.EMPTY)) {
+          throw new DataProviderException("Navigation property " + navPropertyName + " must not be null",
+              HttpStatusCode.BAD_REQUEST);
+        }
+      }
+    }
+  }
+  
+  private String buildPath(final List<String> path) {
+    final StringBuilder builder = new StringBuilder();
+    
+    for(final String segment : path) {
+      if(builder.length() > 0) {
+        builder.append("/");
+      }
+      
+      builder.append(segment);
+    }
+    
+    return builder.toString();
+  }
+
+  private ValidatioResult validateBinding(final Link navigationBindung, final EdmNavigationProperty edmProperty, 
+      final EdmBindingTarget edmBindingTarget) throws DataProviderException {
+    if(navigationBindung == null) {
+      return ValidatioResult.NOT_FOUND;
+    }
+    
+    if (edmProperty.isCollection()) {
+      if(navigationBindung.getBindingLinks().size() == 0) {
+        return ValidatioResult.EMPTY;
+      }
+      
+      for (final String bindingLink : navigationBindung.getBindingLinks()) {
+        validateLink(bindingLink, edmBindingTarget);
+      }
+    } else {
+      if(navigationBindung.getBindingLink() == null) {
+        return ValidatioResult.EMPTY;
+      }
+      
+      validateLink(navigationBindung.getBindingLink(), edmBindingTarget);
+    }
+    
+    return ValidatioResult.FOUND;
+  }
+  
+  private ValidatioResult validateNavigationLink(final Link navigationLink, final EdmNavigationProperty edmProperty, 
+      final EdmBindingTarget edmBindingTarget) throws DataProviderException {
+    if(navigationLink == null) {
+      return ValidatioResult.NOT_FOUND;
+    }
+    
+    if(edmProperty.isCollection()) {
+      final EntitySet inlineEntitySet = navigationLink.getInlineEntitySet();
+      if(!isInsert && inlineEntitySet.getEntities().size() > 0) {
+        throw new DataProvider.DataProviderException("Deep update is not allowed", HttpStatusCode.BAD_REQUEST);
+      } else {
+        for(final Entity entity : navigationLink.getInlineEntitySet().getEntities()) {
+          validate(edmBindingTarget, entity);
+        }
+      }
+    } else {
+      final Entity inlineEntity = navigationLink.getInlineEntity();
+      if(!isInsert && inlineEntity != null) {
+        throw new DataProvider.DataProviderException("Deep update is not allowed", HttpStatusCode.BAD_REQUEST);
+      } else if(inlineEntity != null) {
+        validate(edmBindingTarget, navigationLink.getInlineEntity());
+      }
+    }
+    
+    return ValidatioResult.FOUND;
+  }
+  
+  private void validateLink(final String bindingLink, final EdmBindingTarget edmBindungTarget) 
+      throws DataProviderException {
+    try {
+      final List<UriParameter> keys = uriHelper.getKeyPredicatesFromEntityLink(edm, bindingLink, rawServiceRoot);
+      final Entity entity = provider.read((EdmEntitySet)edmBindungTarget, keys);
+
+      if (entity == null) {
+        throw new DataProviderException("Entity not found", HttpStatusCode.NOT_FOUND);
+      }
+    } catch (DeserializerException e) {
+      throw new DataProviderException("Invalid binding link", HttpStatusCode.BAD_REQUEST);
+    }
+  }
+
+  private void validateEntitySetProperties(final List<Property> properties, final EdmBindingTarget edmBindingTarget, 
+      final EdmEntityType edmType, final List<String> path) throws DataProviderException {
+    validateProperties(properties, edmBindingTarget, edmType, edmType.getKeyPredicateNames(), path);
+  }
+  
+  private void validateProperties(final List<Property> properties, final EdmBindingTarget edmBingingTarget, 
+      final EdmStructuredType edmType, final List<String> keyPredicateNames, final List<String> path) 
+          throws DataProviderException {
+    
+    for(final String propertyName : edmType.getPropertyNames()) {
+      final EdmProperty edmProperty = (EdmProperty) edmType.getProperty(propertyName);
+      
+      // Ignore key properties, they are set automatically
+      if(!keyPredicateNames.contains(propertyName)) {
+        final Property property = getProperty(properties, propertyName);
+        
+        // Check if all "not nullable" properties are set
+        if(!edmProperty.isNullable()) {
+          if((property != null && property.isNull())    // Update,insert; Property is explicit set to null
+            || (isInsert && property == null) ) {       // Insert; Property not provided
+            throw new DataProviderException("Property " + propertyName + " must not be null", 
+                HttpStatusCode.BAD_REQUEST);
+          }
+        }
+        
+        // Validate property value
+        validatePropertyValue(property, edmProperty, edmBingingTarget, path);
+      }
+    }
+  }
+  
+  private void validatePropertyValue(final Property property, final EdmProperty edmProperty, 
+      final EdmBindingTarget edmBindingTarget, final List<String> path) throws DataProviderException {
+    
+    final ArrayList<String> newPath = new ArrayList<String>(path);
+    newPath.add(edmProperty.getName());
+
+    if (edmProperty.isCollection()) {
+      if (edmProperty.getType() instanceof EdmComplexType && property != null) {
+        for (final Object value : property.asCollection()) {
+          validateComplexValue((ComplexValue) value, 
+                               edmBindingTarget, 
+                               (EdmComplexType) edmProperty.getType(), 
+                               newPath);
+        }
+      }
+    } else if (edmProperty.getType() instanceof EdmComplexType) {
+      validateComplexValue((property == null) ? null : property.asComplex(), 
+                           edmBindingTarget,
+                           (EdmComplexType) edmProperty.getType(), 
+                           newPath);
+    }
+  }
+
+  private void validateComplexValue(final ComplexValue value, final EdmBindingTarget edmBindingTarget, 
+      final EdmComplexType edmType, final List<String> path) throws DataProviderException {
+    // The whole complex property can be nullable but nested primitive, navigation properties can be not nullable
+    final List<Property> properties = (value == null) ? new ArrayList<Property>() : value.getValue();
+
+    validateProperties(properties, edmBindingTarget, edmType, new ArrayList<String>(0), path);
+    validateNavigationProperties(value, edmBindingTarget, edmType, path);
+  }
+
+  private Property getProperty(final List<Property> properties, final String name) {
+    for(final Property property : properties) {
+      if(property.getName().equals(name)) {
+        return property;
+      }
+    }
+    
+    return null;
+  }
+  
+  private static enum ValidatioResult {
+    FOUND,
+    NOT_FOUND,
+    EMPTY
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/583c4bd0/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index e105a2f..ed120b9 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -60,6 +60,7 @@ import org.apache.olingo.server.api.uri.UriResourceFunction;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.tecsvc.data.DataProvider;
+import org.apache.olingo.server.tecsvc.data.RequestValidator;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.ExpandSystemQueryOptionHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.CountHandler;
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.FilterHandler;
@@ -230,17 +231,25 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
     final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0);
     final EdmEntitySet edmEntitySet = resourceEntitySet.getEntitySet();
     final EdmEntityType edmEntityType = edmEntitySet.getEntityType();
-
-    Entity entity = dataProvider.create(edmEntitySet);
+    
+    final Entity entity;
     ExpandOption expand = null;
     if (edmEntityType.hasStream()) { // called from createMediaEntity(...), not directly
+      entity = dataProvider.create(edmEntitySet);
       dataProvider.setMedia(entity, odata.createFixedFormatDeserializer().binary(request.getBody()),
           requestFormat.toContentTypeString());
     } else {
       final DeserializerResult deserializerResult = odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
                                                          .entity(request.getBody(), edmEntityType);
-      dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity,
-          deserializerResult.getEntity(), false, true);
+      new RequestValidator(dataProvider, 
+                           true,        // Insert
+                           odata.createUriHelper(), 
+                           serviceMetadata.getEdm(), 
+                           request.getRawBaseUri()
+                           ).validate(edmEntitySet, deserializerResult.getEntity());
+      
+      entity = dataProvider.create(edmEntitySet);
+      dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, deserializerResult.getEntity(), false, true);
       expand = deserializerResult.getExpandTree();
     }
 
@@ -265,8 +274,16 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
     final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo);
     Entity entity = readEntity(uriInfo);
     checkRequestFormat(requestFormat);
-    ODataDeserializer deserializer = odata.createDeserializer(ODataFormat.fromContentType(requestFormat));
+    final ODataDeserializer deserializer = odata.createDeserializer(ODataFormat.fromContentType(requestFormat));
     final Entity changedEntity = deserializer.entity(request.getBody(), edmEntitySet.getEntityType()).getEntity();
+    
+    new RequestValidator(dataProvider, 
+                         false,        // Update
+                         odata.createUriHelper(), 
+                         serviceMetadata.getEdm(), 
+                         request.getRawBaseUri()
+                         ).validate(edmEntitySet, changedEntity);
+    
     dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, changedEntity,
         request.getMethod() == HttpMethod.PATCH, false);
     response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());


[3/3] olingo-odata4 git commit: [OLINGO-545] TecSvc: Test added for deep insets to navigation properties in complex values

Posted by ch...@apache.org.
[OLINGO-545] TecSvc: Test added for deep insets to navigation properties in complex values


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

Branch: refs/heads/master
Commit: d4c2b89e46dcb067272ea370ae4d965c48f101b1
Parents: 518a3a4
Author: Christian Holzer <c....@sap.com>
Authored: Mon Apr 6 08:25:00 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Tue Apr 7 08:26:17 2015 +0200

----------------------------------------------------------------------
 .../fit/tecsvc/client/DeepInsertITCase.java     | 62 +++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d4c2b89e/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
index 5255712..22f56f5 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/DeepInsertITCase.java
@@ -20,6 +20,7 @@ package org.apache.olingo.fit.tecsvc.client;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.net.URI;
@@ -37,10 +38,12 @@ import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySe
 import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
 import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.client.core.ODataClientFactory;
+import org.apache.olingo.commons.api.domain.ODataComplexValue;
 import org.apache.olingo.commons.api.domain.ODataEntity;
 import org.apache.olingo.commons.api.domain.ODataEntitySet;
 import org.apache.olingo.commons.api.domain.ODataInlineEntity;
 import org.apache.olingo.commons.api.domain.ODataInlineEntitySet;
+import org.apache.olingo.commons.api.domain.ODataLink;
 import org.apache.olingo.commons.api.domain.ODataObjectFactory;
 import org.apache.olingo.commons.api.domain.ODataProperty;
 import org.apache.olingo.commons.api.domain.ODataValue;
@@ -79,6 +82,7 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
   private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_ONE = "NavPropertyETTwoKeyNavOne";
   private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany";
   private static final String COL_PROPERTY_STRING = "CollPropertyString";
+  private static final String COL_PROPERTY_COMP_NAV = "CollPropertyCompNav";
   private static final String EDM_STRING = "Edm.String";
 
   @Test
@@ -601,7 +605,63 @@ public class DeepInsertITCase extends AbstractBaseTestITCase {
 
     validateSet(targetURI, cookie, (short) 1, (short) 2, (short) 3);
   }
-
+  
+  @Test
+  @org.junit.Ignore
+  public void testDeepInsertOnNavigationPropertyInComplexProperty() {
+    final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
+    final ODataObjectFactory of = client.getObjectFactory();
+    
+    final ODataEntity inlineEntity = of.newEntity(ET_TWO_KEY_NAV);
+    inlineEntity.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    inlineEntity.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+    inlineEntity.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
+            .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 1)))
+            .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("1")))));
+    
+    final ODataEntity entity = of.newEntity(ET_TWO_KEY_NAV);
+    entity.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP, of.newComplexValue(CT_PRIM_COMP)));
+    entity.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_NAV, of.newComplexValue(CT_BASE_PRIM_COMP_NAV)));
+    entity.getProperties().add(
+        of.newComplexProperty(PROPERTY_COMP_TWO_PRIM, of.newComplexValue(CT_TWO_PRIM)
+            .add(of.newPrimitiveProperty(PROPERTY_INT16, of.newPrimitiveValueBuilder().buildInt16((short) 2)))
+            .add(of.newPrimitiveProperty(PROPERTY_STRING, of.newPrimitiveValueBuilder().buildString("2")))));
+    
+    final ODataLink link = of.newDeepInsertEntity(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE, inlineEntity);
+    final ODataComplexValue complexValueCreate = of.newComplexValue(CT_NAV_FIVE_PROP);
+    complexValueCreate.getNavigationLinks().add(link);
+    
+    entity.getProperties().add(
+        of.newCollectionProperty(COL_PROPERTY_COMP_NAV, of.newCollectionValue(CT_NAV_FIVE_PROP)
+                                                          .add(complexValueCreate)));
+    
+    final URI targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).build();
+    final ODataEntityCreateResponse<ODataEntity> response = client.getCUDRequestFactory()
+                                                                  .getEntityCreateRequest(targetURI, entity)
+                                                                  .execute();
+    
+    assertEquals(HttpStatusCode.CREATED.getStatusCode(), response.getStatusCode());
+    final Iterator<ODataValue> iter = response.getBody()
+                                              .getProperty(COL_PROPERTY_COMP_NAV)
+                                              .getCollectionValue()
+                                              .iterator();
+    
+    assertTrue(iter.hasNext());
+    final ODataComplexValue complexValue = iter.next().asComplex();
+    final ODataLink linkedEntity = complexValue.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_ONE);
+    assertNotNull(linkedEntity);
+    assertEquals(1, linkedEntity.asInlineEntity()
+                                .getEntity()
+                                .getProperty(PROPERTY_INT16)
+                                .getPrimitiveValue()
+                                .toValue());
+  }
+  
   private String getCookie() {
     final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI);
     final ODataRetrieveResponse<ODataEntitySet> response = client.getRetrieveRequestFactory()


[2/3] olingo-odata4 git commit: [OLINGO-545] TecSvc: Support of cyclic expands and filtering on arbitrary level

Posted by ch...@apache.org.
[OLINGO-545] TecSvc: Support of cyclic expands and filtering on arbitrary
level


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

Branch: refs/heads/master
Commit: 518a3a418e9e79e2a3ce5feb61e175083812c2d8
Parents: 583c4bd
Author: Christian Holzer <c....@sap.com>
Authored: Mon Apr 6 08:25:00 2015 +0200
Committer: Christian Holzer <c....@sap.com>
Committed: Tue Apr 7 08:26:05 2015 +0200

----------------------------------------------------------------------
 .../ExpandWithSystemQueryOptionsITCase.java     | 160 +++++++++++++-
 .../processor/TechnicalEntityProcessor.java     |   6 +-
 .../ExpandSystemQueryOptionHandler.java         | 208 +++++++++++++------
 .../expression/ExpressionVisitorImpl.java       |  10 +-
 .../queryoptions/options/FilterHandler.java     |   6 +-
 .../queryoptions/options/OrderByHandler.java    |  12 +-
 6 files changed, 320 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
index 2db9535..1a6b411 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.fit.tecsvc.client;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
 import java.net.URI;
@@ -119,7 +120,7 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractBaseTestITCase {
       }
     }
   }
-
+  
   @Test
   public void testSkip() {
     final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>();
@@ -290,7 +291,162 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractBaseTestITCase {
     assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
     assertEquals(4, entities.size());
   }
-
+  
+  @Test
+  public void testCyclicExpand() {
+    // Expand entity in the following order
+    // 1 => 2 => 1
+    // Entity with Key (PropertyInt16=1, PrroperyString='1') holds references to (PropertyInt16=1, PropertyString='1') 
+    // and (PropertyInt16=1, PropertyString='2')
+    // Entity with Key (PropertyInt16=1, PropertyString='2') holds references to (PropertyInt16=1, PropertyString='1')
+    // Define filters to select explicit the entities at any level => Circle
+    
+    final ODataClient client = getClient();
+    final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>();
+    options.put(QueryOption.EXPAND, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY 
+                                  + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY 
+                                  + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY + "))");
+    options.put(QueryOption.FILTER, "PropertyString eq '2'");
+    
+    final Map<String, Object> keys = new HashMap<String, Object>();
+    keys.put(PROPERTY_INT16, 1);
+    keys.put(PROPERTY_STRING, "1");
+    
+    final URI uri = client.newURIBuilder(SERVICE_URI)
+                          .appendEntitySetSegment(ES_TWO_KEY_NAV)
+                          .appendKeySegment(keys)
+                          .expandWithOptions(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options)
+                          .build();
+    
+    final ODataRetrieveResponse<ODataEntity> response = client.getRetrieveRequestFactory()
+                                                              .getEntityRequest(uri)
+                                                              .execute();
+    
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+    assertNotNull(response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+    assertEquals(1, response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                      .asInlineEntitySet()
+                                      .getEntitySet()
+                                      .getEntities()
+                                      .size());
+    
+    final ODataEntity entitySecondLevel = response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                                            .asInlineEntitySet()
+                                                            .getEntitySet()
+                                                            .getEntities()
+                                                            .get(0);
+    
+    assertEquals(1, entitySecondLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("2", entitySecondLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+    
+    assertNotNull(entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+    assertEquals(1, entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                     .asInlineEntitySet()
+                                     .getEntitySet()
+                                     .getEntities()
+                                     .size());
+    
+    final ODataEntity entityThirdLevel = entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                                          .asInlineEntitySet()
+                                                          .getEntitySet()
+                                                          .getEntities()
+                                                          .get(0);
+    
+    assertEquals(1, entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("1", entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+    
+    assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+    assertEquals(2, entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                    .asInlineEntitySet()
+                                    .getEntitySet()
+                                    .getEntities()
+                                    .size());
+    
+    final List<ODataEntity> fourthLevelEntites = entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                                                 .asInlineEntitySet()
+                                                                 .getEntitySet()
+                                                                 .getEntities();
+    
+    assertEquals(1, fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("1", fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+
+    assertEquals(1, fourthLevelEntites.get(1).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("2", fourthLevelEntites.get(1).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+  }
+  
+  @Test
+  public void testSystemQueryOptionOnThirdLevel() {
+    final ODataClient client = getClient();
+    final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>();
+    options.put(QueryOption.EXPAND, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY 
+                                  + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY 
+                                  + "($expand=" + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY 
+                                  + ";$filter=PropertyString eq '1'))");
+    options.put(QueryOption.FILTER, "PropertyString eq '2'");
+    
+    final Map<String, Object> keys = new HashMap<String, Object>();
+    keys.put(PROPERTY_INT16, 1);
+    keys.put(PROPERTY_STRING, "1");
+    
+    final URI uri = client.newURIBuilder(SERVICE_URI)
+                          .appendEntitySetSegment(ES_TWO_KEY_NAV)
+                          .appendKeySegment(keys)
+                          .expandWithOptions(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options)
+                          .build();
+    
+    final ODataRetrieveResponse<ODataEntity> response = client.getRetrieveRequestFactory()
+                                                              .getEntityRequest(uri)
+                                                              .execute();
+    
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+    assertNotNull(response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+    assertEquals(1, response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                      .asInlineEntitySet()
+                                      .getEntitySet()
+                                      .getEntities()
+                                      .size());
+    
+    final ODataEntity entitySecondLevel = response.getBody().getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                                            .asInlineEntitySet()
+                                                            .getEntitySet()
+                                                            .getEntities()
+                                                            .get(0);
+    
+    assertEquals(1, entitySecondLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("2", entitySecondLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+    
+    assertNotNull(entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+    assertEquals(1, entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                     .asInlineEntitySet()
+                                     .getEntitySet()
+                                     .getEntities()
+                                     .size());
+    
+    final ODataEntity entityThirdLevel = entitySecondLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                                          .asInlineEntitySet()
+                                                          .getEntitySet()
+                                                          .getEntities()
+                                                          .get(0);
+    
+    assertEquals(1, entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("1", entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+    
+    assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY));
+    assertEquals(1, entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                    .asInlineEntitySet()
+                                    .getEntitySet()
+                                    .getEntities()
+                                    .size());
+    
+    final List<ODataEntity> fourthLevelEntites = entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)
+                                                                 .asInlineEntitySet()
+                                                                 .getEntitySet()
+                                                                 .getEntities();
+    
+    assertEquals(1, fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
+    assertEquals("1", fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue());
+  }
+  
   private ODataRetrieveResponse<ODataEntitySet> buildRequest(final String entitySet, final String navigationProperty,
       final Map<QueryOption, Object> expandOptions) {
     return buildRequest(entitySet, navigationProperty, expandOptions, null);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index ed120b9..a2774e3 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -125,7 +125,9 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
       // Create a shallow copy of each entity. So the expanded navigation properties can be modified for serialization,
       // without affecting the data stored in the database.
       final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
-      final EntitySet entitySetSerialization = expandHandler.copyEntitySetShallowRekursive(entitySet);
+      final EntitySet entitySetSerialization = expandHandler.transformEntitySetGraphToTree(entitySet, 
+                                                                                           edmEntitySet, 
+                                                                                           expand);
       expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand);
 
       // Serialize
@@ -186,7 +188,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
     final SelectOption select = uriInfo.getSelectOption();
 
     final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
-    final Entity entitySerialization = expandHandler.copyEntityShallowRekursive(entity);
+    final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand);
     expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand);
 
     response.setContent(serializer.entity(

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
index 3ae3b3a..ce1b774 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java
@@ -18,15 +18,18 @@
  */
 package org.apache.olingo.server.tecsvc.processor.queryoptions;
 
-import java.util.IdentityHashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntitySet;
 import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.commons.core.data.EntityImpl;
 import org.apache.olingo.commons.core.data.EntitySetImpl;
@@ -47,8 +50,6 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.options.SkipHandle
 import org.apache.olingo.server.tecsvc.processor.queryoptions.options.TopHandler;
 
 public class ExpandSystemQueryOptionHandler {
-  private IdentityHashMap<Entity, Entity> copiedEntities = new IdentityHashMap<Entity, Entity>();
-  private IdentityHashMap<EntitySet, EntitySet> copiedEntitySets = new IdentityHashMap<EntitySet, EntitySet>();
 
   public void applyExpandQueryOptions(final EntitySet entitySet, final EdmEntitySet edmEntitySet,
       final ExpandOption expandOption) throws ODataApplicationException {
@@ -70,20 +71,26 @@ public class ExpandSystemQueryOptionHandler {
     applyExpandOptionToEntity(entity, edmEntitySet, expandOption);
   }
 
-  private void applyExpandOptionToEntity(final Entity entity, final EdmEntitySet edmEntitySet,
+  private void applyExpandOptionToEntity(final Entity entity, final EdmBindingTarget edmBindingTarget,
       final ExpandOption expandOption) throws ODataApplicationException {
-    final EdmEntityType entityType = edmEntitySet.getEntityType();
+    final EdmEntityType entityType = edmBindingTarget.getEntityType();
 
     for (ExpandItem item : expandOption.getExpandItems()) {
       final List<UriResource> uriResourceParts = item.getResourcePath().getUriResourceParts();
       if (uriResourceParts.size() == 1 && uriResourceParts.get(0) instanceof UriResourceNavigation) {
         final String navPropertyName = ((UriResourceNavigation) uriResourceParts.get(0)).getProperty().getName();
-        final EdmEntitySet targetEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(navPropertyName);
+        final EdmBindingTarget targetEdmEntitySet = edmBindingTarget.getRelatedBindingTarget(navPropertyName);
 
         final Link link = entity.getNavigationLink(navPropertyName);
         if (link != null && entityType.getNavigationProperty(navPropertyName).isCollection()) {
-          applyOptionsToEntityCollection(link.getInlineEntitySet(), targetEdmEntitySet, item.getFilterOption(),
-              item.getOrderByOption(), item.getCountOption(), item.getSkipOption(), item.getTopOption());
+          applyOptionsToEntityCollection(link.getInlineEntitySet(), 
+                                         targetEdmEntitySet, 
+                                         item.getFilterOption(),
+                                         item.getOrderByOption(), 
+                                         item.getCountOption(), 
+                                         item.getSkipOption(), 
+                                         item.getTopOption(), 
+                                         item.getExpandOption());
         }
       } else {
         throw new ODataApplicationException("Not supported resource part in expand system query option",
@@ -92,77 +99,150 @@ public class ExpandSystemQueryOptionHandler {
     }
   }
 
-  private void applyOptionsToEntityCollection(final EntitySet entitySet, final EdmEntitySet edmEntitySet,
+  private void applyOptionsToEntityCollection(final EntitySet entitySet, final EdmBindingTarget edmBindingTarget,
       final FilterOption filterOption, final OrderByOption orderByOption, final CountOption countOption,
-      final SkipOption skipOption, final TopOption topOption) throws ODataApplicationException {
+      final SkipOption skipOption, final TopOption topOption, final ExpandOption expandOption) 
+          throws ODataApplicationException {
 
-    FilterHandler.applyFilterSystemQuery(filterOption, entitySet, edmEntitySet);
-    OrderByHandler.applyOrderByOption(orderByOption, entitySet, edmEntitySet);
+    FilterHandler.applyFilterSystemQuery(filterOption, entitySet, edmBindingTarget);
+    OrderByHandler.applyOrderByOption(orderByOption, entitySet, edmBindingTarget);
     // TODO Add CountHandler
     SkipHandler.applySkipSystemQueryHandler(skipOption, entitySet);
     TopHandler.applyTopSystemQueryOption(topOption, entitySet);
+    
+    // Apply nested expand system query options to remaining entities
+    if(expandOption != null) {
+      for(final Entity entity : entitySet.getEntities()) {
+        applyExpandOptionToEntity(entity, edmBindingTarget, expandOption);
+      }
+    }
   }
 
-  public EntitySet copyEntitySetShallowRekursive(final EntitySet entitySet) {
-    if (!copiedEntitySets.containsKey(entitySet)) {
-      final EntitySet copiedEntitySet = new EntitySetImpl();
-      copiedEntitySet.setCount(entitySet.getCount());
-      copiedEntitySet.setDeltaLink(entitySet.getDeltaLink());
-      copiedEntitySet.setNext(entitySet.getNext());
-
-      copiedEntitySets.put(entitySet, copiedEntitySet);
-      copiedEntitySets.put(copiedEntitySet, copiedEntitySet);
-
-      for (Entity entity : entitySet.getEntities()) {
-        copiedEntitySet.getEntities().add(copyEntityShallowRekursive(entity));
-      }
-      return copiedEntitySet;
+  public EntitySet transformEntitySetGraphToTree(final EntitySet entitySet, final EdmBindingTarget edmBindingTarget, 
+      final ExpandOption expand) throws ODataApplicationException {
+    
+    final EntitySet newEntitySet = newEntitySet(entitySet);
+    
+    for(final Entity entity : entitySet.getEntities()) {
+      newEntitySet.getEntities().add(transformEntityGraphToTree(entity, edmBindingTarget, expand));
     }
-    return copiedEntitySets.get(entitySet);
+    
+    return newEntitySet;
   }
+  
+  public Entity transformEntityGraphToTree(final Entity entity, EdmBindingTarget edmEntitySet, 
+      final ExpandOption expand) throws ODataApplicationException {
 
-  public Entity copyEntityShallowRekursive(final Entity entity) {
-    if (!copiedEntities.containsKey(entity)) {
-      final Entity copiedEntity = new EntityImpl();
-      copiedEntity.getProperties().addAll(entity.getProperties());
-      copiedEntity.getAnnotations().addAll(entity.getAnnotations());
-      copiedEntity.getAssociationLinks().addAll(entity.getAssociationLinks());
-      copiedEntity.setEditLink(entity.getEditLink());
-      copiedEntity.setId(entity.getId());
-      copiedEntity.setMediaContentSource(entity.getMediaContentSource());
-      copiedEntity.setMediaContentType(entity.getMediaContentType());
-      copiedEntity.setMediaETag(entity.getMediaETag());
-      copiedEntity.getOperations().addAll(entity.getOperations());
-      copiedEntity.setSelfLink(entity.getSelfLink());
-      copiedEntity.setType(entity.getType());
-      copiedEntity.getNavigationBindings().addAll(entity.getNavigationBindings());
-
-      copiedEntities.put(entity, copiedEntity);
-      copiedEntities.put(copiedEntity, copiedEntity);
-
-      // The system query options change the amount and sequence of inline entities (feeds)
-      // So we have to make a shallow copy of all navigation link lists
-      // Make sure, that each entity is only copied once.
-      // Otherwise an infinite loop can occur caused by cyclic navigation relationships.
+    final Entity newEntity = newEntity(entity);
 
+    if (hasExpandItems(expand)) {
+      final boolean expandAll = expandAll(expand);
+      final Set<String> expanded = expandAll ? null : getExpandedPropertyNames(expand.getExpandItems());
+      final EdmEntityType edmType = edmEntitySet.getEntityType();
+      
       for (final Link link : entity.getNavigationLinks()) {
-        final Link newLink = new LinkImpl();
-        newLink.setMediaETag(link.getMediaETag());
-        newLink.setTitle(link.getTitle());
-        newLink.setType(link.getType());
-        newLink.setRel(link.getRel());
-
-        final EntitySet inlineEntitySet = link.getInlineEntitySet();
-        if (inlineEntitySet != null) {
-          newLink.setInlineEntitySet(copyEntitySetShallowRekursive(inlineEntitySet));
-        } else if (link.getInlineEntity() != null) {
-          newLink.setInlineEntity(copyEntityShallowRekursive(link.getInlineEntity()));
+        final String propertyName = link.getTitle();
+        
+        if (expandAll || expanded.contains(propertyName)) {
+          final EdmNavigationProperty edmNavigationProperty = edmType.getNavigationProperty(propertyName);
+          final EdmBindingTarget edmBindingTarget = edmEntitySet.getRelatedBindingTarget(propertyName);
+          final Link newLink = newLink(link);
+          newEntity.getNavigationLinks().add(newLink);
+          final ExpandOption innerExpandOption = getInnerExpandOption(expand, propertyName);
+          
+          if(edmNavigationProperty.isCollection()) {
+            newLink.setInlineEntitySet(transformEntitySetGraphToTree(link.getInlineEntitySet(), 
+                                                                     edmBindingTarget, 
+                                                                     innerExpandOption));
+          } else {
+            newLink.setInlineEntity(transformEntityGraphToTree(link.getInlineEntity(), 
+                                                               edmBindingTarget, 
+                                                               innerExpandOption));  
+          }
         }
-        copiedEntity.getNavigationLinks().add(newLink);
       }
+      
+    }
+    return newEntity;
+  }
+  
+  public EntitySet newEntitySet(final EntitySet entitySet) {
+    final EntitySet newEntitySet = new EntitySetImpl();
+    newEntitySet.setCount(entitySet.getCount());
+    newEntitySet.setDeltaLink(entitySet.getDeltaLink());
+    newEntitySet.setNext(entitySet.getNext());
+    
+    return newEntitySet;
+  }
+  
+  private Entity newEntity(final Entity entity) {
+    final Entity newEntity = new EntityImpl();
+    
+    newEntity.getProperties().addAll(entity.getProperties());
+    newEntity.getAnnotations().addAll(entity.getAnnotations());
+    newEntity.getAssociationLinks().addAll(entity.getAssociationLinks());
+    newEntity.setEditLink(entity.getEditLink());
+    newEntity.setId(entity.getId());
+    newEntity.setMediaContentSource(entity.getMediaContentSource());
+    newEntity.setMediaContentType(entity.getMediaContentType());
+    newEntity.setMediaETag(entity.getMediaETag());
+    newEntity.getOperations().addAll(entity.getOperations());
+    newEntity.setSelfLink(entity.getSelfLink());
+    newEntity.setType(entity.getType());
+    newEntity.getNavigationBindings().addAll(entity.getNavigationBindings());
+    
+    return newEntity;
+  }
+  
+  private Link newLink(Link link) {
+    final Link newLink = new LinkImpl();
+    newLink.setMediaETag(link.getMediaETag());
+    newLink.setTitle(link.getTitle());
+    newLink.setType(link.getType());
+    newLink.setRel(link.getRel());
+    
+    return newLink;
+  }
+  
+  private boolean hasExpandItems(ExpandOption expand) {
+    return expand != null && expand.getExpandItems() != null && !expand.getExpandItems().isEmpty();
+  }
+  
+  private boolean expandAll(ExpandOption expand) {
+    for (final ExpandItem item : expand.getExpandItems()) {
+      if (item.isStar()) {
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  private Set<String> getExpandedPropertyNames(List<ExpandItem> expandItems) throws ODataApplicationException {
+    Set<String> expanded = new HashSet<String>();
+    for (final ExpandItem item : expandItems) {
+      final List<UriResource> resourceParts = item.getResourcePath().getUriResourceParts();
+      if (resourceParts.size() == 1) {
+        final UriResource resource = resourceParts.get(0);
+        if (resource instanceof UriResourceNavigation) {
+          expanded.add(((UriResourceNavigation) resource).getProperty().getName());
+        }
+      } else {
+        throw new ODataApplicationException("Expand is not supported within complex properties.",
+            HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+      }
+    }
+    return expanded;
+  }
 
-      return copiedEntity;
+  private ExpandOption getInnerExpandOption(final ExpandOption expand, final String propertyName) {
+    for(final ExpandItem item : expand.getExpandItems()) {
+      final UriResource resource = item.getResourcePath().getUriResourceParts().get(0);
+      if(resource instanceof UriResourceNavigation 
+          && propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) {
+        return item.getExpandOption();
+      }
     }
-    return copiedEntities.get(entity);
+    
+    return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
index 3a91f49..ab0eca3 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java
@@ -23,8 +23,8 @@ import java.util.Locale;
 
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
 import org.apache.olingo.commons.api.edm.EdmComplexType;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEnumType;
 import org.apache.olingo.commons.api.edm.EdmProperty;
 import org.apache.olingo.commons.api.edm.EdmType;
@@ -49,11 +49,11 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operati
 public class ExpressionVisitorImpl implements ExpressionVisitor<VisitorOperand> {
 
   final private Entity entity;
-  final private EdmEntitySet edmEntitySet;
+  final private EdmBindingTarget bindingTarget;
 
-  public ExpressionVisitorImpl(Entity entity, EdmEntitySet edmEntitySet) {
+  public ExpressionVisitorImpl(Entity entity, EdmBindingTarget bindingTarget) {
     this.entity = entity;
-    this.edmEntitySet = edmEntitySet;
+    this.bindingTarget = bindingTarget;
   }
 
   @Override
@@ -183,7 +183,7 @@ public class ExpressionVisitorImpl implements ExpressionVisitor<VisitorOperand>
     Property currentProperty = entity.getProperty(uriResourceParts.get(0).toString());
     EdmType currentType = ((UriResourcePartTyped) uriResourceParts.get(0)).getType();
 
-    EdmProperty currentEdmProperty = edmEntitySet.getEntityType()
+    EdmProperty currentEdmProperty = bindingTarget.getEntityType()
         .getStructuralProperty(uriResourceParts.get(0).toString());
 
     for (int i = 1; i < uriResourceParts.size(); i++) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
index f1ba4ee..d4068a1 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/FilterHandler.java
@@ -23,7 +23,7 @@ import java.util.Locale;
 
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.commons.core.edm.primitivetype.EdmBoolean;
 import org.apache.olingo.server.api.ODataApplicationException;
@@ -53,8 +53,8 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operand
 
 public class FilterHandler {
 
-  public static void applyFilterSystemQuery(FilterOption filterOption, EntitySet entitySet, EdmEntitySet edmEntitySet)
-      throws ODataApplicationException {
+  public static void applyFilterSystemQuery(FilterOption filterOption, EntitySet entitySet, 
+      EdmBindingTarget edmEntitySet) throws ODataApplicationException {
 
     if (filterOption == null) {
       return;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/518a3a41/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
index 71b9a81..d166c4e 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/options/OrderByHandler.java
@@ -24,7 +24,7 @@ import java.util.Locale;
 
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntitySet;
-import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.uri.queryoption.OrderByItem;
@@ -35,14 +35,14 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operand
 
 public class OrderByHandler {
   public static void applyOrderByOption(final OrderByOption orderByOption, final EntitySet entitySet,
-      final EdmEntitySet edmEntitySet) throws ODataApplicationException {
+      final EdmBindingTarget edmBindingTarget) throws ODataApplicationException {
 
     if (orderByOption == null) {
       return;
     }
 
     try {
-      applyOrderByOptionInternal(orderByOption, entitySet, edmEntitySet);
+      applyOrderByOptionInternal(orderByOption, entitySet, edmBindingTarget);
     } catch (SystemQueryOptionsRuntimeException e) {
       if (e.getCause() instanceof ODataApplicationException) {
         // Throw the nested exception, to send the correct HTTP status code in the HTTP response
@@ -55,7 +55,7 @@ public class OrderByHandler {
   }
 
   private static void applyOrderByOptionInternal(final OrderByOption orderByOption, final EntitySet entitySet,
-      final EdmEntitySet edmEntitySet) throws ODataApplicationException {
+      final EdmBindingTarget edmBindingTarget) throws ODataApplicationException {
     Collections.sort(entitySet.getEntities(), new Comparator<Entity>() {
       @Override
       @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -69,9 +69,9 @@ public class OrderByHandler {
           try {
             final OrderByItem item = orderByOption.getOrders().get(i);
             final TypedOperand op1 =
-                item.getExpression().accept(new ExpressionVisitorImpl(e1, edmEntitySet)).asTypedOperand();
+                item.getExpression().accept(new ExpressionVisitorImpl(e1, edmBindingTarget)).asTypedOperand();
             final TypedOperand op2 =
-                item.getExpression().accept(new ExpressionVisitorImpl(e2, edmEntitySet)).asTypedOperand();
+                item.getExpression().accept(new ExpressionVisitorImpl(e2, edmBindingTarget)).asTypedOperand();
 
             if (op1.isNull() || op2.isNull()) {
               if (op1.isNull() && op2.isNull()) {