Repository: olingo-odata4
Updated Branches:
  refs/heads/master ea89f7213 -> 7ad5b0fb5


[OLINGO-663] conditional handling in technical service, part 2

Signed-off-by: Christian Amend <[email protected]>


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

Branch: refs/heads/master
Commit: 7ad5b0fb54dd3f52247716afb32b2e8192fe750c
Parents: ea89f72
Author: Klaus Straubinger <[email protected]>
Authored: Thu May 28 12:35:41 2015 +0200
Committer: Christian Amend <[email protected]>
Committed: Thu May 28 15:12:31 2015 +0200

----------------------------------------------------------------------
 .../fit/tecsvc/client/ConditionalITCase.java    | 156 ++++++++--
 .../olingo/fit/tecsvc/client/MediaITCase.java   |   6 +-
 .../tecsvc/client/PrimitiveComplexITCase.java   | 121 +++++++-
 .../olingo/server/api/EtagInformation.java      |   4 +-
 .../olingo/server/tecsvc/ETagSupport.java       |  34 +++
 .../olingo/server/tecsvc/TechnicalServlet.java  |   7 +-
 .../olingo/server/tecsvc/data/DataProvider.java |  10 +-
 .../processor/TechnicalEntityProcessor.java     |  97 +++---
 .../TechnicalPrimitiveComplexProcessor.java     | 294 ++++++++++++-------
 .../tecsvc/processor/TechnicalProcessor.java    |  47 ++-
 10 files changed, 584 insertions(+), 192 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
index 2dcbc26..796c2c4 100644
--- 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
+++ 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
@@ -18,20 +18,35 @@
  */
 package org.apache.olingo.fit.tecsvc.client;
 
+import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
+import java.net.URI;
+
+import org.apache.commons.io.IOUtils;
 import org.apache.olingo.client.api.ODataClient;
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import org.apache.olingo.client.api.communication.request.ODataBasicRequest;
+import 
org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest;
+import 
org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateRequest;
+import 
org.apache.olingo.client.api.communication.request.cud.ODataPropertyUpdateRequest;
+import org.apache.olingo.client.api.communication.request.cud.UpdateType;
 import 
org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import 
org.apache.olingo.client.api.communication.request.retrieve.ODataPropertyRequest;
+import 
org.apache.olingo.client.api.communication.request.retrieve.ODataValueRequest;
+import 
org.apache.olingo.client.api.communication.request.streamed.ODataMediaEntityUpdateRequest;
+import org.apache.olingo.client.api.communication.response.ODataDeleteResponse;
 import 
org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.client.api.domain.ClientEntity;
+import org.apache.olingo.client.api.domain.ClientProperty;
+import org.apache.olingo.client.api.http.HttpClientException;
 import org.apache.olingo.client.core.ODataClientFactory;
-import org.apache.olingo.commons.api.ODataError;
 import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.fit.AbstractBaseTestITCase;
 import org.apache.olingo.fit.tecsvc.TecSvcConst;
