[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())
