[OLINGO-663] metadata ETag

Change-Id: I93bcf78b9be2a34e46c4f2f4d8ec739faf197951

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/a604fa78
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/a604fa78
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/a604fa78

Branch: refs/heads/master
Commit: a604fa78f2a85dbd612da7f5c0546e3424ae2698
Parents: 4cd8752
Author: Klaus Straubinger <[email protected]>
Authored: Fri Jun 5 15:44:05 2015 +0200
Committer: Christian Amend <[email protected]>
Committed: Mon Jun 8 10:01:27 2015 +0200

----------------------------------------------------------------------
 .../fit/tecsvc/client/BatchClientITCase.java    |  10 +-
 .../fit/tecsvc/client/ConditionalITCase.java    |  35 +++
 .../tecsvc/client/PrimitiveComplexITCase.java   |  18 +-
 .../fit/tecsvc/http/BasicBatchITCase.java       |   2 +-
 .../retrieve/EdmMetadataRequestImpl.java        |  31 ++-
 .../retrieve/XMLMetadataRequestImpl.java        |  21 +-
 .../olingo/server/api/ServiceMetadata.java      |   6 +-
 .../server/api/processor/DefaultProcessor.java  |   2 +-
 .../server/api/serializer/ODataSerializer.java  |  88 +++---
 .../server/core/responses/PropertyResponse.java |   6 +-
 .../core/responses/ServiceDocumentResponse.java |   2 +-
 .../serializer/json/ODataJsonSerializer.java    |  61 +++--
 .../json/ServiceDocumentJsonSerializer.java     |  33 ++-
 .../serializer/xml/ODataXmlSerializerImpl.java  |  12 +-
 .../olingo/server/core/etag/ETagHelperTest.java |  79 ++++++
 .../olingo/server/core/etag/ETagParserTest.java |   2 +-
 .../server/tecsvc/MetadataETagSupport.java      |  40 +++
 .../olingo/server/tecsvc/TechnicalServlet.java  |  17 +-
 .../processor/TechnicalActionProcessor.java     |   6 +-
 .../TechnicalPrimitiveComplexProcessor.java     |   7 +-
 .../json/ODataJsonSerializerTest.java           | 270 ++++++++++---------
 .../serializer/json/ServiceDocumentTest.java    | 108 ++------
 .../server/sample/processor/CarsProcessor.java  |   2 +-
 23 files changed, 526 insertions(+), 332 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
index 63c9a7f..932f9b1 100644
--- 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
+++ 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
@@ -170,7 +170,7 @@ public class BatchClientITCase extends AbstractTestITCase {
     assertEquals(1, oDataResonse.getHeader("OData-Version").size());
     assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
     assertEquals(1, oDataResonse.getHeader("Content-Length").size());
-    assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+    assertEquals("582", oDataResonse.getHeader("Content-Length").toArray()[0]);
     assertEquals("application/json;odata.metadata=minimal", 
oDataResonse.getContentType());
   }
 
@@ -202,7 +202,7 @@ public class BatchClientITCase extends AbstractTestITCase {
     assertEquals(1, oDataResonse.getHeader("OData-Version").size());
     assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
     assertEquals(1, oDataResonse.getHeader("Content-Length").size());
-    assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+    assertEquals("582", oDataResonse.getHeader("Content-Length").toArray()[0]);
     assertEquals("application/json;odata.metadata=minimal", 
oDataResonse.getContentType());
   }
 