@@ -39,31 +54,27 @@ import org.junit.Test;
 
 public final class ConditionalITCase extends AbstractBaseTestITCase {
 
+  private final ODataClient client = getClient();
+
+  private final URI uriEntity = client.newURIBuilder(TecSvcConst.BASE_URI)
+      .appendEntitySetSegment("ESCompAllPrim").appendKeySegment(0).build();
+  private final URI uriProperty = 
client.newURIBuilder(uriEntity.toASCIIString())
+      
.appendPropertySegment("PropertyComp").appendPropertySegment("PropertyDuration").build();
+  private final URI uriPropertyValue = 
client.newURIBuilder(uriProperty.toASCIIString()).appendValueSegment().build();
+  private final URI uriMedia = client.newURIBuilder(TecSvcConst.BASE_URI)
+      
.appendEntitySetSegment("ESMedia").appendKeySegment(1).appendValueSegment().build();
+
   @Test
   public void readWithWrongIfMatch() throws Exception {
-    final ODataClient client = getClient();
-    ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(
-        client.newURIBuilder(TecSvcConst.BASE_URI)
-        .appendEntitySetSegment("ESCompAllPrim").appendKeySegment(0).build());
+    ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(uriEntity);
     request.setIfMatch("W/\"1\"");
     assertNotNull(request);
-
-    try {
-      request.execute();
-      fail("Expected Exception not thrown!");
-    } catch (final ODataClientErrorException e) {
-      assertEquals(HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), 
e.getStatusLine().getStatusCode());
-      final ODataError error = e.getODataError();
-      assertThat(error.getMessage(), containsString("condition"));
-    }
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
   }
 
   @Test
   public void readNotModified() throws Exception {
-    final ODataClient client = getClient();
-    ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(
-        client.newURIBuilder(TecSvcConst.BASE_URI)
-        .appendEntitySetSegment("ESCompAllPrim").appendKeySegment(0).build());
+    ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(uriEntity);
     request.setIfNoneMatch("W/\"0\"");
     assertNotNull(request);
 
@@ -71,6 +82,115 @@ public final class ConditionalITCase extends 
AbstractBaseTestITCase {
     assertEquals(HttpStatusCode.NOT_MODIFIED.getStatusCode(), 
response.getStatusCode());
   }
 
+  @Test
+  public void updateWithoutIfMatch() throws Exception {
+    executeAndExpectError(
+        client.getCUDRequestFactory().getEntityUpdateRequest(
+            uriEntity, UpdateType.PATCH, 
client.getObjectFactory().newEntity(null)),
+        HttpStatusCode.PRECONDITION_REQUIRED);
+  }
+
+  @Test
+  public void updateWithWrongIfMatch() throws Exception {
+    ODataEntityUpdateRequest<ClientEntity> request = 
client.getCUDRequestFactory().getEntityUpdateRequest(
+        uriEntity, UpdateType.PATCH, 
client.getObjectFactory().newEntity(null));
+    request.setIfMatch("W/\"1\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void updateMediaWithWrongIfMatch() throws Exception {
+    ODataMediaEntityUpdateRequest<ClientEntity> request = 
client.getCUDRequestFactory().getMediaEntityUpdateRequest(
+        uriMedia, IOUtils.toInputStream("ignored"));
+    request.setIfMatch("W/\"42\"");
+
+    try {
+      request.payloadManager().getResponse();
+      fail("Expected Exception not thrown!");
+    } catch (final HttpClientException e) {
+      final ODataClientErrorException ex = (ODataClientErrorException) 
e.getCause().getCause();
+      assertEquals(HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), 
ex.getStatusLine().getStatusCode());
+      assertThat(ex.getODataError().getMessage(), containsString("condition"));
+    }
+  }
+
+  @Test
+  public void deleteWithWrongIfMatch() throws Exception {
+    ODataDeleteRequest request = 
client.getCUDRequestFactory().getDeleteRequest(uriEntity);
+    request.setIfMatch("W/\"1\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void deleteMediaWithWrongIfMatch() throws Exception {
+    ODataDeleteRequest request = 
client.getCUDRequestFactory().getDeleteRequest(uriMedia);
+    request.setIfMatch("W/\"42\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void indirectEntityChange() throws Exception {
+    final String eTag = "W/\"0\"";
+    ODataDeleteRequest deleteRequest = 
client.getCUDRequestFactory().getDeleteRequest(uriProperty);
+    deleteRequest.setIfMatch(eTag);
+    final ODataDeleteResponse response = deleteRequest.execute();
+
+    ODataEntityUpdateRequest<ClientEntity> request = 
client.getCUDRequestFactory().getEntityUpdateRequest(
+        uriEntity, UpdateType.PATCH, 
client.getObjectFactory().newEntity(null));
+    request.setIfMatch(eTag);
+    // This request has to be in the same session as the first in order to 
access the same data provider.
+    request.addCustomHeader(HttpHeader.COOKIE, 
response.getHeader(HttpHeader.SET_COOKIE).iterator().next());
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void readPropertyNotModified() throws Exception {
+    ODataPropertyRequest<ClientProperty> request = 
client.getRetrieveRequestFactory().getPropertyRequest(uriProperty);
+    request.setIfNoneMatch("W/\"0\"");
+    assertEquals(HttpStatusCode.NOT_MODIFIED.getStatusCode(), 
request.execute().getStatusCode());
+  }
+
+  @Test
+  public void readPropertyValueNotModified() throws Exception {
+    ODataValueRequest request = 
client.getRetrieveRequestFactory().getPropertyValueRequest(uriPropertyValue);
+    request.setIfNoneMatch("W/\"0\"");
+    assertEquals(HttpStatusCode.NOT_MODIFIED.getStatusCode(), 
request.execute().getStatusCode());
+  }
+
+  @Test
+  public void updatePropertyWithWrongIfMatch() throws Exception {
+    ODataPropertyUpdateRequest request = 
client.getCUDRequestFactory().getPropertyPrimitiveValueUpdateRequest(
+        uriProperty,
+        client.getObjectFactory().newPrimitiveProperty("PropertyDuration",
+            
client.getObjectFactory().newPrimitiveValueBuilder().buildString("PT42S")));
+    request.setIfMatch("W/\"1\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void deletePropertyWithWrongIfMatch() throws Exception {
+    ODataDeleteRequest request = 
client.getCUDRequestFactory().getDeleteRequest(uriProperty);
+    request.setIfMatch("W/\"1\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void deletePropertyValueWithWrongIfMatch() throws Exception {
+    ODataDeleteRequest request = 
client.getCUDRequestFactory().getDeleteRequest(uriPropertyValue);
+    request.setIfMatch("W/\"1\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  private void executeAndExpectError(ODataBasicRequest<?> request, final 
HttpStatusCode status) {
+    try {
+      request.execute();
+      fail("Expected Exception not thrown!");
+    } catch (final ODataClientErrorException e) {
+      assertEquals(status.getStatusCode(), e.getStatusLine().getStatusCode());
+      assertThat(e.getODataError().getMessage(), 
anyOf(containsString("condition"), containsString("match")));
+    }
+  }
+
   @Override
   protected ODataClient getClient() {
     ODataClient odata = ODataClientFactory.getClient();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
index e48f08c..3bd14c1 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
@@ -75,7 +75,8 @@ public final class MediaITCase extends AbstractBaseTestITCase 
{
     final ODataClient client = getClient();
     final URI uri = client.newURIBuilder(TecSvcConst.BASE_URI)
         
.appendEntitySetSegment("ESMedia").appendKeySegment(4).appendValueSegment().build();
-    final ODataDeleteRequest request = 
client.getCUDRequestFactory().getDeleteRequest(uri);
+    ODataDeleteRequest request = 
client.getCUDRequestFactory().getDeleteRequest(uri);
+    request.setIfMatch("W/\"4\"");
     assertNotNull(request);
 
     final ODataDeleteResponse response = request.execute();
@@ -102,6 +103,7 @@ public final class MediaITCase extends 
AbstractBaseTestITCase {
         client.getCUDRequestFactory().getMediaEntityUpdateRequest(uri,
             IOUtils.toInputStream("just a test"));
     request.setContentType(ContentType.TEXT_PLAIN.toContentTypeString());
+    request.setIfMatch("W/\"4\"");
     assertNotNull(request);
 
     final ODataMediaEntityUpdateResponse<ClientEntity> response = 
request.payloadManager().getResponse();
@@ -143,7 +145,7 @@ public final class MediaITCase extends 
AbstractBaseTestITCase {
     // This check has to be in the same session in order to access the same 
data provider.
     ODataMediaRequest mediaRequest = 
client.getRetrieveRequestFactory().getMediaRequest(
         
client.newURIBuilder(TecSvcConst.BASE_URI).appendEntitySetSegment("ESMedia")
-        .appendKeySegment(5).appendValueSegment().build());
+            .appendKeySegment(5).appendValueSegment().build());
     mediaRequest.addCustomHeader(HttpHeader.COOKIE, 
response.getHeader(HttpHeader.SET_COOKIE).iterator().next());
     ODataRetrieveResponse<InputStream> mediaResponse = mediaRequest.execute();
     assertEquals(HttpStatusCode.OK.getStatusCode(), 
mediaResponse.getStatusCode());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java
 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java
index 50ab97b..d71e6a0 100644
--- 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java
+++ 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java
@@ -20,23 +20,31 @@ package org.apache.olingo.fit.tecsvc.client;
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.net.URI;
+import java.util.Iterator;
 
 import org.apache.commons.io.IOUtils;
 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;
+import 
org.apache.olingo.client.api.communication.request.cud.ODataPropertyUpdateRequest;
+import org.apache.olingo.client.api.communication.request.cud.UpdateType;
 import 
org.apache.olingo.client.api.communication.request.retrieve.ODataPropertyRequest;
 import 
org.apache.olingo.client.api.communication.request.retrieve.ODataValueRequest;
 import org.apache.olingo.client.api.communication.response.ODataDeleteResponse;
+import 
org.apache.olingo.client.api.communication.response.ODataPropertyUpdateResponse;
 import 
org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
+import org.apache.olingo.client.api.domain.ClientCollectionValue;
+import org.apache.olingo.client.api.domain.ClientComplexValue;
 import org.apache.olingo.client.api.domain.ClientPrimitiveValue;
 import org.apache.olingo.client.api.domain.ClientProperty;
+import org.apache.olingo.client.api.domain.ClientValue;
 import org.apache.olingo.client.core.ODataClientFactory;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.format.ODataFormat;
@@ -52,13 +60,12 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
 
   @Test
   public void readSimpleProperty() throws Exception {
-    ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
+    final ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
         .getPropertyRequest(getClient().newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment("ESTwoPrim")
             .appendKeySegment(32766)
             .appendPropertySegment("PropertyString")
             .build());
-
     assertNotNull(request);
 
     ODataRetrieveResponse<ClientProperty> response = request.execute();
@@ -135,7 +142,7 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
 
   @Test
   public void readComplexProperty() throws Exception {
-    ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
+    final ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
         .getPropertyRequest(getClient().newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment("ESMixPrimCollComp")
             .appendKeySegment(7)
@@ -153,7 +160,7 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
 
   @Test
   public void readComplexPropertyContextURL() throws Exception {
-    ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
+    final ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
         .getPropertyRequest(getClient().newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment("ESMixPrimCollComp")
             .appendKeySegment(7)
@@ -185,7 +192,7 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
 
   @Test
   public void readUnknownProperty() throws Exception {
-    ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
+    final ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
         .getPropertyRequest(getClient().newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment("ESTwoPrim")
             .appendKeySegment(32766)
@@ -201,17 +208,115 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
 
   @Test
   public void readNoContentProperty() throws Exception {
-    ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
+    final ODataPropertyRequest<ClientProperty> request = 
getClient().getRetrieveRequestFactory()
         .getPropertyRequest(getClient().newURIBuilder(SERVICE_URI)
             .appendEntitySetSegment("ESTwoPrim")
             .appendKeySegment(-32766)
             .appendPropertySegment("PropertyString")
             .build());
-    ODataRetrieveResponse<ClientProperty> response = request.execute();
+    final ODataRetrieveResponse<ClientProperty> response = request.execute();
     assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), 
response.getStatusCode());
   }
 
   @Test
+  public void updatePrimitiveProperty() throws Exception {
+    final ODataPropertyUpdateRequest request =
+        
getClient().getCUDRequestFactory().getPropertyPrimitiveValueUpdateRequest(
+            getClient().newURIBuilder(SERVICE_URI)
+                .appendEntitySetSegment("ESTwoPrim").appendKeySegment(32766)
+                .appendPropertySegment("PropertyString")
+                .build(),
+            
getClient().getObjectFactory().newPrimitiveProperty("PropertyString",
+                
getClient().getObjectFactory().newPrimitiveValueBuilder().buildString("Test 
String1")));
+    assertNotNull(request);
+
+    final ODataPropertyUpdateResponse response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+    assertThat(response.getContentType(), 
containsString(ContentType.APPLICATION_JSON.toContentTypeString()));
+
+    final ClientProperty property = response.getBody();
+    assertNotNull(property);
+    assertNotNull(property.getPrimitiveValue());
+    assertEquals("Test String1", property.getPrimitiveValue().toValue());
+  }
+
+  @Test
+  public void patchComplexProperty() throws Exception {
+    final ODataPropertyUpdateRequest request =
+        
getClient().getCUDRequestFactory().getPropertyComplexValueUpdateRequest(
+            getClient().newURIBuilder(SERVICE_URI)
+                
.appendEntitySetSegment("ESMixPrimCollComp").appendKeySegment(7)
+                .appendPropertySegment("PropertyComp")
+                .build(),
+            UpdateType.PATCH,
+            getClient().getObjectFactory().newComplexProperty("PropertyComp",
+                getClient().getObjectFactory().newComplexValue(null).add(
+                    
getClient().getObjectFactory().newPrimitiveProperty("PropertyString",
+                        
getClient().getObjectFactory().newPrimitiveValueBuilder().buildString("Test 
String42")))));
+    assertNotNull(request);
+
+    final ODataPropertyUpdateResponse response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+
+    final ClientProperty property = response.getBody();
+    assertNotNull(property);
+    assertNotNull(property.getComplexValue());
+    final ClientComplexValue value = property.getComplexValue();
+    assertEquals("Test String42", 
value.get("PropertyString").getPrimitiveValue().toValue());
+    assertEquals(222, 
value.get("PropertyInt16").getPrimitiveValue().toValue());
+  }
+
+  @Test
+  public void updatePrimitiveCollection() throws Exception {
+    final ODataPropertyUpdateRequest request =
+        
getClient().getCUDRequestFactory().getPropertyCollectionValueUpdateRequest(
+            getClient().newURIBuilder(SERVICE_URI)
+                
.appendEntitySetSegment("ESMixPrimCollComp").appendKeySegment(7)
+                .appendPropertySegment("CollPropertyString")
+                .build(),
+            
getClient().getObjectFactory().newCollectionProperty("CollPropertyString",
+                getClient().getObjectFactory().newCollectionValue(null)
+                    
.add(getClient().getObjectFactory().newPrimitiveValueBuilder().buildString("Test
 String1"))
+                    
.add(getClient().getObjectFactory().newPrimitiveValueBuilder().buildString("Test
 String2"))));
+    assertNotNull(request);
+
+    final ODataPropertyUpdateResponse response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+
+    final ClientProperty property = response.getBody();
+    assertNotNull(property);
+    final ClientCollectionValue<ClientValue> value = 
property.getCollectionValue();
+    assertNotNull(value);
+    Iterator<ClientValue> iterator = value.iterator();
+    assertTrue(iterator.hasNext());
+    assertEquals("Test String1", iterator.next().asPrimitive().toValue());
+    assertEquals("Test String2", iterator.next().asPrimitive().toValue());
+    assertFalse(iterator.hasNext());
+  }
+
+  @Test
+  public void updateComplexCollection() throws Exception {
+    final ODataPropertyUpdateRequest request =
+        
getClient().getCUDRequestFactory().getPropertyCollectionValueUpdateRequest(
+            getClient().newURIBuilder(SERVICE_URI)
+                
.appendEntitySetSegment("ESMixPrimCollComp").appendKeySegment(7)
+                .appendPropertySegment("CollPropertyComp")
+                .build(),
+            
getClient().getObjectFactory().newCollectionProperty("CollPropertyComp",
+                getClient().getObjectFactory().newCollectionValue(null)));
+    assertNotNull(request);
+
+    final ODataPropertyUpdateResponse response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+
+    final ClientProperty property = response.getBody();
+    assertNotNull(property);
+    final ClientCollectionValue<ClientValue> value = 
property.getCollectionValue();
+    assertNotNull(value);
+    assertFalse(value.iterator().hasNext());
+  }
+
+  @Test
   public void readPropertyValue() throws Exception {
     final ODataValueRequest request = getClient().getRetrieveRequestFactory()
         .getPropertyValueRequest(getClient().newURIBuilder(SERVICE_URI)
@@ -220,7 +325,7 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
             .appendPropertySegment("PropertyString")
             .appendValueSegment()
             .build());
-    ODataRetrieveResponse<ClientPrimitiveValue> response = request.execute();
+    final ODataRetrieveResponse<ClientPrimitiveValue> response = 
request.execute();
     assertEquals("Test String1", response.getBody().toValue());
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/lib/server-api/src/main/java/org/apache/olingo/server/api/EtagInformation.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/EtagInformation.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/EtagInformation.java
index d6be3fa..87c6a17 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/EtagInformation.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/EtagInformation.java
@@ -48,7 +48,9 @@ public class EtagInformation {
   }
 
   /**
-   * <p>Checks whether a given ETag value is matched by this ETag 
information.</p>
+   * <p>Checks whether a given ETag value is matched by this ETag information,
+   * using weak comparison as described in
+   * <a href="https://www.ietf.org/rfc/rfc7232.txt";>RFC 7232</a>, section 
2.3.2.</p>
    * <p>If the given value is <code>null</code>, or if this ETag information
    * does not contain anything, the result is <code>false</code>.</p>
    * @param etag the ETag value to match

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java
new file mode 100644
index 0000000..825032d
--- /dev/null
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import org.apache.olingo.server.api.CustomETagSupport;
+
+public class ETagSupport implements CustomETagSupport {
+
+  @Override
+  public boolean hasETag(final String entitySetName) {
+    return entitySetName.equals("ESCompAllPrim");
+  }
+
+  @Override
+  public boolean hasMediaETag(final String entitySetName) {
+    return entitySetName.equals("ESMedia");
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
index 25c86c7..0412c80 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
@@ -48,7 +48,7 @@ public class TechnicalServlet extends HttpServlet {
   private static final Logger LOG = 
LoggerFactory.getLogger(TechnicalServlet.class);
 
   @Override
-  protected void service(final HttpServletRequest req, final 
HttpServletResponse resp)
+  protected void service(final HttpServletRequest request, final 
HttpServletResponse response)
       throws ServletException, IOException {
     try {
       OData odata = OData.newInstance();
@@ -57,7 +57,7 @@ public class TechnicalServlet extends HttpServlet {
       final List<EdmxReference> references = Arrays.asList(reference);
       final ServiceMetadata serviceMetadata = odata.createServiceMetadata(new 
EdmTechProvider(references), references);
 
-      HttpSession session = req.getSession(true);
+      HttpSession session = request.getSession(true);
       DataProvider dataProvider = (DataProvider) 
session.getAttribute(DataProvider.class.getName());
       if (dataProvider == null) {
         dataProvider = new DataProvider();
@@ -69,7 +69,8 @@ public class TechnicalServlet extends HttpServlet {
       handler.register(new TechnicalEntityProcessor(dataProvider, 
serviceMetadata));
       handler.register(new TechnicalPrimitiveComplexProcessor(dataProvider, 
serviceMetadata));
       handler.register(new TechnicalBatchProcessor(dataProvider));
-      handler.process(req, resp);
+      handler.register(new ETagSupport());
+      handler.process(request, response);
     } catch (RuntimeException e) {
       LOG.error("Server Error", e);
       throw new ServletException(e);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/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 9de9bea..732d43b 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
@@ -275,6 +275,10 @@ public class DataProvider {
     }
 
     // Update the ETag if present.
+    updateETag(entity);
+  }
+
+  public void updateETag(Entity entity) {
     if (entity.getETag() != null) {
       entity.setETag("W/\"" + System.nanoTime() + "\"");
     }
@@ -411,8 +415,8 @@ public class DataProvider {
     }
   }
 
-  @SuppressWarnings({ "unchecked" })
-  public void updateProperty(final EdmProperty edmProperty, final Property 
property, final Property newProperty,
+  @SuppressWarnings("unchecked")
+  public void updateProperty(final EdmProperty edmProperty, Property property, 
final Property newProperty,
       final boolean patch) throws DataProviderException {
     if (edmProperty.isPrimitive()) {
       if (newProperty != null || !patch) {
@@ -420,7 +424,7 @@ public class DataProvider {
         property.setValue(property.getValueType(), value);
       }
     } else if (edmProperty.isCollection()) {
-      // Updating collection properties mean replacing all entites with the 
given ones
+      // Updating collection properties means replacing all entries with the 
given ones.
       property.asCollection().clear();
 
       if (newProperty != null) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/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 0ad21bb..489055b 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
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.server.tecsvc.processor;
 
+import java.util.List;
 import java.util.Locale;
 
 import org.apache.olingo.commons.api.data.ContextURL;
@@ -34,7 +35,6 @@ import org.apache.olingo.commons.api.http.HttpContentType;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpMethod;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
-import org.apache.olingo.server.api.EtagInformation;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.ODataRequest;
 import org.apache.olingo.server.api.ODataResponse;
@@ -57,9 +57,11 @@ import 
org.apache.olingo.server.api.serializer.ODataSerializer;
 import org.apache.olingo.server.api.serializer.SerializerException;
 import org.apache.olingo.server.api.serializer.SerializerResult;
 import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.api.uri.UriResource;
 import org.apache.olingo.server.api.uri.UriResourceAction;
 import org.apache.olingo.server.api.uri.UriResourceEntitySet;
 import org.apache.olingo.server.api.uri.UriResourceFunction;
+import org.apache.olingo.server.api.uri.UriResourceValue;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.SelectOption;
@@ -98,7 +100,10 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
   public void processActionEntityCollection(final ODataRequest request, final 
ODataResponse response,
       final UriInfo uriInfo, final ContentType requestFormat, final 
ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    EdmAction action = checkBoundAndExtractAction(uriInfo);
+    blockBoundActions(uriInfo);
+    final EdmAction action = ((UriResourceAction) 
uriInfo.asUriInfoResource().getUriResourceParts().get(0))
+        .getAction();
+
     DeserializerResult deserializerResult =
         odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
             .actionParameters(request.getBody(), action);
@@ -148,7 +153,7 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
       final ContentType requestedContentType) throws 
ODataApplicationException, SerializerException {
     validateOptions(uriInfo.asUriInfoResource());
 
-    processEntity(request, response, uriInfo, requestedContentType, false);
+    readEntity(request, response, uriInfo, requestedContentType, false);
   }
 
   @Override
@@ -238,6 +243,10 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
         throw e;
       }
     }
+
+    checkChangePreconditions(entity.getETag(),
+        request.getHeaders(HttpHeader.IF_MATCH),
+        request.getHeaders(HttpHeader.IF_NONE_MATCH));
     checkRequestFormat(requestFormat);
     final ODataDeserializer deserializer = 
odata.createDeserializer(ODataFormat.fromContentType(requestFormat));
     final Entity changedEntity = deserializer.entity(request.getBody(), 
edmEntitySet.getEntityType()).getEntity();
@@ -252,10 +261,10 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, 
changedEntity,
         request.getMethod() == HttpMethod.PATCH, false);
 
+    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     final ODataFormat format = ODataFormat.fromContentType(responseFormat);
     response.setContent(serializeEntity(entity, edmEntitySet, edmEntityType, 
format, null, null)
         .getContent());
-    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     response.setHeader(HttpHeader.CONTENT_TYPE, 
responseFormat.toContentTypeString());
     if (entity.getETag() != null) {
       response.setHeader(HttpHeader.ETAG, entity.getETag());
@@ -270,6 +279,9 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     final EdmEntityType edmEntityType = edmEntitySet.getEntityType();
 
     Entity entity = readEntity(uriInfo);
+    checkChangePreconditions(entity.getMediaETag(),
+        request.getHeaders(HttpHeader.IF_MATCH),
+        request.getHeaders(HttpHeader.IF_NONE_MATCH));
     checkRequestFormat(requestFormat);
     dataProvider.setMedia(entity, 
odata.createFixedFormatDeserializer().binary(request.getBody()),
         requestFormat.toContentTypeString());
@@ -285,10 +297,16 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void deleteEntity(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo)
+  public void deleteEntity(final ODataRequest request, ODataResponse response, 
final UriInfo uriInfo)
       throws ODataApplicationException {
     final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo);
     final Entity entity = readEntity(uriInfo);
+    final List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
+    final boolean isValue = resourcePaths.get(resourcePaths.size() - 1) 
instanceof UriResourceValue;
+
+    checkChangePreconditions(isValue ? entity.getMediaETag() : 
entity.getETag(),
+        request.getHeaders(HttpHeader.IF_MATCH),
+        request.getHeaders(HttpHeader.IF_NONE_MATCH));
     dataProvider.delete(edmEntitySet, entity);
     response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
   }
@@ -297,7 +315,9 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
   public void processActionEntity(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo,
       final ContentType requestFormat, final ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    final EdmAction action = checkBoundAndExtractAction(uriInfo);
+    blockBoundActions(uriInfo);
+    final EdmAction action = ((UriResourceAction) 
uriInfo.asUriInfoResource().getUriResourceParts().get(0))
+        .getAction();
     final EdmEntitySet edmEntitySet = 
getEdmEntitySet(uriInfo.asUriInfoResource());
     final EdmEntityType type = (EdmEntityType) 
action.getReturnType().getType();
 
@@ -319,9 +339,8 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
       final ODataFormat format = ODataFormat.fromContentType(responseFormat);
       response.setContent(serializeEntity(entityResult.getEntity(), 
edmEntitySet, type, format, null, null)
           .getContent());
-      response.setStatusCode((entityResult.isCreated() ? 
HttpStatusCode.CREATED : HttpStatusCode.OK).getStatusCode
-
-          ());
+      response.setStatusCode((entityResult.isCreated() ? 
HttpStatusCode.CREATED : HttpStatusCode.OK)
+          .getStatusCode());
       response.setHeader(HttpHeader.CONTENT_TYPE, 
responseFormat.toContentTypeString());
       if (entityResult.getEntity().getETag() != null) {
         response.setHeader(HttpHeader.ETAG, 
entityResult.getEntity().getETag());
@@ -343,17 +362,8 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
   }
 
-  private void checkRequestFormat(final ContentType requestFormat) throws 
ODataApplicationException {
-    if (requestFormat == null) {
-      throw new ODataApplicationException("The content type has not been set 
in the request.",
-          HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
-    }
-  }
-
   private ContextURL getContextUrl(final EdmEntitySet entitySet, final 
EdmEntityType entityType,
-      final boolean isSingleEntity, final ExpandOption expand, final 
SelectOption select) throws
-
-      SerializerException {
+      final boolean isSingleEntity, final ExpandOption expand, final 
SelectOption select) throws SerializerException {
     Builder builder = ContextURL.with();
     builder = entitySet == null ?
         isSingleEntity ? builder.type(entityType) : 
builder.asCollection().type(entityType) :
@@ -365,30 +375,26 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void readReference(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo, 
+  public void readReference(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo, 
       final ContentType requestedContentType) throws 
ODataApplicationException, SerializerException {
-    
-    processEntity(request, response, uriInfo, requestedContentType, true);
+    readEntity(request, response, uriInfo, requestedContentType, true);
   }
 
   @Override
-  public void createReference(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo, 
+  public void createReference(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo, 
       final ContentType requestFormat) throws ODataApplicationException, 
DeserializerException {
-    
     throw new ODataApplicationException("Not implemented", 
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
   }
 
   @Override
-  public void updateReference(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo, 
+  public void updateReference(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo, 
       final ContentType requestFormat) throws ODataApplicationException, 
DeserializerException {
-    
     throw new ODataApplicationException("Not implemented", 
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
   }
 
   @Override
-  public void deleteReference(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo) 
+  public void deleteReference(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo) 
       throws ODataApplicationException {
-    
     throw new ODataApplicationException("Not implemented", 
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
   }
 
@@ -399,7 +405,7 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     processEntityCollection(request, response, uriInfo, requestedContentType, 
true);
   }
 
-  private void processEntity(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo, 
+  private void readEntity(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo, 
       final ContentType requestedContentType, final boolean isReference)
       throws ODataApplicationException, SerializerException {
     final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo);
@@ -410,18 +416,9 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
 
     final Entity entity = readEntity(uriInfo);
 
-    if (entity.getETag() != null) {
-      final EtagInformation ifMatch = 
odata.createEtagInformation(request.getHeaders(HttpHeader.IF_MATCH));
-      if (!ifMatch.isMatchedBy(entity.getETag()) && 
!ifMatch.getEtags().isEmpty()) {
-        throw new ODataApplicationException("The If-Match precondition is not 
fulfilled.",
-            HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.ROOT);
-      }
-      if 
(odata.createEtagInformation(request.getHeaders(HttpHeader.IF_NONE_MATCH))
-          .isMatchedBy(entity.getETag())) {
-        throw new ODataApplicationException("The entity has not been 
modified.",
-            HttpStatusCode.NOT_MODIFIED.getStatusCode(), Locale.ROOT);
-      }
-    }
+    checkReadPreconditions(entity.getETag(),
+        request.getHeaders(HttpHeader.IF_MATCH),
+        request.getHeaders(HttpHeader.IF_NONE_MATCH));
 
     final ODataFormat format = 
ODataFormat.fromContentType(requestedContentType);
     final ExpandOption expand = uriInfo.getExpandOption();
@@ -430,12 +427,11 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     final ExpandSystemQueryOptionHandler expandHandler = new 
ExpandSystemQueryOptionHandler();
     final Entity entitySerialization = 
expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand);
     expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, 
expand);
-    
-    final SerializerResult serializerResult = (isReference) ? 
-          serializeReference(entity, edmEntitySet, format) 
-        : serializeEntity(entitySerialization, edmEntitySet, edmEntityType, 
format, expand, select);
-    
-          
+
+    final SerializerResult serializerResult = isReference ?
+        serializeReference(entity, edmEntitySet, format) :
+        serializeEntity(entitySerialization, edmEntitySet, edmEntityType, 
format, expand, select);
+
     if (entity.getETag() != null) {
       response.setHeader(HttpHeader.ETAG, entity.getETag());
     }
@@ -495,15 +491,15 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
     // Serialize
     final SerializerResult serializerResult = (isReference) ? 
         serializeReferenceCollection(entitySetSerialization, edmEntitySet, 
format) :
-          serializeEntiyCollection(entitySetSerialization, edmEntitySet, 
edmEntityType, format, 
-                                   expand, select, countOption);
+        serializeEntityCollection(entitySetSerialization, edmEntitySet, 
edmEntityType, format,
+            expand, select, countOption);
     
     response.setContent(serializerResult.getContent());
     response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     response.setHeader(HttpHeader.CONTENT_TYPE, 
requestedContentType.toContentTypeString());
   }
 
-  private SerializerResult serializeEntiyCollection(final EntityCollection 
entityCollection, 
+  private SerializerResult serializeEntityCollection(final EntityCollection 
entityCollection, 
       final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType, 
final ODataFormat format, 
       final ExpandOption expand, final SelectOption select, final CountOption 
countOption) 
           throws SerializerException {
@@ -541,7 +537,6 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
   private SerializerResult serializeEntity(final Entity entity,
       final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType, 
final ODataFormat format,
       final ExpandOption expand, final SelectOption select) throws 
SerializerException {
-    
     return odata.createSerializer(format).entity(
         serviceMetadata,
         edmEntityType,

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
index 0d911f0..4f1a4ba 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
@@ -40,6 +40,7 @@ import 
org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpMethod;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.ODataRequest;
@@ -68,6 +69,7 @@ import org.apache.olingo.server.api.uri.UriHelper;
 import org.apache.olingo.server.api.uri.UriInfo;
 import org.apache.olingo.server.api.uri.UriInfoResource;
 import org.apache.olingo.server.api.uri.UriResource;
+import org.apache.olingo.server.api.uri.UriResourceAction;
 import org.apache.olingo.server.api.uri.UriResourceFunction;
 import org.apache.olingo.server.api.uri.UriResourceKind;
 import org.apache.olingo.server.api.uri.UriResourceProperty;
@@ -90,30 +92,31 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void readPrimitive(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo,
+  public void readPrimitive(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
       final ContentType contentType) throws ODataApplicationException, 
SerializerException {
-    readProperty(response, uriInfo, contentType, RepresentationType.PRIMITIVE);
+    readProperty(request, response, uriInfo, contentType, 
RepresentationType.PRIMITIVE);
   }
 
   @Override
-  public void updatePrimitive(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo,
+  public void updatePrimitive(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
       final ContentType requestFormat, final ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    throw new ODataApplicationException("Not supported yet.",
-        HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+    updateProperty(request, response, uriInfo, requestFormat, responseFormat, 
RepresentationType.PRIMITIVE);
   }
 
   @Override
-  public void deletePrimitive(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo)
+  public void deletePrimitive(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo)
       throws ODataApplicationException {
-    deleteProperty(response, uriInfo);
+    deleteProperty(request, response, uriInfo);
   }
 
   @Override
-  public void processActionPrimitive(final ODataRequest request, final 
ODataResponse response,
+  public void processActionPrimitive(final ODataRequest request, ODataResponse 
response,
       final UriInfo uriInfo, final ContentType requestFormat, final 
ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    EdmAction action = checkBoundAndExtractAction(uriInfo);
+    blockBoundActions(uriInfo);
+    final EdmAction action = ((UriResourceAction) 
uriInfo.asUriInfoResource().getUriResourceParts().get(0))
+        .getAction();
     DeserializerResult deserializerResult =
         odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
             .actionParameters(request.getBody(), action);
@@ -124,8 +127,9 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
       if (action.getReturnType().isNullable()) {
         response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
       } else {
-        // Not nullable return type so we have to give back a 500
-        throw new ODataApplicationException("The action could no be executed", 
500, Locale.ROOT);
+        // Not nullable return type so we have to give back an Internal Server 
Error
+        throw new ODataApplicationException("The action could not be 
executed.",
+            HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
       }
     } else {
       ContextURL contextURL = ContextURL.with().type(type).build();
@@ -141,31 +145,31 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void readPrimitiveCollection(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo,
+  public void readPrimitiveCollection(final ODataRequest request, 
ODataResponse response, final UriInfo uriInfo,
       final ContentType contentType) throws ODataApplicationException, 
SerializerException {
-    readProperty(response, uriInfo, contentType, 
RepresentationType.COLLECTION_PRIMITIVE);
+    readProperty(request, response, uriInfo, contentType, 
RepresentationType.COLLECTION_PRIMITIVE);
   }
 
   @Override
-  public void updatePrimitiveCollection(final ODataRequest request, final 
ODataResponse response,
+  public void updatePrimitiveCollection(final ODataRequest request, 
ODataResponse response,
       final UriInfo uriInfo, final ContentType requestFormat, final 
ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    throw new ODataApplicationException("Not supported yet.",
-        HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+    updateProperty(request, response, uriInfo, requestFormat, responseFormat, 
RepresentationType.COLLECTION_PRIMITIVE);
   }
 
   @Override
-  public void
-      deletePrimitiveCollection(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo)
-          throws ODataApplicationException {
-    deleteProperty(response, uriInfo);
+  public void deletePrimitiveCollection(final ODataRequest request, 
ODataResponse response, final UriInfo uriInfo)
+      throws ODataApplicationException {
+    deleteProperty(request, response, uriInfo);
   }
 
   @Override
-  public void processActionPrimitiveCollection(final ODataRequest request, 
final ODataResponse response,
+  public void processActionPrimitiveCollection(final ODataRequest request, 
ODataResponse response,
       final UriInfo uriInfo, final ContentType requestFormat, final 
ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    EdmAction action = checkBoundAndExtractAction(uriInfo);
+    blockBoundActions(uriInfo);
+    final EdmAction action = ((UriResourceAction) 
uriInfo.asUriInfoResource().getUriResourceParts().get(0))
+        .getAction();
     DeserializerResult deserializerResult =
         odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
             .actionParameters(request.getBody(), action);
@@ -175,10 +179,12 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
 
     if (property == null || property.isNull()) {
       // Collection Propertys must never be null
-      throw new ODataApplicationException("The action could no be executed", 
500, Locale.ROOT);
+      throw new ODataApplicationException("The action could not be executed.",
+          HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
     } else if (property.asCollection().contains(null) && 
!action.getReturnType().isNullable()) {
       // Not nullable return type but array contains a null value
-      throw new ODataApplicationException("The action could no be executed", 
500, Locale.ROOT);
+      throw new ODataApplicationException("The action could not be executed.",
+          HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
     }
     EdmPrimitiveType type = (EdmPrimitiveType) 
action.getReturnType().getType();
     ContextURL contextURL = 
ContextURL.with().type(type).asCollection().build();
@@ -194,24 +200,31 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void readComplex(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo,
+  public void readComplex(final ODataRequest request, ODataResponse response, 
final UriInfo uriInfo,
       final ContentType contentType) throws ODataApplicationException, 
SerializerException {
-    readProperty(response, uriInfo, contentType, RepresentationType.COMPLEX);
+    readProperty(request, response, uriInfo, contentType, 
RepresentationType.COMPLEX);
   }
 
   @Override
-  public void updateComplex(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo,
+  public void updateComplex(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
       final ContentType requestFormat, final ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    throw new ODataApplicationException("Not supported yet.",
-        HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+    updateProperty(request, response, uriInfo, requestFormat, responseFormat, 
RepresentationType.COMPLEX);
+  }
+
+  @Override
+  public void deleteComplex(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo)
+      throws ODataApplicationException {
+    deleteProperty(request, response, uriInfo);
   }
 
   @Override
-  public void processActionComplex(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo,
+  public void processActionComplex(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
       final ContentType requestFormat, final ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    EdmAction action = checkBoundAndExtractAction(uriInfo);
+    blockBoundActions(uriInfo);
+    final EdmAction action = ((UriResourceAction) 
uriInfo.asUriInfoResource().getUriResourceParts().get(0))
+        .getAction();
     DeserializerResult deserializerResult =
         odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
             .actionParameters(request.getBody(), action);
@@ -222,8 +235,9 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
       if (action.getReturnType().isNullable()) {
         response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
       } else {
-        // Not nullable return type so we have to give back a 500
-        throw new ODataApplicationException("The action could no be executed", 
500, Locale.ROOT);
+        // Not nullable return type so we have to give back an Internal Server 
Error
+        throw new ODataApplicationException("The action could not be 
executed.",
+            HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
       }
     } else {
       ContextURL contextURL = ContextURL.with().type(type).build();
@@ -240,30 +254,31 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void deleteComplex(final ODataRequest request, final ODataResponse 
response, final UriInfo uriInfo)
-      throws ODataApplicationException {
-    deleteProperty(response, uriInfo);
-  }
-
-  @Override
-  public void readComplexCollection(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo,
+  public void readComplexCollection(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
       final ContentType contentType) throws ODataApplicationException, 
SerializerException {
-    readProperty(response, uriInfo, contentType, 
RepresentationType.COLLECTION_COMPLEX);
+    readProperty(request, response, uriInfo, contentType, 
RepresentationType.COLLECTION_COMPLEX);
   }
 
   @Override
-  public void updateComplexCollection(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo,
+  public void updateComplexCollection(final ODataRequest request, 
ODataResponse response, final UriInfo uriInfo,
       final ContentType requestFormat, final ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    throw new ODataApplicationException("Not supported yet.",
-        HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+    updateProperty(request, response, uriInfo, requestFormat, responseFormat, 
RepresentationType.COLLECTION_COMPLEX);
   }
 
   @Override
-  public void processActionComplexCollection(final ODataRequest request, final 
ODataResponse response,
+  public void deleteComplexCollection(final ODataRequest request, 
ODataResponse response, final UriInfo uriInfo)
+      throws ODataApplicationException {
+    deleteProperty(request, response, uriInfo);
+  }
+
+  @Override
+  public void processActionComplexCollection(final ODataRequest request, 
ODataResponse response,
       final UriInfo uriInfo, final ContentType requestFormat, final 
ContentType responseFormat)
       throws ODataApplicationException, DeserializerException, 
SerializerException {
-    EdmAction action = checkBoundAndExtractAction(uriInfo);
+    blockBoundActions(uriInfo);
+    final EdmAction action = ((UriResourceAction) 
uriInfo.asUriInfoResource().getUriResourceParts().get(0))
+        .getAction();
     DeserializerResult deserializerResult =
         odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
             .actionParameters(request.getBody(), action);
@@ -273,10 +288,12 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
 
     if (property == null || property.isNull()) {
       // Collection Propertys must never be null
-      throw new ODataApplicationException("The action could no be executed", 
500, Locale.ROOT);
+      throw new ODataApplicationException("The action could not be executed.",
+          HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
     } else if (property.asCollection().contains(null) && 
!action.getReturnType().isNullable()) {
       // Not nullable return type but array contains a null value
-      throw new ODataApplicationException("The action could no be executed", 
500, Locale.ROOT);
+      throw new ODataApplicationException("The action could not be executed.",
+          HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT);
     }
     EdmComplexType type = (EdmComplexType) action.getReturnType().getType();
     ContextURL contextURL = 
ContextURL.with().type(type).asCollection().build();
@@ -291,14 +308,9 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
     response.setHeader(HttpHeader.CONTENT_TYPE, 
responseFormat.toContentTypeString());
   }
 
-  @Override
-  public void deleteComplexCollection(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo)
-      throws ODataApplicationException {
-    deleteProperty(response, uriInfo);
-  }
-
-  private void readProperty(final ODataResponse response, final UriInfo 
uriInfo, final ContentType contentType,
-      final RepresentationType representationType) throws 
ODataApplicationException, SerializerException {
+  private void readProperty(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
+      final ContentType contentType, final RepresentationType 
representationType)
+          throws ODataApplicationException, SerializerException {
     final UriInfoResource resource = uriInfo.asUriInfoResource();
     validateOptions(resource);
     validatePath(resource);
@@ -308,9 +320,16 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
     final List<String> path = getPropertyPath(resourceParts, 0);
 
     final Entity entity = readEntity(uriInfo);
+
+    if (entity != null && entity.getETag() != null) {
+      checkReadPreconditions(entity.getETag(),
+          request.getHeaders(HttpHeader.IF_MATCH),
+          request.getHeaders(HttpHeader.IF_NONE_MATCH));
+    }
+
     final Property property = entity == null ?
-        
getPropertyData(dataProvider.readFunctionPrimitiveComplex(((UriResourceFunction)
 resourceParts.get(0))
-            .getFunction(),
+        getPropertyData(
+            dataProvider.readFunctionPrimitiveComplex(((UriResourceFunction) 
resourceParts.get(0)).getFunction(),
             ((UriResourceFunction) resourceParts.get(0)).getParameters()), 
path) :
         getPropertyData(entity, path);
 
@@ -320,6 +339,7 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
       if (property.getValue() == null) {
         response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
       } else {
+        response.setStatusCode(HttpStatusCode.OK.getStatusCode());
         final EdmProperty edmProperty = path.isEmpty() ? null :
             ((UriResourceProperty) resourceParts.get(resourceParts.size() - 
1)).getProperty();
         final EdmType type = edmProperty == null ?
@@ -328,69 +348,82 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
         final EdmReturnType returnType = resourceParts.get(0) instanceof 
UriResourceFunction ?
             ((UriResourceFunction) 
resourceParts.get(0)).getFunction().getReturnType() : null;
 
-        final ODataFormat format = ODataFormat.fromContentType(contentType);
-        ODataSerializer serializer = odata.createSerializer(format);
         final ExpandOption expand = uriInfo.getExpandOption();
         final SelectOption select = uriInfo.getSelectOption();
-        final ContextURL contextURL = format == ODataFormat.JSON_NO_METADATA ? 
null :
-            getContextUrl(edmEntitySet, entity, path, type, 
representationType, expand, select);
-        switch (representationType) {
-        case PRIMITIVE:
-          response.setContent(serializer.primitive((EdmPrimitiveType) type, 
property,
-              PrimitiveSerializerOptions.with().contextURL(contextURL)
-                  .nullable(edmProperty == null ? returnType.isNullable() : 
edmProperty.isNullable())
-                  .maxLength(edmProperty == null ? returnType.getMaxLength() : 
edmProperty.getMaxLength())
-                  .precision(edmProperty == null ? returnType.getPrecision() : 
edmProperty.getPrecision())
-                  .scale(edmProperty == null ? returnType.getScale() : 
edmProperty.getScale())
-                  .unicode(edmProperty == null ? null : 
edmProperty.isUnicode())
-                  .build()).getContent());
-          break;
-        case COMPLEX:
-          response.setContent(serializer.complex(serviceMetadata, 
(EdmComplexType) type, property,
-              ComplexSerializerOptions.with().contextURL(contextURL)
-                  .expand(expand).select(select)
-                  .build()).getContent());
-          break;
-        case COLLECTION_PRIMITIVE:
-          
response.setContent(serializer.primitiveCollection((EdmPrimitiveType) type, 
property,
-              PrimitiveSerializerOptions.with().contextURL(contextURL)
-                  .nullable(edmProperty == null ? returnType.isNullable() : 
edmProperty.isNullable())
-                  .maxLength(edmProperty == null ? returnType.getMaxLength() : 
edmProperty.getMaxLength())
-                  .precision(edmProperty == null ? returnType.getPrecision() : 
edmProperty.getPrecision())
-                  .scale(edmProperty == null ? returnType.getScale() : 
edmProperty.getScale())
-                  .unicode(edmProperty == null ? null : 
edmProperty.isUnicode())
-                  .build()).getContent());
-          break;
-        case COLLECTION_COMPLEX:
-          response.setContent(serializer.complexCollection(serviceMetadata, 
(EdmComplexType) type, property,
-              ComplexSerializerOptions.with().contextURL(contextURL)
-                  .expand(expand).select(select)
-                  .build()).getContent());
-          break;
-        default:
-          break;
-        }
-        response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+        final ODataFormat format = ODataFormat.fromContentType(contentType);
+        final SerializerResult result = serializeProperty(entity, 
edmEntitySet, path, property, edmProperty,
+            type, returnType, representationType, format, expand, select);
+        response.setContent(result.getContent());
         response.setHeader(HttpHeader.CONTENT_TYPE, 
contentType.toContentTypeString());
       }
+      if (entity != null && entity.getETag() != null) {
+        response.setHeader(HttpHeader.ETAG, entity.getETag());
+      }
+    }
+  }
+
+  private void updateProperty(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
+      final ContentType requestFormat, final ContentType responseFormat, final 
RepresentationType representationType)
+          throws ODataApplicationException, DeserializerException, 
SerializerException {
+    final UriInfoResource resource = uriInfo.asUriInfoResource();
+    validatePath(resource);
+    final EdmEntitySet edmEntitySet = getEdmEntitySet(resource);
+
+    Entity entity = readEntity(uriInfo);
+    checkChangePreconditions(entity.getETag(),
+        request.getHeaders(HttpHeader.IF_MATCH),
+        request.getHeaders(HttpHeader.IF_NONE_MATCH));
+
+    final List<UriResource> resourceParts = resource.getUriResourceParts();
+    final List<String> path = getPropertyPath(resourceParts, 0);
+    final EdmProperty edmProperty = ((UriResourceProperty) 
resourceParts.get(resourceParts.size() - 1))
+        .getProperty();
+
+    checkRequestFormat(requestFormat);
+    final Property changedProperty = 
odata.createDeserializer(ODataFormat.fromContentType(requestFormat))
+        .property(request.getBody(), edmProperty).getProperty();
+    if (changedProperty.isNull() && !edmProperty.isNullable()) {
+      throw new ODataApplicationException("Not nullable.", 
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
+    }
+
+    Property property = getPropertyData(entity, path);
+
+    dataProvider.updateProperty(edmProperty, property, changedProperty, 
request.getMethod() == HttpMethod.PATCH);
+    dataProvider.updateETag(entity);
+
+    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+    final ODataFormat format = ODataFormat.fromContentType(responseFormat);
+    final SerializerResult result = serializeProperty(entity, edmEntitySet, 
path, property, edmProperty,
+        edmProperty.getType(), null, representationType, format, null, null);
+    response.setContent(result.getContent());
+    response.setHeader(HttpHeader.CONTENT_TYPE, 
responseFormat.toContentTypeString());
+    if (entity.getETag() != null) {
+      response.setHeader(HttpHeader.ETAG, entity.getETag());
     }
   }
 
-  private void deleteProperty(final ODataResponse response, final UriInfo 
uriInfo) throws ODataApplicationException {
+  private void deleteProperty(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo)
+      throws ODataApplicationException {
     final UriInfoResource resource = uriInfo.asUriInfoResource();
     validatePath(resource);
     getEdmEntitySet(uriInfo); // including checks
 
+    Entity entity = readEntity(uriInfo);
+    checkChangePreconditions(entity.getETag(),
+        request.getHeaders(HttpHeader.IF_MATCH),
+        request.getHeaders(HttpHeader.IF_NONE_MATCH));
+
     final List<UriResource> resourceParts = resource.getUriResourceParts();
     final List<String> path = getPropertyPath(resourceParts, 0);
 
-    final Property property = getPropertyData(readEntity(uriInfo), path);
+    Property property = getPropertyData(entity, path);
 
     final EdmProperty edmProperty = ((UriResourceProperty) 
resourceParts.get(resourceParts.size() - 1))
         .getProperty();
 
     if (edmProperty.isNullable()) {
       property.setValue(property.getValueType(), edmProperty.isCollection() ? 
Collections.emptyList() : null);
+      dataProvider.updateETag(entity);
       response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
     } else {
       throw new ODataApplicationException("Not nullable.", 
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
@@ -436,6 +469,54 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
     return result.toString();
   }
 
+  private SerializerResult serializeProperty(final Entity entity, final 
EdmEntitySet edmEntitySet,
+      final List<String> path, final Property property, final EdmProperty 
edmProperty,
+      final EdmType type, final EdmReturnType returnType,
+      final RepresentationType representationType, final ODataFormat format,
+      final ExpandOption expand, final SelectOption select) throws 
SerializerException {
+    ODataSerializer serializer = odata.createSerializer(format);
+    final ContextURL contextURL = format == ODataFormat.JSON_NO_METADATA ? 
null :
+        getContextUrl(edmEntitySet, entity, path, type, representationType, 
expand, select);
+    SerializerResult result = null;
+    switch (representationType) {
+    case PRIMITIVE:
+      result = serializer.primitive((EdmPrimitiveType) type, property,
+          PrimitiveSerializerOptions.with().contextURL(contextURL)
+              .nullable(edmProperty == null ? returnType.isNullable() : 
edmProperty.isNullable())
+              .maxLength(edmProperty == null ? returnType.getMaxLength() : 
edmProperty.getMaxLength())
+              .precision(edmProperty == null ? returnType.getPrecision() : 
edmProperty.getPrecision())
+              .scale(edmProperty == null ? returnType.getScale() : 
edmProperty.getScale())
+              .unicode(edmProperty == null ? null : edmProperty.isUnicode())
+              .build());
+      break;
+    case COMPLEX:
+      result = serializer.complex(serviceMetadata, (EdmComplexType) type, 
property,
+          ComplexSerializerOptions.with().contextURL(contextURL)
+              .expand(expand).select(select)
+              .build());
+      break;
+    case COLLECTION_PRIMITIVE:
+      result = serializer.primitiveCollection((EdmPrimitiveType) type, 
property,
+          PrimitiveSerializerOptions.with().contextURL(contextURL)
+              .nullable(edmProperty == null ? returnType.isNullable() : 
edmProperty.isNullable())
+              .maxLength(edmProperty == null ? returnType.getMaxLength() : 
edmProperty.getMaxLength())
+              .precision(edmProperty == null ? returnType.getPrecision() : 
edmProperty.getPrecision())
+              .scale(edmProperty == null ? returnType.getScale() : 
edmProperty.getScale())
+              .unicode(edmProperty == null ? null : edmProperty.isUnicode())
+              .build());
+      break;
+    case COLLECTION_COMPLEX:
+      result = serializer.complexCollection(serviceMetadata, (EdmComplexType) 
type, property,
+          ComplexSerializerOptions.with().contextURL(contextURL)
+              .expand(expand).select(select)
+              .build());
+      break;
+    default:
+      break;
+    }
+    return result;
+  }
+
   private ContextURL getContextUrl(final EdmEntitySet entitySet, final Entity 
entity, final List<String> path,
       final EdmType type, final RepresentationType representationType,
       final ExpandOption expand, final SelectOption select) throws 
SerializerException {
@@ -455,7 +536,7 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
   }
 
   @Override
-  public void readPrimitiveValue(final ODataRequest request, final 
ODataResponse response, final UriInfo uriInfo,
+  public void readPrimitiveValue(final ODataRequest request, ODataResponse 
response, final UriInfo uriInfo,
       final ContentType contentType) throws ODataApplicationException, 
SerializerException {
     final UriInfoResource resource = uriInfo.asUriInfoResource();
     validateOptions(resource);
@@ -466,6 +547,12 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
     final List<String> path = getPropertyPath(resourceParts, 1);
 
     final Entity entity = readEntity(uriInfo);
+    if (entity != null && entity.getETag() != null) {
+      checkReadPreconditions(entity.getETag(),
+          request.getHeaders(HttpHeader.IF_MATCH),
+          request.getHeaders(HttpHeader.IF_NONE_MATCH));
+    }
+
     final Property property = entity == null ?
         
getPropertyData(dataProvider.readFunctionPrimitiveComplex(((UriResourceFunction)
 resourceParts.get(0))
             .getFunction(),
@@ -496,6 +583,9 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
       response.setHeader(HttpHeader.CONTENT_TYPE, 
contentType.toContentTypeString());
       response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     }
+    if (entity != null && entity.getETag() != null) {
+      response.setHeader(HttpHeader.ETAG, entity.getETag());
+    }
   }
 
   private void validatePath(final UriInfoResource uriInfo) throws 
ODataApplicationException {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7ad5b0fb/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
index e5991bd..3f5c7bb 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
@@ -18,19 +18,21 @@
  */
 package org.apache.olingo.server.tecsvc.processor;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntityCollection;
 import org.apache.olingo.commons.api.data.Link;
-import org.apache.olingo.commons.api.edm.EdmAction;
 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.EdmFunction;
 import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.EtagInformation;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.ServiceMetadata;
@@ -218,12 +220,49 @@ public abstract class TechnicalProcessor implements 
Processor {
     }
   }
 
-  protected EdmAction checkBoundAndExtractAction(final UriInfo uriInfo) throws 
ODataApplicationException {
+  protected void blockBoundActions(final UriInfo uriInfo) throws 
ODataApplicationException {
     final List<UriResource> uriResourceParts = 
uriInfo.asUriInfoResource().getUriResourceParts();
-    if (uriResourceParts.size() > 1) {
+    if (uriResourceParts.size() > 1
+        && uriResourceParts.get(uriResourceParts.size() - 1) instanceof 
UriResourceAction) {
       throw new ODataApplicationException("Bound actions are not supported 
yet.",
           HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
     }
-    return ((UriResourceAction) uriResourceParts.get(0)).getAction();
+  }
+
+  protected void checkRequestFormat(final ContentType requestFormat) throws 
ODataApplicationException {
+    if (requestFormat == null) {
+      throw new ODataApplicationException("The content type has not been set 
in the request.",
+          HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
+    }
+  }
+
+  protected void checkReadPreconditions(final String eTag,
+      final Collection<String> ifMatchHeaders, final Collection<String> 
ifNoneMatchHeaders)
+          throws ODataApplicationException {
+    if (eTag != null) {
+      final EtagInformation ifMatch = 
odata.createEtagInformation(ifMatchHeaders);
+      if (!ifMatch.isMatchedBy(eTag) && !ifMatch.getEtags().isEmpty()) {
+        throw new ODataApplicationException("The If-Match precondition is not 
fulfilled.",
+            HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.ROOT);
+      }
+      if (odata.createEtagInformation(ifNoneMatchHeaders).isMatchedBy(eTag)) {
+        throw new ODataApplicationException("The entity has not been 
modified.",
+            HttpStatusCode.NOT_MODIFIED.getStatusCode(), Locale.ROOT);
+      }
+    }
+  }
+
+  protected void checkChangePreconditions(final String eTag,
+      final Collection<String> ifMatchHeaders, final Collection<String> 
ifNoneMatchHeaders)
+          throws ODataApplicationException {
+    if (eTag != null) {
+      final EtagInformation ifMatch = 
odata.createEtagInformation(ifMatchHeaders);
+      final EtagInformation ifNoneMatch = 
odata.createEtagInformation(ifNoneMatchHeaders);
+      if (!ifMatch.isMatchedBy(eTag) && !ifMatch.getEtags().isEmpty()
+          || ifNoneMatch.isMatchedBy(eTag)) {
+        throw new ODataApplicationException("The preconditions are not 
fulfilled.",
+            HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.ROOT);
+      }
+    }
   }
 }

Reply via email to