@@ -234,7 +234,7 @@ public class BatchClientITCase extends AbstractTestITCase {
     assertEquals(1, oDataResonse.getHeader("OData-Version").size());
     assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
     assertEquals(1, oDataResonse.getHeader("Content-Length").size());
-    assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+    assertEquals("582", oDataResonse.getHeader("Content-Length").toArray()[0]);
     assertEquals("application/json;odata.metadata=minimal", 
oDataResonse.getContentType());
 
     // Check second get request
@@ -334,7 +334,7 @@ public class BatchClientITCase extends AbstractTestITCase {
     assertEquals(1, oDataResonse.getHeader("OData-Version").size());
     assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
     assertEquals(1, oDataResonse.getHeader("Content-Length").size());
-    assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+    assertEquals("582", oDataResonse.getHeader("Content-Length").toArray()[0]);
     assertEquals("application/json;odata.metadata=minimal", 
oDataResonse.getContentType());
 
     // Check second get request
@@ -357,7 +357,7 @@ public class BatchClientITCase extends AbstractTestITCase {
     assertEquals(1, oDataResonse.getHeader("OData-Version").size());
     assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
     assertEquals(1, oDataResonse.getHeader("Content-Length").size());
-    assertEquals("446", oDataResonse.getHeader("Content-Length").toArray()[0]);
+    assertEquals("490", oDataResonse.getHeader("Content-Length").toArray()[0]);
     assertEquals("application/json;odata.metadata=minimal", 
oDataResonse.getContentType());
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/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 796c2c4..c5d2926 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
@@ -35,16 +35,20 @@ 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.EdmMetadataRequest;
 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.ODataServiceDocumentRequest;
 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.domain.ClientServiceDocument;
 import org.apache.olingo.client.api.http.HttpClientException;
 import org.apache.olingo.client.core.ODataClientFactory;
+import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
@@ -65,6 +69,37 @@ public final class ConditionalITCase extends 
AbstractBaseTestITCase {
       
.appendEntitySetSegment("ESMedia").appendKeySegment(1).appendValueSegment().build();
 
   @Test
+  public void readServiceDocument() throws Exception {
+    ODataServiceDocumentRequest request = client.getRetrieveRequestFactory()
+        .getServiceDocumentRequest(TecSvcConst.BASE_URI);
+    ODataRetrieveResponse<ClientServiceDocument> response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+
+    request = 
client.getRetrieveRequestFactory().getServiceDocumentRequest(TecSvcConst.BASE_URI);
+    request.setIfNoneMatch(response.getETag());
+    assertEquals(HttpStatusCode.NOT_MODIFIED.getStatusCode(), 
request.execute().getStatusCode());
+
+    request = 
client.getRetrieveRequestFactory().getServiceDocumentRequest(TecSvcConst.BASE_URI);
+    request.setIfMatch("W/\"0\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
+  public void readMetadataDocument() throws Exception {
+    EdmMetadataRequest request = 
client.getRetrieveRequestFactory().getMetadataRequest(TecSvcConst.BASE_URI);
+    ODataRetrieveResponse<Edm> response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+
+    request = 
client.getRetrieveRequestFactory().getMetadataRequest(TecSvcConst.BASE_URI);
+    request.setIfNoneMatch(response.getETag());
+    assertEquals(HttpStatusCode.NOT_MODIFIED.getStatusCode(), 
request.execute().getStatusCode());
+
+    request = 
client.getRetrieveRequestFactory().getMetadataRequest(TecSvcConst.BASE_URI);
+    request.setIfMatch("W/\"0\"");
+    executeAndExpectError(request, HttpStatusCode.PRECONDITION_FAILED);
+  }
+
+  @Test
   public void readWithWrongIfMatch() throws Exception {
     ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(uriEntity);
     request.setIfMatch("W/\"1\"");

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/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 d71e6a0..04f9768 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
@@ -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
@@ -87,10 +87,9 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
             .appendPropertySegment("PropertyString")
             .build());
     ODataRetrieveResponse<ClientProperty> response = request.execute();
-    String expectedResult =
-        "{\"@odata.context\":\"$metadata#ESTwoPrim(32766)/PropertyString\"," +
-            "\"value\":\"Test String1\"}";
-    assertEquals(expectedResult, IOUtils.toString(response.getRawResponse(), 
"UTF-8"));
+    String actualResult = IOUtils.toString(response.getRawResponse(), "UTF-8");
+    
assertTrue(actualResult.startsWith("{\"@odata.context\":\"$metadata#ESTwoPrim(32766)/PropertyString\","));
+    assertTrue(actualResult.endsWith("\"value\":\"Test String1\"}"));
   }
 
   @Test
@@ -167,10 +166,9 @@ public class PrimitiveComplexITCase extends 
AbstractBaseTestITCase {
             .appendPropertySegment("PropertyComp")
             .build());
     ODataRetrieveResponse<ClientProperty> response = request.execute();
-    String expectedResult =
-        "{\"@odata.context\":\"$metadata#ESMixPrimCollComp(7)/PropertyComp\"," 
+
-            "\"PropertyInt16\":222,\"PropertyString\":\"TEST B\"}";
-    assertEquals(expectedResult, IOUtils.toString(response.getRawResponse(), 
"UTF-8"));
+    String actualResult = IOUtils.toString(response.getRawResponse(), "UTF-8");
+    
assertTrue(actualResult.startsWith("{\"@odata.context\":\"$metadata#ESMixPrimCollComp(7)/PropertyComp\","));
+    
assertTrue(actualResult.endsWith("\"PropertyInt16\":222,\"PropertyString\":\"TEST
 B\"}"));
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicBatchITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicBatchITCase.java 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicBatchITCase.java
index 11b7a3e..3964d3b 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicBatchITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicBatchITCase.java
@@ -60,7 +60,7 @@ public class BasicBatchITCase extends AbstractBaseTestITCase {
     assertEquals("HTTP/1.1 200 OK", reader.readLine());
     assertEquals("OData-Version: 4.0", reader.readLine());
     assertEquals("Content-Type: application/json;odata.metadata=minimal", 
reader.readLine());
-    assertEquals("Content-Length: 538", reader.readLine());
+    assertEquals("Content-Length: 582", reader.readLine());
     blankLine(reader);
 
     reader.close();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/EdmMetadataRequestImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/EdmMetadataRequestImpl.java
 
b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/EdmMetadataRequestImpl.java
index 8366304..ee5e2e7 100644
--- 
a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/EdmMetadataRequestImpl.java
+++ 
b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/EdmMetadataRequestImpl.java
@@ -19,10 +19,13 @@
 package org.apache.olingo.client.core.communication.request.retrieve;
 
 import java.net.URI;
+import java.util.Collection;
 
 import org.apache.http.client.HttpClient;
 import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.communication.header.HeaderName;
 import 
org.apache.olingo.client.api.communication.request.retrieve.EdmMetadataRequest;
+import 
org.apache.olingo.client.api.communication.request.retrieve.XMLMetadataRequest;
 import 
org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.client.api.edm.xml.XMLMetadata;
 import org.apache.olingo.commons.api.edm.Edm;
@@ -43,8 +46,17 @@ class EdmMetadataRequestImpl extends 
AbstractMetadataRequestImpl<Edm> implements
 
   private EdmMetadataResponseImpl getPrivateResponse() {
     if (privateResponse == null) {
-      final ODataRetrieveResponse<XMLMetadata> xmlMetadataResponse =
-              
odataClient.getRetrieveRequestFactory().getXMLMetadataRequest(serviceRoot).execute();
+      XMLMetadataRequest request = 
odataClient.getRetrieveRequestFactory().getXMLMetadataRequest(serviceRoot);
+      if (getPrefer() != null) {
+        request.setPrefer(getPrefer());
+      }
+      if (getIfMatch() != null) {
+        request.setIfMatch(getIfMatch());
+      }
+      if (getIfNoneMatch() != null) {
+        request.setIfNoneMatch(getIfNoneMatch());
+      }
+      final ODataRetrieveResponse<XMLMetadata> xmlMetadataResponse = 
request.execute();
 
       privateResponse = new EdmMetadataResponseImpl(odataClient, httpClient, 
xmlMetadataResponse);
     }
@@ -90,6 +102,21 @@ class EdmMetadataRequestImpl extends 
AbstractMetadataRequestImpl<Edm> implements
       return xmlMetadataResponse.getStatusMessage();
     }
 
+    @Override
+    public Collection<String> getHeaderNames() {
+      return xmlMetadataResponse.getHeaderNames();
+    }
+
+    @Override
+    public Collection<String> getHeader(final String name) {
+      return xmlMetadataResponse.getHeader(name);
+    }
+
+    @Override
+    public Collection<String> getHeader(final HeaderName name) {
+      return xmlMetadataResponse.getHeader(name);
+    }
+
     public XMLMetadata getXMLMetadata() {
       if (metadata == null) {
         try {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/XMLMetadataRequestImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/XMLMetadataRequestImpl.java
 
b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/XMLMetadataRequestImpl.java
index ff49b7b..2e432c8 100644
--- 
a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/XMLMetadataRequestImpl.java
+++ 
b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/XMLMetadataRequestImpl.java
@@ -35,6 +35,7 @@ import 
org.apache.olingo.commons.api.edm.provider.CsdlAnnotation;
 import org.apache.olingo.commons.api.edm.provider.CsdlAnnotations;
 import org.apache.olingo.commons.api.edm.provider.CsdlSchema;
 import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
 
 public class XMLMetadataRequestImpl
     extends AbstractMetadataRequestImpl<XMLMetadata>
@@ -46,9 +47,21 @@ public class XMLMetadataRequestImpl
 
   @Override
   public ODataRetrieveResponse<XMLMetadata> execute() {
-    final SingleXMLMetadatRequestImpl rootReq = new 
SingleXMLMetadatRequestImpl(odataClient, uri, null);
+    SingleXMLMetadatRequestImpl rootReq = new 
SingleXMLMetadatRequestImpl(odataClient, uri, null);
+    if (getPrefer() != null) {
+      rootReq.setPrefer(getPrefer());
+    }
+    if (getIfMatch() != null) {
+      rootReq.setIfMatch(getIfMatch());
+    }
+    if (getIfNoneMatch() != null) {
+      rootReq.setIfNoneMatch(getIfNoneMatch());
+    }
     final ODataRetrieveResponse<XMLMetadata> rootRes = rootReq.execute();
 
+    if (rootRes.getStatusCode() != HttpStatusCode.OK.getStatusCode()) {
+      return rootRes;
+    }
     final XMLMetadataResponseImpl response =
         new XMLMetadataResponseImpl(odataClient, httpClient, 
rootReq.getHttpResponse(), rootRes.getBody());
 
@@ -176,12 +189,8 @@ public class XMLMetadataRequestImpl
         final HttpResponse res, final XMLMetadata metadata) {
 
       super(odataClient, httpClient, null);
+      initFromHttpResponse(res);
       this.metadata = metadata;
-
-      statusCode = res.getStatusLine().getStatusCode();
-      statusMessage = res.getStatusLine().getReasonPhrase();
-
-      hasBeenInitialized = true;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
index 3cecd8b..a12f1ac 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
@@ -46,7 +46,9 @@ public interface ServiceMetadata {
    * @return list of defined emdx references of this service
    */
   List<EdmxReference> getReferences();
-  
+
+  /**
+   * @return metadata ETag support
+   */
   ServiceMetadataETagSupport getServiceMetadataETagSupport();
-  
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
index f73ed27..c209764 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
@@ -73,7 +73,7 @@ public class DefaultProcessor implements MetadataProcessor, 
ServiceDocumentProce
       response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode());
     } else {
       ODataSerializer serializer = 
odata.createSerializer(ODataFormat.fromContentType(requestedContentType));
-      response.setContent(serializer.serviceDocument(serviceMetadata.getEdm(), 
null).getContent());
+      response.setContent(serializer.serviceDocument(serviceMetadata, 
null).getContent());
       response.setStatusCode(HttpStatusCode.OK.getStatusCode());
       response.setHeader(HttpHeader.CONTENT_TYPE, 
requestedContentType.toContentTypeString());
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
index 470d090..3727064 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
@@ -22,7 +22,6 @@ import org.apache.olingo.commons.api.data.ContextURL;
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntityCollection;
 import org.apache.olingo.commons.api.data.Property;
-import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmComplexType;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
@@ -38,10 +37,10 @@ public interface ODataSerializer {
 
   /**
    * Writes the service document into an InputStream.
-   * @param edm the Entity Data Model
-   * @param serviceRoot the service-root URI of this OData service
+   * @param serviceMetadata the metadata information for the service
+   * @param serviceRoot     the service-root URI of this OData service
    */
-  SerializerResult serviceDocument(Edm edm, String serviceRoot) throws 
SerializerException;
+  SerializerResult serviceDocument(ServiceMetadata serviceMetadata, String 
serviceRoot) throws SerializerException;
 
   /**
    * Writes the metadata document into an InputStream.
@@ -52,91 +51,86 @@ public interface ODataSerializer {
   /**
    * Writes an ODataError into an InputStream.
    * @param error the main error
-   * @return inputStream containing the OData-formatted error
    */
   SerializerResult error(ODataServerError error) throws SerializerException;
 
   /**
    * Writes entity-collection data into an InputStream.
-   * @param metadata Metadata for the service
+   * @param metadata   metadata for the service
    * @param entityType the {@link EdmEntityType}
-   * @param entitySet the data of the entity set
-   * @param options options for the serializer
+   * @param entitySet  the data of the entity set
+   * @param options    options for the serializer
    */
   SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType 
entityType,
       EntityCollection entitySet, EntityCollectionSerializerOptions options) 
throws SerializerException;
 
   /**
    * Writes entity data into an InputStream.
-   * @param metadata Metadata for the service
+   * @param metadata   metadata for the service
    * @param entityType the {@link EdmEntityType}
-   * @param entity the data of the entity
-   * @param options options for the serializer
+   * @param entity     the data of the entity
+   * @param options    options for the serializer
    */
   SerializerResult entity(ServiceMetadata metadata, EdmEntityType entityType, 
Entity entity,
       EntitySerializerOptions options) throws SerializerException;
 
   /**
    * Writes primitive-type instance data into an InputStream.
-   * @param type primitive type
+   * @param metadata metadata for the service
+   * @param type     primitive type
    * @param property property value
-   * @param options options for the serializer
+   * @param options  options for the serializer
    */
-  SerializerResult primitive(EdmPrimitiveType type, Property property, 
PrimitiveSerializerOptions options)
-      throws SerializerException;
+  SerializerResult primitive(ServiceMetadata metadata, EdmPrimitiveType type, 
Property property,
+      PrimitiveSerializerOptions options) throws SerializerException;
 
   /**
    * Writes complex-type instance data into an InputStream.
-   * @param metadata Metadata for the service
-   * @param type complex type
+   * @param metadata metadata for the service
+   * @param type     complex type
    * @param property property value
-   * @param options options for the serializer
+   * @param options  options for the serializer
    */
   SerializerResult complex(ServiceMetadata metadata, EdmComplexType type, 
Property property,
       ComplexSerializerOptions options) throws SerializerException;
 
   /**
    * Writes data of a collection of primitive-type instances into an 
InputStream.
-   * @param type primitive type
+   * @param metadata metadata for the service
+   * @param type     primitive type
    * @param property property value
-   * @param options options for the serializer
+   * @param options  options for the serializer
    */
-  SerializerResult primitiveCollection(EdmPrimitiveType type, Property 
property, PrimitiveSerializerOptions options)
-      throws SerializerException;
+  SerializerResult primitiveCollection(ServiceMetadata metadata, 
EdmPrimitiveType type, Property property,
+      PrimitiveSerializerOptions options) throws SerializerException;
 
   /**
    * Writes data of a collection of complex-type instances into an InputStream.
-   * @param metadata Metadata for the service
-   * @param type complex type
+   * @param metadata metadata for the service
+   * @param type     complex type
    * @param property property value
-   * @param options options for the serializer
+   * @param options  options for the serializer
    */
   SerializerResult complexCollection(ServiceMetadata metadata, EdmComplexType 
type, Property property,
       ComplexSerializerOptions options) throws SerializerException;
-  
+
   /**
-   * Writes a single entity reference into an InputStream
-   * 
-   * @param metadata              Metadata for the service
-   * @param edmEntitySet          {@link EdmEntitySet}
-   * @param entity                data of the entity
-   * @param contextUrl            {@link ContextURL}
-   * @return Serialized           entity reference
-   * @throws SerializerException
+   * Writes a single entity reference into an InputStream.
+   * @param metadata     metadata for the service
+   * @param edmEntitySet {@link EdmEntitySet}
+   * @param entity       data of the entity
+   * @param contextURL   {@link ContextURL}
    */
-  SerializerResult reference(ServiceMetadata metadata, EdmEntitySet 
edmEntitySet, Entity entity, 
-      final ContextURL contextUrl) throws SerializerException;
-  
+  SerializerResult reference(ServiceMetadata metadata, EdmEntitySet 
edmEntitySet, Entity entity,
+      ContextURL contextURL) throws SerializerException;
+
   /**
-   * Writes entity collection references into an InputStream
-   * 
-   * @param metadata              Metadata for the service
-   * @param edmEntitySet          {@link EdmEntitySet}
-   * @param entityCollection      data of the entity collection
-   * @param contextURL 
-   * @return Serialized           entity reference
-   * @throws SerializerException
+   * Writes entity-collection references into an InputStream.
+   * @param metadata         metadata for the service
+   * @param edmEntitySet     {@link EdmEntitySet}
+   * @param entityCollection data of the entity collection
+   * @param contextURL       {@link ContextURL}
    */
-  SerializerResult referenceCollection(ServiceMetadata metadata, EdmEntitySet 
edmEntitySet, 
-      EntityCollection entityCollection, final ContextURL contextURL) throws 
SerializerException;
+  SerializerResult referenceCollection(ServiceMetadata metadata, EdmEntitySet 
edmEntitySet,
+      EntityCollection entityCollection, ContextURL contextURL) throws 
SerializerException;
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
index 1353ece..0cf4ff2 100644
--- 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
+++ 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
@@ -120,9 +120,11 @@ public class PropertyResponse extends ServiceResponse {
   private void writePrimitiveProperty(EdmPrimitiveType type, Property property)
       throws SerializerException {
     if(this.collection) {
-      this.response.setContent(this.serializer.primitiveCollection(type, 
property, this.primitiveOptions).getContent());
+      this.response.setContent(
+          this.serializer.primitiveCollection(metadata, type, property, 
this.primitiveOptions).getContent());
     } else {
-      this.response.setContent(this.serializer.primitive(type, property, 
this.primitiveOptions).getContent());
+      this.response.setContent(
+          this.serializer.primitive(metadata, type, property, 
this.primitiveOptions).getContent());
     }
     writeOK(this.responseContentType.toContentTypeString());
     close();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
index 43bafbc..407510d 100644
--- 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
+++ 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
@@ -51,7 +51,7 @@ public class ServiceDocumentResponse extends ServiceResponse {
   public void writeServiceDocument(String serviceRoot)
       throws ODataLibraryException {
     assert (!isClosed());
-    
this.response.setContent(this.serializer.serviceDocument(this.metadata.getEdm(),
 serviceRoot).getContent());
+    this.response.setContent(this.serializer.serviceDocument(metadata, 
serviceRoot).getContent());
     writeOK(this.responseContentType.toContentTypeString());
     close();
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index 08f557a..168adfa 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
@@ -31,7 +31,6 @@ import org.apache.olingo.commons.api.data.EntityCollection;
 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.EdmComplexType;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
@@ -80,7 +79,8 @@ public class ODataJsonSerializer implements ODataSerializer {
   }
 
   @Override
-  public SerializerResult serviceDocument(final Edm edm, final String 
serviceRoot) throws SerializerException {
+  public SerializerResult serviceDocument(final ServiceMetadata metadata, 
final String serviceRoot)
+      throws SerializerException {
     CircleStreamBuffer buffer;
     JsonGenerator gen = null;
 
@@ -89,7 +89,7 @@ public class ODataJsonSerializer implements ODataSerializer {
       gen = new JsonFactory().createGenerator(buffer.getOutputStream())
           .setPrettyPrinter(new DefaultPrettyPrinter());
 
-      new ServiceDocumentJsonSerializer(edm, 
serviceRoot).writeServiceDocument(gen);
+      new ServiceDocumentJsonSerializer(metadata, serviceRoot, 
format).writeServiceDocument(gen);
 
       gen.close();
 
@@ -146,6 +146,8 @@ public class ODataJsonSerializer implements ODataSerializer 
{
             ContextURLBuilder.create(contextURL).toASCIIString());
       }
 
+      writeMetadataETag(metadata, json);
+
       if (options != null && options.getCount() != null && 
options.getCount().getValue()
           && entitySet.getCount() != null) {
         writeCount(entitySet, json);
@@ -196,6 +198,16 @@ public class ODataJsonSerializer implements 
ODataSerializer {
     return contextURL;
   }
 
+  private void writeMetadataETag(final ServiceMetadata metadata, JsonGenerator 
json) throws IOException {
+    if (format != ODataFormat.JSON_NO_METADATA
+        && metadata != null
+        && metadata.getServiceMetadataETagSupport() != null
+        && metadata.getServiceMetadataETagSupport().getMetadataETag() != null) 
{
+      json.writeStringField(Constants.JSON_METADATA_ETAG,
+          metadata.getServiceMetadataETagSupport().getMetadataETag());
+    }
+  }
+
   protected void writeEntitySet(final ServiceMetadata metadata, final 
EdmEntityType entityType,
       final EntityCollection entitySet, final ExpandOption expand, final 
SelectOption select,
       final boolean onlyReference, final JsonGenerator json) throws 
IOException,
@@ -219,8 +231,9 @@ public class ODataJsonSerializer implements ODataSerializer 
{
       throws IOException, SerializerException {
     json.writeStartObject();
     if (format != ODataFormat.JSON_NO_METADATA) {
-      if (contextURL != null) {
+      if (contextURL != null) { // top-level entity
         json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
+        writeMetadataETag(metadata, json);
       }
       if (entity.getETag() != null) {
         json.writeStringField(Constants.JSON_ETAG, entity.getETag());
@@ -529,8 +542,8 @@ public class ODataJsonSerializer implements ODataSerializer 
{
   }
 
   @Override
-  public SerializerResult primitive(final EdmPrimitiveType type, final 
Property property,
-      final PrimitiveSerializerOptions options) throws SerializerException {
+  public SerializerResult primitive(final ServiceMetadata metadata, final 
EdmPrimitiveType type,
+      final Property property, final PrimitiveSerializerOptions options) 
throws SerializerException {
     final ContextURL contextURL = checkContextURL(options == null ? null : 
options.getContextURL());
     CircleStreamBuffer buffer = new CircleStreamBuffer();
     try {
@@ -539,6 +552,7 @@ public class ODataJsonSerializer implements ODataSerializer 
{
       if (contextURL != null) {
         json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
       }
+      writeMetadataETag(metadata, json);
       if (property.isNull()) {
         throw new SerializerException("Property value can not be null.", 
SerializerException.MessageKeys.NULL_INPUT);
       } else {
@@ -572,6 +586,7 @@ public class ODataJsonSerializer implements ODataSerializer 
{
       if (contextURL != null) {
         json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
       }
+      writeMetadataETag(metadata, json);
       EdmComplexType resolvedType = resolveComplexType(metadata, type, 
property.getType());
       if (!resolvedType.equals(type)) {
         json.writeStringField(Constants.JSON_TYPE, "#" + property.getType());
@@ -593,8 +608,8 @@ public class ODataJsonSerializer implements ODataSerializer 
{
   }
 
   @Override
-  public SerializerResult primitiveCollection(final EdmPrimitiveType type, 
final Property property,
-      final PrimitiveSerializerOptions options) throws SerializerException {
+  public SerializerResult primitiveCollection(final ServiceMetadata metadata, 
final EdmPrimitiveType type,
+      final Property property, final PrimitiveSerializerOptions options) 
throws SerializerException {
     final ContextURL contextURL = checkContextURL(options == null ? null : 
options.getContextURL());
     CircleStreamBuffer buffer = new CircleStreamBuffer();
     try {
@@ -603,6 +618,7 @@ public class ODataJsonSerializer implements ODataSerializer 
{
       if (contextURL != null) {
         json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
       }
+      writeMetadataETag(metadata, json);
       json.writeFieldName(Constants.VALUE);
       writePrimitiveCollection(type, property,
           options.isNullable(), options.getMaxLength(), 
options.getPrecision(), options.getScale(),
@@ -632,6 +648,7 @@ public class ODataJsonSerializer implements ODataSerializer 
{
       if (contextURL != null) {
         json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
       }
+      writeMetadataETag(metadata, json);
       json.writeFieldName(Constants.VALUE);
       writeComplexCollection(type, property, null, json);
       json.writeEndObject();
@@ -656,8 +673,8 @@ public class ODataJsonSerializer implements ODataSerializer 
{
     
     try {
       final JsonGenerator json = new 
JsonFactory().createGenerator(buffer.getOutputStream());
-      writeReference(edmEntitySet, entity, contextURL, uriHelper, json);
-      
+      writeReference(metadata, edmEntitySet, entity, contextURL, uriHelper, 
json);
+
       json.close();
     } catch (IOException e) {
       throw new SerializerException("An I/O exception occurred.", e,  
SerializerException.MessageKeys.IO_EXCEPTION);
@@ -682,8 +699,9 @@ public class ODataJsonSerializer implements ODataSerializer 
{
       }
       
       json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
-      writeReferenceCollection(metadata, edmEntitySet, entityCollection, 
uriHelper,json);
-      
+      writeMetadataETag(metadata, json);
+      writeReferenceCollection(edmEntitySet, entityCollection, uriHelper,json);
+
       if(entityCollection.getNext() != null) {
         writeNextLink(entityCollection, json);
       }
@@ -697,27 +715,26 @@ public class ODataJsonSerializer implements 
ODataSerializer {
     return 
SerializerResultImpl.with().content(buffer.getInputStream()).build();
   }
 
-  protected void writeReferenceCollection(final ServiceMetadata metadata, 
final EdmEntitySet edmEntitySet, 
-      final EntityCollection entityCollection, final UriHelper uriHelper, 
final JsonGenerator json) 
-          throws IOException, SerializerException {
-  
+  protected void writeReferenceCollection(final EdmEntitySet edmEntitySet, 
final EntityCollection entityCollection,
+      final UriHelper uriHelper, final JsonGenerator json) throws IOException, 
SerializerException {
     json.writeArrayFieldStart(Constants.VALUE);
 
     for(final Entity entity : entityCollection.getEntities()) {
-      writeReference(edmEntitySet, entity, null, uriHelper, json);
+      writeReference(null, edmEntitySet, entity, null, uriHelper, json);
     }
 
     json.writeEndArray();
   }
   
-  protected void writeReference(final EdmEntitySet edmEntitySet, final Entity 
entity, final ContextURL contextURL, 
-      final UriHelper uriHelper, final JsonGenerator json) throws IOException, 
SerializerException {
-    
+  protected void writeReference(final ServiceMetadata metadata, final 
EdmEntitySet edmEntitySet, final Entity entity,
+      final ContextURL contextURL, final UriHelper uriHelper, final 
JsonGenerator json)
+      throws IOException, SerializerException {
     json.writeStartObject();
-    if(contextURL != null) {
+    if (contextURL != null) {
       json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString());
+      writeMetadataETag(metadata, json);
     }
-    
+
     json.writeStringField(Constants.JSON_ID, 
uriHelper.buildCanonicalURL(edmEntitySet, entity));
     json.writeEndObject();
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ServiceDocumentJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ServiceDocumentJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ServiceDocumentJsonSerializer.java
index e2f7261..65b4673 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ServiceDocumentJsonSerializer.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ServiceDocumentJsonSerializer.java
@@ -26,6 +26,8 @@ import org.apache.olingo.commons.api.edm.EdmEntityContainer;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmFunctionImport;
 import org.apache.olingo.commons.api.edm.EdmSingleton;
+import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.server.api.ServiceMetadata;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 
@@ -36,32 +38,37 @@ public class ServiceDocumentJsonSerializer {
   public static final String SINGLETON = "Singleton";
   public static final String SERVICE_DOCUMENT = "ServiceDocument";
 
-  private final Edm edm;
+  private final ServiceMetadata metadata;
   private final String serviceRoot;
+  private final ODataFormat format;
 
-  public ServiceDocumentJsonSerializer(final Edm edm, final String 
serviceRoot) {
-    this.edm = edm;
+  public ServiceDocumentJsonSerializer(final ServiceMetadata metadata, final 
String serviceRoot,
+      final ODataFormat format) {
+    this.metadata = metadata;
     this.serviceRoot = serviceRoot;
+    this.format = format;
   }
 
   public void writeServiceDocument(final JsonGenerator gen) throws IOException 
{
     gen.writeStartObject();
 
-    Object metadataUri;
+    final String metadataUri =
+        (serviceRoot == null ? "" :
+            serviceRoot.endsWith("/") ? serviceRoot : (serviceRoot + "/"))
+        + Constants.METADATA;
+    gen.writeObjectField(Constants.JSON_CONTEXT, metadataUri);
 
-    if (serviceRoot == null) {
-      metadataUri = Constants.METADATA;
-    } else {
-      if (serviceRoot.endsWith("/")) {
-        metadataUri = serviceRoot + Constants.METADATA;
-      } else {
-        metadataUri = serviceRoot + "/" + Constants.METADATA;
-      }
+    if (format != ODataFormat.JSON_NO_METADATA
+        && metadata != null
+        && metadata.getServiceMetadataETagSupport() != null
+        && metadata.getServiceMetadataETagSupport().getMetadataETag() != null) 
{
+      gen.writeStringField(Constants.JSON_METADATA_ETAG,
+          metadata.getServiceMetadataETagSupport().getMetadataETag());
     }
 
-    gen.writeObjectField(Constants.JSON_CONTEXT, metadataUri);
     gen.writeArrayFieldStart(Constants.VALUE);
 
+    final Edm edm = metadata.getEdm();
     writeEntitySets(gen, edm);
     writeFunctionImports(gen, edm);
     writeSingletons(gen, edm);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
index 795a330..e6b04a6 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
@@ -26,7 +26,6 @@ import org.apache.olingo.commons.api.data.ContextURL;
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntityCollection;
 import org.apache.olingo.commons.api.data.Property;
-import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmComplexType;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmEntityType;
@@ -53,7 +52,8 @@ public class ODataXmlSerializerImpl implements 
ODataSerializer {
   private static final Logger log = 
LoggerFactory.getLogger(ODataXmlSerializerImpl.class);
 
   @Override
-  public SerializerResultImpl serviceDocument(final Edm edm, final String 
serviceRoot) throws SerializerException {
+  public SerializerResultImpl serviceDocument(final ServiceMetadata metadata, 
final String serviceRoot)
+      throws SerializerException {
     throw new SerializerException("Service Document not implemented for XML 
format",
         SerializerException.MessageKeys.NOT_IMPLEMENTED);
   }
@@ -110,8 +110,8 @@ public class ODataXmlSerializerImpl implements 
ODataSerializer {
   }
 
   @Override
-  public SerializerResult primitive(final EdmPrimitiveType type, final 
Property property,
-      final PrimitiveSerializerOptions options) throws SerializerException {
+  public SerializerResult primitive(final ServiceMetadata metadata, final 
EdmPrimitiveType type,
+      final Property property, final PrimitiveSerializerOptions options) 
throws SerializerException {
     throw new SerializerException("Serialization not implemented for XML 
format.",
         SerializerException.MessageKeys.NOT_IMPLEMENTED);
   }
@@ -124,8 +124,8 @@ public class ODataXmlSerializerImpl implements 
ODataSerializer {
   }
 
   @Override
-  public SerializerResult primitiveCollection(final EdmPrimitiveType type, 
final Property property,
-      final PrimitiveSerializerOptions options) throws SerializerException {
+  public SerializerResult primitiveCollection(final ServiceMetadata metadata, 
final EdmPrimitiveType type,
+      final Property property, final PrimitiveSerializerOptions options) 
throws SerializerException {
     throw new SerializerException("Serialization not implemented for XML 
format.",
         SerializerException.MessageKeys.NOT_IMPLEMENTED);
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagHelperTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagHelperTest.java
 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagHelperTest.java
new file mode 100644
index 0000000..e992a5c
--- /dev/null
+++ 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagHelperTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core.etag;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.etag.ETagHelper;
+import org.apache.olingo.server.api.etag.PreconditionException;
+import org.junit.Test;
+
+public class ETagHelperTest {
+
+  private static final ETagHelper eTagHelper = 
OData.newInstance().createETagHelper();
+
+  @Test
+  public void readPrecondition() throws Exception {
+    assertFalse(eTagHelper.checkReadPreconditions(null, null, null));
+    assertFalse(eTagHelper.checkReadPreconditions("\"ETag\"", null, null));
+    assertFalse(eTagHelper.checkReadPreconditions(null, 
Collections.singleton("\"ETag\""), null));
+    assertFalse(eTagHelper.checkReadPreconditions(null, null, 
Collections.singleton("\"ETag\"")));
+    assertFalse(eTagHelper.checkReadPreconditions("\"ETag\"", 
Collections.singleton("\"ETag\""), null));
+    assertFalse(eTagHelper.checkReadPreconditions("\"ETag\"", 
Collections.singleton("*"), null));
+    assertTrue(eTagHelper.checkReadPreconditions("\"ETag\"", null, 
Collections.singleton("\"ETag\"")));
+    assertTrue(eTagHelper.checkReadPreconditions("\"ETag\"", null, 
Collections.singleton("*")));
+    assertFalse(eTagHelper.checkReadPreconditions("\"ETag\"", null, 
Collections.singleton("\"ETag2\"")));
+  }
+
+  @Test(expected = PreconditionException.class)
+  public void readPreconditionFail() throws Exception {
+    eTagHelper.checkReadPreconditions("\"ETag\"", 
Collections.singleton("\"ETag2\""), null);
+  }
+
+  @Test
+  public void changePrecondition() throws Exception {
+    eTagHelper.checkChangePreconditions(null, null, null);
+    eTagHelper.checkChangePreconditions("\"ETag\"", null, null);
+    eTagHelper.checkChangePreconditions(null, 
Collections.singleton("\"ETag\""), null);
+    eTagHelper.checkChangePreconditions(null, Collections.singleton("*"), 
null);
+    eTagHelper.checkChangePreconditions(null, null, 
Collections.singleton("*"));
+    eTagHelper.checkChangePreconditions("\"ETag\"", 
Collections.singleton("\"ETag\""), null);
+    eTagHelper.checkChangePreconditions("\"ETag\"", 
Collections.singleton("*"), null);
+    eTagHelper.checkChangePreconditions("\"ETag\"", null, 
Collections.singleton("\"ETag2\""));
+  }
+
+  @Test(expected = PreconditionException.class)
+  public void changePreconditionFailIfMatch() throws Exception {
+    eTagHelper.checkChangePreconditions("\"ETag\"", 
Collections.singleton("\"ETag2\""), null);
+  }
+
+  @Test(expected = PreconditionException.class)
+  public void changePreconditionFailIfNoneMatch() throws Exception {
+    eTagHelper.checkChangePreconditions("\"ETag\"", null, 
Collections.singleton("\"ETag\""));
+  }
+
+  @Test(expected = PreconditionException.class)
+  public void changePreconditionFailIfNoneMatchAll() throws Exception {
+    eTagHelper.checkChangePreconditions("\"ETag\"", null, 
Collections.singleton("*"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java
 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java
index 059d53d..3e68af6 100644
--- 
a/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java
+++ 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java
@@ -126,4 +126,4 @@ public class ETagParserTest {
     
assertFalse(eTagHelper.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"",
 "\"ETag3\",\"ETag4\""))
         .isMatchedBy("\"ETag5\""));
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/MetadataETagSupport.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/MetadataETagSupport.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/MetadataETagSupport.java
new file mode 100644
index 0000000..e235bcf
--- /dev/null
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/MetadataETagSupport.java
@@ -0,0 +1,40 @@
+/*
+ * 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.etag.ServiceMetadataETagSupport;
+
+public class MetadataETagSupport implements ServiceMetadataETagSupport {
+
+  private final String metadataETag;
+
+  public MetadataETagSupport(final String metadataETag) {
+    this.metadataETag = metadataETag;
+  }
+
+  @Override
+  public String getMetadataETag() {
+    return metadataETag;
+  }
+
+  @Override
+  public String getServiceDocumentETag() {
+    return metadataETag;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/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 a96f585..349af30 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
@@ -20,7 +20,7 @@ package org.apache.olingo.server.tecsvc;
 
 import java.io.IOException;
 import java.net.URI;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import javax.servlet.ServletException;
@@ -47,6 +47,16 @@ public class TechnicalServlet extends HttpServlet {
 
   private static final long serialVersionUID = 1L;
   private static final Logger LOG = 
LoggerFactory.getLogger(TechnicalServlet.class);
+  /**
+   * <p>ETag for the service document and the metadata document</p>
+   * <p>We use the same field for service-document and metadata-document ETags.
+   * It must change whenever the corresponding document changes.
+   * We don't know when someone changed the EDM in a way that changes one of 
these
+   * documents, but we do know that the EDM is defined completely in code and 
that
+   * therefore any change must be deployed, resulting in re-loading of this 
class,
+   * giving this field a new and hopefully unique value.</p>
+   */
+  private static final String metadataETag = "W/\"" + System.nanoTime() + "\"";
 
   @Override
   protected void service(final HttpServletRequest request, final 
HttpServletResponse response)
@@ -55,8 +65,9 @@ public class TechnicalServlet extends HttpServlet {
       OData odata = OData.newInstance();
       EdmxReference reference = new 
EdmxReference(URI.create("../v4.0/cs02/vocabularies/Org.OData.Core.V1.xml"));
       reference.addInclude(new EdmxReferenceInclude("Org.OData.Core.V1", 
"Core"));
-      final List<EdmxReference> references = Arrays.asList(reference);
-      final ServiceMetadata serviceMetadata = odata.createServiceMetadata(new 
EdmTechProvider(references), references);
+      final List<EdmxReference> references = 
Collections.singletonList(reference);
+      final ServiceMetadata serviceMetadata = odata.createServiceMetadata(
+          new EdmTechProvider(references), references, new 
MetadataETagSupport(metadataETag));
 
       HttpSession session = request.getSession(true);
       DataProvider dataProvider = (DataProvider) 
session.getAttribute(DataProvider.class.getName());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalActionProcessor.java
----------------------------------------------------------------------
diff --git 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalActionProcessor.java
 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalActionProcessor.java
index f3f85fd..da8c5cc 100644
--- 
a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalActionProcessor.java
+++ 
b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalActionProcessor.java
@@ -176,7 +176,7 @@ public class TechnicalActionProcessor extends 
TechnicalProcessor
 
     SerializerResult result =
         odata.createSerializer(ODataFormat.fromContentType(responseFormat))
-            .primitiveCollection(type, property, options);
+            .primitiveCollection(serviceMetadata, type, property, options);
 
     response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     response.setContent(result.getContent());
@@ -208,8 +208,8 @@ public class TechnicalActionProcessor extends 
TechnicalProcessor
       ContextURL contextURL = ContextURL.with().type(type).build();
       PrimitiveSerializerOptions options = 
PrimitiveSerializerOptions.with().contextURL(contextURL).build();
 
-      SerializerResult result = 
odata.createSerializer(ODataFormat.fromContentType(responseFormat)).primitive(type,
-          property, options);
+      SerializerResult result = 
odata.createSerializer(ODataFormat.fromContentType(responseFormat))
+          .primitive(serviceMetadata, type, property, options);
 
       response.setStatusCode(HttpStatusCode.OK.getStatusCode());
       response.setContent(result.getContent());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a604fa78/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 58b38c4..4ff7634 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
@@ -278,6 +278,9 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
       property.setValue(property.getValueType(), edmProperty.isCollection() ? 
Collections.emptyList() : null);
       dataProvider.updateETag(entity);
       response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
+      if (entity.getETag() != null) {
+        response.setHeader(HttpHeader.ETAG, entity.getETag());
+      }
     } else {
       throw new ODataApplicationException("Not nullable.", 
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
     }
@@ -333,7 +336,7 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
     SerializerResult result = null;
     switch (representationType) {
     case PRIMITIVE:
-      result = serializer.primitive((EdmPrimitiveType) type, property,
+      result = serializer.primitive(serviceMetadata, (EdmPrimitiveType) type, 
property,
           PrimitiveSerializerOptions.with().contextURL(contextURL)
               .nullable(edmProperty == null ? returnType.isNullable() : 
edmProperty.isNullable())
               .maxLength(edmProperty == null ? returnType.getMaxLength() : 
edmProperty.getMaxLength())
@@ -349,7 +352,7 @@ public class TechnicalPrimitiveComplexProcessor extends 
TechnicalProcessor
               .build());
       break;
     case COLLECTION_PRIMITIVE:
-      result = serializer.primitiveCollection((EdmPrimitiveType) type, 
property,
+      result = serializer.primitiveCollection(serviceMetadata, 
(EdmPrimitiveType) type, property,
           PrimitiveSerializerOptions.with().contextURL(contextURL)
               .nullable(edmProperty == null ? returnType.isNullable() : 
edmProperty.isNullable())
               .maxLength(edmProperty == null ? returnType.getMaxLength() : 
edmProperty.getMaxLength())

Reply via email to