Repository: olingo-odata4 Updated Branches: refs/heads/master 0c511f80b -> 29b169def
[OLINGO-663] Add CustomETagSupport Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/29b169de Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/29b169de Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/29b169de Branch: refs/heads/master Commit: 29b169def41cfdf6c5b1c155ad2d50a6844073ec Parents: 0c511f8 Author: Christian Amend <[email protected]> Authored: Fri May 22 14:29:33 2015 +0200 Committer: Christian Amend <[email protected]> Committed: Thu May 28 10:39:47 2015 +0200 ---------------------------------------------------------------------- .../olingo/server/api/CustomETagSupport.java | 50 ++ .../olingo/server/api/ODataHttpHandler.java | 6 + .../olingo/server/core/ODataDispatcher.java | 560 +++++++++++++++++++ .../apache/olingo/server/core/ODataHandler.java | 522 +---------------- .../server/core/ODataHttpHandlerImpl.java | 6 + .../core/PreconditionRequiredException.java | 51 ++ .../server/core/PreconditionsValidator.java | 138 +++++ .../server/core/PreconditionsValidatorTest.java | 149 +++++ 8 files changed, 981 insertions(+), 501 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java new file mode 100644 index 0000000..1cca8ab --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java @@ -0,0 +1,50 @@ +/* + * 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.api; + +/** + * <p>Processors that would like to support concurrency control for certain entity sets can implement this + * interface.</p> + * <p>If implemented this interface can be registered at the ODataHttpHandler. This will result in change request to + * require an if-match/if-none-match or an if-modified-since/if-unmodified-since header. Otherwise the request will + * result in a "Precondition Required" response</p> + */ +public interface CustomETagSupport { + + /** + * This method will be called for update requests which target an entity or a property of an entity. + * If this method returns true and an header is not specified we will return a "Precondition Required" response. + * Validation has to be performed inside the processor methods after the dispatching. + * If this method returns false and an header is specified we will ignore the header. + * @param entitySetName + * @return true if the entity set specified needs an if-match/if-none-match header + */ + boolean hasETag(String entitySetName); + + /** + * This method will be called for update requests which target a media entity value. + * If this method returns true and an header is not specified we will return a "Precondition Required" response. + * Validation has to be performed inside the processor methods after the dispatching. + * If this method returns false and an header is specified we will ignore the header. + * @param entitySetName + * @return true if the entity set specified needs an if-match/if-none-match header + */ + boolean hasMediaETag(String entitySetName); + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java index 70ca15e..3700291 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java @@ -59,4 +59,10 @@ public interface ODataHttpHandler { */ void setSplit(int split); + /** + * Register this interface to support concurrency control for certain entity sets. + * @param customETagSupport + */ + void register(CustomETagSupport customConcurrencyControlSupport); + } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java new file mode 100644 index 0000000..f7be5bb --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java @@ -0,0 +1,560 @@ +/* + * 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; + +import org.apache.olingo.commons.api.edm.EdmAction; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.EdmFunction; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; +import org.apache.olingo.commons.api.edm.EdmReturnType; +import org.apache.olingo.commons.api.edm.EdmType; +import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.deserializer.DeserializerException; +import org.apache.olingo.server.api.processor.ActionComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionComplexProcessor; +import org.apache.olingo.server.api.processor.ActionEntityCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionEntityProcessor; +import org.apache.olingo.server.api.processor.ActionPrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionPrimitiveProcessor; +import org.apache.olingo.server.api.processor.ActionVoidProcessor; +import org.apache.olingo.server.api.processor.BatchProcessor; +import org.apache.olingo.server.api.processor.ComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.ComplexProcessor; +import org.apache.olingo.server.api.processor.CountComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor; +import org.apache.olingo.server.api.processor.CountPrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityProcessor; +import org.apache.olingo.server.api.processor.MediaEntityProcessor; +import org.apache.olingo.server.api.processor.MetadataProcessor; +import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.PrimitiveProcessor; +import org.apache.olingo.server.api.processor.PrimitiveValueProcessor; +import org.apache.olingo.server.api.processor.ReferenceCollectionProcessor; +import org.apache.olingo.server.api.processor.ReferenceProcessor; +import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; +import org.apache.olingo.server.api.serializer.RepresentationType; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceAction; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.UriResourceFunction; +import org.apache.olingo.server.api.uri.UriResourceNavigation; +import org.apache.olingo.server.api.uri.UriResourcePartTyped; +import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty; +import org.apache.olingo.server.api.uri.UriResourceProperty; +import org.apache.olingo.server.core.batchhandler.BatchHandler; + +public class ODataDispatcher { + + private final HttpMethod method; + private final UriInfo uriInfo; + private final ODataHandler handler; + + public ODataDispatcher(HttpMethod method, UriInfo uriInfo, ODataHandler handler) { + this.method = method; + this.uriInfo = uriInfo; + this.handler = handler; + } + + public void dispatch(ODataRequest request, ODataResponse response) throws ODataHandlerException, + ContentNegotiatorException, SerializerException, ODataApplicationException, DeserializerException, + PreconditionRequiredException { + switch (uriInfo.getKind()) { + case metadata: + checkMethod(method, HttpMethod.GET); + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.METADATA); + handler.selectProcessor(MetadataProcessor.class) + .readMetadata(request, response, uriInfo, requestedContentType); + break; + + case service: + checkMethod(method, HttpMethod.GET); + if ("".equals(request.getRawODataPath())) { + handler.selectProcessor(RedirectProcessor.class) + .redirect(request, response); + } else { + final ContentType serviceContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.SERVICE); + handler.selectProcessor(ServiceDocumentProcessor.class) + .readServiceDocument(request, response, uriInfo, serviceContentType); + } + break; + + case resource: + handleResourceDispatching(request, response); + break; + + case batch: + checkMethod(method, HttpMethod.POST); + new BatchHandler(handler, handler.selectProcessor(BatchProcessor.class)) + .process(request, response, true); + break; + + default: + throw new ODataHandlerException("not implemented", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } + } + + private void handleResourceDispatching(final ODataRequest request, final ODataResponse response) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException, PreconditionRequiredException { + + final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1; + final UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); + + switch (lastPathSegment.getKind()) { + case action: + checkMethod(request.getMethod(), HttpMethod.POST); + handleActionDispatching(request, response, (UriResourceAction) lastPathSegment); + break; + + case function: + checkMethod(request.getMethod(), HttpMethod.GET); + handleFunctionDispatching(request, response, (UriResourceFunction) lastPathSegment); + break; + + case entitySet: + case navigationProperty: + handleEntityDispatching(request, response, + ((UriResourcePartTyped) lastPathSegment).isCollection(), isMedia(lastPathSegment)); + break; + + case count: + checkMethod(request.getMethod(), HttpMethod.GET); + handleCountDispatching(request, response, lastPathSegmentIndex); + break; + + case primitiveProperty: + handlePrimitiveDispatching(request, response, + ((UriResourceProperty) lastPathSegment).isCollection()); + break; + + case complexProperty: + handleComplexDispatching(request, response, + ((UriResourceProperty) lastPathSegment).isCollection()); + break; + + case value: + handleValueDispatching(request, response, lastPathSegmentIndex); + break; + + case ref: + handleReferenceDispatching(request, response, lastPathSegmentIndex); + break; + + default: + throw new ODataHandlerException("not implemented", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } + } + + private void handleFunctionDispatching(final ODataRequest request, final ODataResponse response, + final UriResourceFunction uriResourceFunction) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException, PreconditionRequiredException { + EdmFunction function = uriResourceFunction.getFunction(); + if (function == null) { + function = uriResourceFunction.getFunctionImport().getUnboundFunctions().get(0); + } + final EdmReturnType returnType = function.getReturnType(); + switch (returnType.getType().getKind()) { + case ENTITY: + handleEntityDispatching(request, response, + returnType.isCollection() && uriResourceFunction.getKeyPredicates().isEmpty(), + false); + break; + case PRIMITIVE: + handlePrimitiveDispatching(request, response, returnType.isCollection()); + break; + case COMPLEX: + handleComplexDispatching(request, response, returnType.isCollection()); + break; + default: + throw new ODataHandlerException("not implemented", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } + } + + private void handleActionDispatching(final ODataRequest request, final ODataResponse response, + final UriResourceAction uriResourceAction) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException, PreconditionRequiredException { + final EdmAction action = uriResourceAction.getAction(); + if (action.isBound()) { + // Only bound actions can have etag control for the binding parameter + validatePreconditions(request, false); + } + final EdmReturnType returnType = action.getReturnType(); + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, RepresentationType.ACTION_PARAMETERS); + + if (returnType == null) { + handler.selectProcessor(ActionVoidProcessor.class) + .processActionVoid(request, response, uriInfo, requestFormat); + } else { + final boolean isCollection = returnType.isCollection(); + ContentType responseFormat = null; + switch (returnType.getType().getKind()) { + case ENTITY: + responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), + isCollection ? RepresentationType.COLLECTION_ENTITY : RepresentationType.ENTITY); + if (isCollection) { + handler.selectProcessor(ActionEntityCollectionProcessor.class) + .processActionEntityCollection(request, response, uriInfo, requestFormat, responseFormat); + } else { + handler.selectProcessor(ActionEntityProcessor.class) + .processActionEntity(request, response, uriInfo, requestFormat, responseFormat); + } + break; + + case PRIMITIVE: + responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), + isCollection ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE); + if (isCollection) { + handler.selectProcessor(ActionPrimitiveCollectionProcessor.class) + .processActionPrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat); + } else { + handler.selectProcessor(ActionPrimitiveProcessor.class) + .processActionPrimitive(request, response, uriInfo, requestFormat, responseFormat); + } + break; + + case COMPLEX: + responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), + isCollection ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX); + if (isCollection) { + handler.selectProcessor(ActionComplexCollectionProcessor.class) + .processActionComplexCollection(request, response, uriInfo, requestFormat, responseFormat); + } else { + handler.selectProcessor(ActionComplexProcessor.class) + .processActionComplex(request, response, uriInfo, requestFormat, responseFormat); + } + break; + + default: + throw new ODataHandlerException("not implemented", + ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); + } + } + } + + private void handleReferenceDispatching(final ODataRequest request, final ODataResponse response, + final int lastPathSegmentIndex) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException { + final HttpMethod method = request.getMethod(); + if (((UriResourcePartTyped) uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1)).isCollection()) { + if (method == HttpMethod.GET) { + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_REFERENCE); + handler.selectProcessor(ReferenceCollectionProcessor.class) + .readReferenceCollection(request, response, uriInfo, responseFormat); + } else if (method == HttpMethod.POST) { + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, RepresentationType.REFERENCE); + handler.selectProcessor(ReferenceProcessor.class) + .createReference(request, response, uriInfo, requestFormat); + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } else { + if (method == HttpMethod.GET) { + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.REFERENCE); + handler.selectProcessor(ReferenceProcessor.class).readReference(request, response, uriInfo, responseFormat); + } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, RepresentationType.REFERENCE); + handler.selectProcessor(ReferenceProcessor.class) + .updateReference(request, response, uriInfo, requestFormat); + } else if (method == HttpMethod.DELETE) { + handler.selectProcessor(ReferenceProcessor.class) + .deleteReference(request, response, uriInfo); + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } + } + + private void handleValueDispatching(final ODataRequest request, final ODataResponse response, + final int lastPathSegmentIndex) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException, PreconditionRequiredException { + final HttpMethod method = request.getMethod(); + final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); + if (resource instanceof UriResourceProperty + || resource instanceof UriResourceFunction + && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { + final EdmType type = resource instanceof UriResourceProperty ? + ((UriResourceProperty) resource).getType() : ((UriResourceFunction) resource).getType(); + final RepresentationType valueRepresentationType = + type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? + RepresentationType.BINARY : RepresentationType.VALUE; + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), valueRepresentationType); + + handler.selectProcessor(PrimitiveValueProcessor.class) + .readPrimitiveValue(request, response, uriInfo, requestedContentType); + } else if (method == HttpMethod.PUT && resource instanceof UriResourceProperty) { + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, valueRepresentationType); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), valueRepresentationType); + handler.selectProcessor(PrimitiveValueProcessor.class) + .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat); + } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { + handler.selectProcessor(PrimitiveValueProcessor.class).deletePrimitive(request, response, uriInfo); + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } else { + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.MEDIA); + handler.selectProcessor(MediaEntityProcessor.class) + .readMediaEntity(request, response, uriInfo, requestedContentType); + } else if (method == HttpMethod.PUT && resource instanceof UriResourceEntitySet) { + validatePreconditions(request, true); + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); + handler.selectProcessor(MediaEntityProcessor.class) + .updateMediaEntity(request, response, uriInfo, requestFormat, responseFormat); + } else if (method == HttpMethod.DELETE && resource instanceof UriResourceEntitySet) { + validatePreconditions(request, true); + handler.selectProcessor(MediaEntityProcessor.class).deleteEntity(request, response, uriInfo); + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } + } + + private void handleComplexDispatching(final ODataRequest request, final ODataResponse response, + final boolean isCollection) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException { + final HttpMethod method = request.getMethod(); + final RepresentationType complexRepresentationType = isCollection ? + RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX; + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), complexRepresentationType); + if (isCollection) { + handler.selectProcessor(ComplexCollectionProcessor.class) + .readComplexCollection(request, response, uriInfo, requestedContentType); + } else { + handler.selectProcessor(ComplexProcessor.class) + .readComplex(request, response, uriInfo, requestedContentType); + } + } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, complexRepresentationType); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), complexRepresentationType); + if (isCollection) { + handler.selectProcessor(ComplexCollectionProcessor.class) + .updateComplexCollection(request, response, uriInfo, requestFormat, responseFormat); + } else { + handler.selectProcessor(ComplexProcessor.class) + .updateComplex(request, response, uriInfo, requestFormat, responseFormat); + } + } else if (method == HttpMethod.DELETE) { + if (isCollection) { + handler.selectProcessor(ComplexCollectionProcessor.class) + .deleteComplexCollection(request, response, uriInfo); + } else { + handler.selectProcessor(ComplexProcessor.class) + .deleteComplex(request, response, uriInfo); + } + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } + + private void handlePrimitiveDispatching(final ODataRequest request, final ODataResponse response, + final boolean isCollection) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException { + final HttpMethod method = request.getMethod(); + final RepresentationType representationType = isCollection ? + RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE; + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), representationType); + if (isCollection) { + handler.selectProcessor(PrimitiveCollectionProcessor.class) + .readPrimitiveCollection(request, response, uriInfo, requestedContentType); + } else { + handler.selectProcessor(PrimitiveProcessor.class) + .readPrimitive(request, response, uriInfo, requestedContentType); + } + } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, representationType); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), representationType); + if (isCollection) { + handler.selectProcessor(PrimitiveCollectionProcessor.class) + .updatePrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat); + } else { + handler.selectProcessor(PrimitiveProcessor.class) + .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat); + } + } else if (method == HttpMethod.DELETE) { + if (isCollection) { + handler.selectProcessor(PrimitiveCollectionProcessor.class) + .deletePrimitiveCollection(request, response, uriInfo); + } else { + handler.selectProcessor(PrimitiveProcessor.class) + .deletePrimitive(request, response, uriInfo); + } + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } + + private void handleCountDispatching(final ODataRequest request, final ODataResponse response, + final int lastPathSegmentIndex) + throws ODataHandlerException, ODataApplicationException, SerializerException { + final HttpMethod method = request.getMethod(); + if (method == HttpMethod.GET) { + final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); + if (resource instanceof UriResourceEntitySet + || resource instanceof UriResourceNavigation + || resource instanceof UriResourceFunction + && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.ENTITY) { + handler.selectProcessor(CountEntityCollectionProcessor.class) + .countEntityCollection(request, response, uriInfo); + } else if (resource instanceof UriResourcePrimitiveProperty + || resource instanceof UriResourceFunction + && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { + handler.selectProcessor(CountPrimitiveCollectionProcessor.class) + .countPrimitiveCollection(request, response, uriInfo); + } else { + handler.selectProcessor(CountComplexCollectionProcessor.class) + .countComplexCollection(request, response, uriInfo); + } + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed for count.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } + + private void handleEntityDispatching(final ODataRequest request, final ODataResponse response, + final boolean isCollection, final boolean isMedia) + throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, + SerializerException, DeserializerException, PreconditionRequiredException { + final HttpMethod method = request.getMethod(); + if (isCollection) { + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_ENTITY); + handler.selectProcessor(EntityCollectionProcessor.class) + .readEntityCollection(request, response, uriInfo, requestedContentType); + } else if (method == HttpMethod.POST) { + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); + if (isMedia) { + handler.selectProcessor(MediaEntityProcessor.class) + .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat); + } else { + checkContentTypeSupport(requestFormat, RepresentationType.ENTITY); + handler.selectProcessor(EntityProcessor.class) + .createEntity(request, response, uriInfo, requestFormat, responseFormat); + } + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } else { + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); + handler.selectProcessor(EntityProcessor.class) + .readEntity(request, response, uriInfo, requestedContentType); + } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { + validatePreconditions(request, false); + final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); + checkContentTypeSupport(requestFormat, RepresentationType.ENTITY); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); + handler.selectProcessor(EntityProcessor.class) + .updateEntity(request, response, uriInfo, requestFormat, responseFormat); + } else if (method == HttpMethod.DELETE) { + validatePreconditions(request, false); + handler.selectProcessor(isMedia ? MediaEntityProcessor.class : EntityProcessor.class) + .deleteEntity(request, response, uriInfo); + } else { + throw new ODataHandlerException("HTTP method " + method + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); + } + } + } + + private void validatePreconditions(ODataRequest request, boolean isMediaValue) throws PreconditionRequiredException { + // If needed perform preconditions validation + if (handler.getCustomETagSupport() != null) { + new PreconditionsValidator(handler.getCustomETagSupport(), uriInfo, request.getHeader("if-match"), request + .getHeader("if-none-match")).validatePreconditions(isMediaValue); + } + } + + private void checkMethod(final HttpMethod requestMethod, final HttpMethod allowedMethod) + throws ODataHandlerException { + if (requestMethod != allowedMethod) { + throw new ODataHandlerException("HTTP method " + requestMethod + " is not allowed.", + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, requestMethod.toString()); + } + } + + private void checkContentTypeSupport(final ContentType requestFormat, final RepresentationType representationType) + throws ODataHandlerException, ContentNegotiatorException { + ContentNegotiator.checkSupport(requestFormat, handler.getCustomContentTypeSupport(), representationType); + } + + private boolean isMedia(final UriResource pathSegment) { + return pathSegment instanceof UriResourceEntitySet + && ((UriResourceEntitySet) pathSegment).getEntityType().hasStream() + || pathSegment instanceof UriResourceNavigation + && ((EdmEntityType) ((UriResourceNavigation) pathSegment).getType()).hasStream(); + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java index 755bc92..b1beaea 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java @@ -21,65 +21,26 @@ package org.apache.olingo.server.core; import java.util.LinkedList; import java.util.List; -import org.apache.olingo.commons.api.edm.EdmAction; -import org.apache.olingo.commons.api.edm.EdmEntityType; -import org.apache.olingo.commons.api.edm.EdmFunction; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; -import org.apache.olingo.commons.api.edm.EdmReturnType; -import org.apache.olingo.commons.api.edm.EdmType; -import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; -import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; -import org.apache.olingo.server.api.ODataServerError; +import org.apache.olingo.server.api.CustomETagSupport; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.deserializer.DeserializerException; -import org.apache.olingo.server.api.processor.ActionComplexCollectionProcessor; -import org.apache.olingo.server.api.processor.ActionComplexProcessor; -import org.apache.olingo.server.api.processor.ActionEntityCollectionProcessor; -import org.apache.olingo.server.api.processor.ActionEntityProcessor; -import org.apache.olingo.server.api.processor.ActionPrimitiveCollectionProcessor; -import org.apache.olingo.server.api.processor.ActionPrimitiveProcessor; -import org.apache.olingo.server.api.processor.ActionVoidProcessor; -import org.apache.olingo.server.api.processor.BatchProcessor; -import org.apache.olingo.server.api.processor.ComplexCollectionProcessor; -import org.apache.olingo.server.api.processor.ComplexProcessor; -import org.apache.olingo.server.api.processor.CountComplexCollectionProcessor; -import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor; -import org.apache.olingo.server.api.processor.CountPrimitiveCollectionProcessor; import org.apache.olingo.server.api.processor.DefaultProcessor; -import org.apache.olingo.server.api.processor.EntityCollectionProcessor; -import org.apache.olingo.server.api.processor.EntityProcessor; import org.apache.olingo.server.api.processor.ErrorProcessor; -import org.apache.olingo.server.api.processor.MediaEntityProcessor; -import org.apache.olingo.server.api.processor.MetadataProcessor; -import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor; -import org.apache.olingo.server.api.processor.PrimitiveProcessor; -import org.apache.olingo.server.api.processor.PrimitiveValueProcessor; import org.apache.olingo.server.api.processor.Processor; -import org.apache.olingo.server.api.processor.ReferenceCollectionProcessor; -import org.apache.olingo.server.api.processor.ReferenceProcessor; -import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; import org.apache.olingo.server.api.serializer.RepresentationType; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriInfo; -import org.apache.olingo.server.api.uri.UriResource; -import org.apache.olingo.server.api.uri.UriResourceAction; -import org.apache.olingo.server.api.uri.UriResourceEntitySet; -import org.apache.olingo.server.api.uri.UriResourceFunction; -import org.apache.olingo.server.api.uri.UriResourceNavigation; -import org.apache.olingo.server.api.uri.UriResourcePartTyped; -import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty; -import org.apache.olingo.server.api.uri.UriResourceProperty; -import org.apache.olingo.server.core.batchhandler.BatchHandler; import org.apache.olingo.server.core.uri.parser.Parser; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; @@ -91,8 +52,9 @@ public class ODataHandler { private final OData odata; private final ServiceMetadata serviceMetadata; - private List<Processor> processors = new LinkedList<Processor>(); - private CustomContentTypeSupport customContentTypeSupport = null; + private final List<Processor> processors = new LinkedList<Processor>(); + private CustomContentTypeSupport customContentTypeSupport; + private CustomETagSupport customETagSupport; private UriInfo uriInfo; @@ -146,7 +108,7 @@ public class ODataHandler { private void processInternal(final ODataRequest request, final ODataResponse response) throws ODataHandlerException, UriParserException, UriValidationException, ContentNegotiatorException, - ODataApplicationException, SerializerException, DeserializerException { + ODataApplicationException, SerializerException, DeserializerException, PreconditionRequiredException { validateODataVersion(request, response); uriInfo = new Parser().parseUri(request.getRawODataPath(), request.getRawQueryPath(), null, @@ -155,42 +117,7 @@ public class ODataHandler { final HttpMethod method = request.getMethod(); new UriValidator().validate(uriInfo, method); - switch (uriInfo.getKind()) { - case metadata: - checkMethod(method, HttpMethod.GET); - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.METADATA); - selectProcessor(MetadataProcessor.class) - .readMetadata(request, response, uriInfo, requestedContentType); - break; - - case service: - checkMethod(method, HttpMethod.GET); - if ("".equals(request.getRawODataPath())) { - selectProcessor(RedirectProcessor.class) - .redirect(request, response); - } else { - final ContentType serviceContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.SERVICE); - selectProcessor(ServiceDocumentProcessor.class) - .readServiceDocument(request, response, uriInfo, serviceContentType); - } - break; - - case resource: - handleResourceDispatching(request, response); - break; - - case batch: - checkMethod(method, HttpMethod.POST); - new BatchHandler(this, selectProcessor(BatchProcessor.class)) - .process(request, response, true); - break; - - default: - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); - } + new ODataDispatcher(method, uriInfo, this).dispatch(request, response); } public void handleException(final ODataRequest request, final ODataResponse response, @@ -206,7 +133,7 @@ public class ODataHandler { ContentType requestedContentType; try { requestedContentType = ContentNegotiator.doContentNegotiation( - uriInfo == null ? null : uriInfo.getFormatOption(), request, customContentTypeSupport, + uriInfo == null ? null : uriInfo.getFormatOption(), request, getCustomContentTypeSupport(), RepresentationType.ERROR); } catch (final ContentNegotiatorException e) { requestedContentType = ODataFormat.JSON.getContentType(); @@ -214,418 +141,6 @@ public class ODataHandler { exceptionProcessor.processError(request, response, serverError, requestedContentType); } - private void handleResourceDispatching(final ODataRequest request, final ODataResponse response) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - - final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1; - final UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); - - switch (lastPathSegment.getKind()) { - case action: - checkMethod(request.getMethod(), HttpMethod.POST); - handleActionDispatching(request, response, (UriResourceAction) lastPathSegment); - break; - - case function: - checkMethod(request.getMethod(), HttpMethod.GET); - handleFunctionDispatching(request, response, (UriResourceFunction) lastPathSegment); - break; - - case entitySet: - case navigationProperty: - handleEntityDispatching(request, response, - ((UriResourcePartTyped) lastPathSegment).isCollection(), isMedia(lastPathSegment)); - break; - - case count: - checkMethod(request.getMethod(), HttpMethod.GET); - handleCountDispatching(request, response, lastPathSegmentIndex); - break; - - case primitiveProperty: - handlePrimitiveDispatching(request, response, - ((UriResourceProperty) lastPathSegment).isCollection()); - break; - - case complexProperty: - handleComplexDispatching(request, response, - ((UriResourceProperty) lastPathSegment).isCollection()); - break; - - case value: - handleValueDispatching(request, response, lastPathSegmentIndex); - break; - - case ref: - handleReferenceDispatching(request, response, lastPathSegmentIndex); - break; - - default: - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); - } - } - - private void handleFunctionDispatching(final ODataRequest request, final ODataResponse response, - final UriResourceFunction uriResourceFunction) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - EdmFunction function = uriResourceFunction.getFunction(); - if (function == null) { - function = uriResourceFunction.getFunctionImport().getUnboundFunctions().get(0); - } - final EdmReturnType returnType = function.getReturnType(); - switch (returnType.getType().getKind()) { - case ENTITY: - handleEntityDispatching(request, response, - returnType.isCollection() && uriResourceFunction.getKeyPredicates().isEmpty(), - false); - break; - case PRIMITIVE: - handlePrimitiveDispatching(request, response, returnType.isCollection()); - break; - case COMPLEX: - handleComplexDispatching(request, response, returnType.isCollection()); - break; - default: - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); - } - } - - private void handleActionDispatching(final ODataRequest request, final ODataResponse response, - final UriResourceAction uriResourceAction) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - final EdmAction action = uriResourceAction.getAction(); - final EdmReturnType returnType = action.getReturnType(); - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, RepresentationType.ACTION_PARAMETERS); - - if (returnType == null) { - selectProcessor(ActionVoidProcessor.class) - .processActionVoid(request, response, uriInfo, requestFormat); - } else { - final boolean isCollection = returnType.isCollection(); - ContentType responseFormat = null; - switch (returnType.getType().getKind()) { - case ENTITY: - responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, - isCollection ? RepresentationType.COLLECTION_ENTITY : RepresentationType.ENTITY); - if (isCollection) { - selectProcessor(ActionEntityCollectionProcessor.class) - .processActionEntityCollection(request, response, uriInfo, requestFormat, responseFormat); - } else { - selectProcessor(ActionEntityProcessor.class) - .processActionEntity(request, response, uriInfo, requestFormat, responseFormat); - } - break; - - case PRIMITIVE: - responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, - isCollection ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE); - if (isCollection) { - selectProcessor(ActionPrimitiveCollectionProcessor.class) - .processActionPrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat); - } else { - selectProcessor(ActionPrimitiveProcessor.class) - .processActionPrimitive(request, response, uriInfo, requestFormat, responseFormat); - } - break; - - case COMPLEX: - responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, - isCollection ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX); - if (isCollection) { - selectProcessor(ActionComplexCollectionProcessor.class) - .processActionComplexCollection(request, response, uriInfo, requestFormat, responseFormat); - } else { - selectProcessor(ActionComplexProcessor.class) - .processActionComplex(request, response, uriInfo, requestFormat, responseFormat); - } - break; - - default: - throw new ODataHandlerException("not implemented", - ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); - } - } - } - - private void handleReferenceDispatching(final ODataRequest request, final ODataResponse response, - final int lastPathSegmentIndex) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - final HttpMethod method = request.getMethod(); - if (((UriResourcePartTyped) uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1)).isCollection()) { - if (method == HttpMethod.GET) { - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.COLLECTION_REFERENCE); - selectProcessor(ReferenceCollectionProcessor.class) - .readReferenceCollection(request, response, uriInfo, responseFormat); - } else if (method == HttpMethod.POST) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, RepresentationType.REFERENCE); - selectProcessor(ReferenceProcessor.class) - .createReference(request, response, uriInfo, requestFormat); - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } else { - if (method == HttpMethod.GET) { - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.REFERENCE); - selectProcessor(ReferenceProcessor.class).readReference(request, response, uriInfo, responseFormat); - } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, RepresentationType.REFERENCE); - selectProcessor(ReferenceProcessor.class) - .updateReference(request, response, uriInfo, requestFormat); - } else if (method == HttpMethod.DELETE) { - selectProcessor(ReferenceProcessor.class) - .deleteReference(request, response, uriInfo); - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } - } - - private void handleValueDispatching(final ODataRequest request, final ODataResponse response, - final int lastPathSegmentIndex) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - final HttpMethod method = request.getMethod(); - final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); - if (resource instanceof UriResourceProperty - || resource instanceof UriResourceFunction - && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { - final EdmType type = resource instanceof UriResourceProperty ? - ((UriResourceProperty) resource).getType() : ((UriResourceFunction) resource).getType(); - final RepresentationType valueRepresentationType = - type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? - RepresentationType.BINARY : RepresentationType.VALUE; - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, valueRepresentationType); - - selectProcessor(PrimitiveValueProcessor.class) - .readPrimitiveValue(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.PUT && resource instanceof UriResourceProperty) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, valueRepresentationType); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, valueRepresentationType); - selectProcessor(PrimitiveValueProcessor.class) - .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { - selectProcessor(PrimitiveValueProcessor.class).deletePrimitive(request, response, uriInfo); - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } else { - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.MEDIA); - selectProcessor(MediaEntityProcessor.class) - .readMediaEntity(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.PUT && resource instanceof UriResourceEntitySet) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.ENTITY); - selectProcessor(MediaEntityProcessor.class) - .updateMediaEntity(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE && resource instanceof UriResourceEntitySet) { - selectProcessor(MediaEntityProcessor.class).deleteEntity(request, response, uriInfo); - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } - } - - private void handleComplexDispatching(final ODataRequest request, final ODataResponse response, - final boolean isCollection) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - final HttpMethod method = request.getMethod(); - final RepresentationType complexRepresentationType = isCollection ? - RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX; - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, complexRepresentationType); - if (isCollection) { - selectProcessor(ComplexCollectionProcessor.class) - .readComplexCollection(request, response, uriInfo, requestedContentType); - } else { - selectProcessor(ComplexProcessor.class) - .readComplex(request, response, uriInfo, requestedContentType); - } - } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, complexRepresentationType); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, complexRepresentationType); - if (isCollection) { - selectProcessor(ComplexCollectionProcessor.class) - .updateComplexCollection(request, response, uriInfo, requestFormat, responseFormat); - } else { - selectProcessor(ComplexProcessor.class) - .updateComplex(request, response, uriInfo, requestFormat, responseFormat); - } - } else if (method == HttpMethod.DELETE) { - if (isCollection) { - selectProcessor(ComplexCollectionProcessor.class) - .deleteComplexCollection(request, response, uriInfo); - } else { - selectProcessor(ComplexProcessor.class) - .deleteComplex(request, response, uriInfo); - } - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } - - private void handlePrimitiveDispatching(final ODataRequest request, final ODataResponse response, - final boolean isCollection) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - final HttpMethod method = request.getMethod(); - final RepresentationType representationType = isCollection ? - RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE; - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, representationType); - if (isCollection) { - selectProcessor(PrimitiveCollectionProcessor.class) - .readPrimitiveCollection(request, response, uriInfo, requestedContentType); - } else { - selectProcessor(PrimitiveProcessor.class) - .readPrimitive(request, response, uriInfo, requestedContentType); - } - } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, representationType); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, representationType); - if (isCollection) { - selectProcessor(PrimitiveCollectionProcessor.class) - .updatePrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat); - } else { - selectProcessor(PrimitiveProcessor.class) - .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat); - } - } else if (method == HttpMethod.DELETE) { - if (isCollection) { - selectProcessor(PrimitiveCollectionProcessor.class) - .deletePrimitiveCollection(request, response, uriInfo); - } else { - selectProcessor(PrimitiveProcessor.class) - .deletePrimitive(request, response, uriInfo); - } - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } - - private void handleCountDispatching(final ODataRequest request, final ODataResponse response, - final int lastPathSegmentIndex) - throws ODataHandlerException, ODataApplicationException, SerializerException { - final HttpMethod method = request.getMethod(); - if (method == HttpMethod.GET) { - final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); - if (resource instanceof UriResourceEntitySet - || resource instanceof UriResourceNavigation - || resource instanceof UriResourceFunction - && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.ENTITY) { - selectProcessor(CountEntityCollectionProcessor.class) - .countEntityCollection(request, response, uriInfo); - } else if (resource instanceof UriResourcePrimitiveProperty - || resource instanceof UriResourceFunction - && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { - selectProcessor(CountPrimitiveCollectionProcessor.class) - .countPrimitiveCollection(request, response, uriInfo); - } else { - selectProcessor(CountComplexCollectionProcessor.class) - .countComplexCollection(request, response, uriInfo); - } - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed for count.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } - - private void handleEntityDispatching(final ODataRequest request, final ODataResponse response, - final boolean isCollection, final boolean isMedia) - throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, - SerializerException, DeserializerException { - final HttpMethod method = request.getMethod(); - if (isCollection) { - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.COLLECTION_ENTITY); - selectProcessor(EntityCollectionProcessor.class) - .readEntityCollection(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.POST) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.ENTITY); - if (isMedia) { - selectProcessor(MediaEntityProcessor.class) - .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat); - } else { - checkContentTypeSupport(requestFormat, RepresentationType.ENTITY); - selectProcessor(EntityProcessor.class) - .createEntity(request, response, uriInfo, requestFormat, responseFormat); - } - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } else { - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.ENTITY); - selectProcessor(EntityProcessor.class) - .readEntity(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) { - final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); - checkContentTypeSupport(requestFormat, RepresentationType.ENTITY); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, customContentTypeSupport, RepresentationType.ENTITY); - selectProcessor(EntityProcessor.class) - .updateEntity(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE) { - selectProcessor(isMedia ? MediaEntityProcessor.class : EntityProcessor.class) - .deleteEntity(request, response, uriInfo); - } else { - throw new ODataHandlerException("HTTP method " + method + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString()); - } - } - } - - private void checkMethod(final HttpMethod requestMethod, final HttpMethod allowedMethod) - throws ODataHandlerException { - if (requestMethod != allowedMethod) { - throw new ODataHandlerException("HTTP method " + requestMethod + " is not allowed.", - ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, requestMethod.toString()); - } - } - - private void checkContentTypeSupport(final ContentType requestFormat, final RepresentationType representationType) - throws ODataHandlerException, ContentNegotiatorException { - ContentNegotiator.checkSupport(requestFormat, customContentTypeSupport, representationType); - } - private void validateODataVersion(final ODataRequest request, final ODataResponse response) throws ODataHandlerException { final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION); @@ -639,14 +154,7 @@ public class ODataHandler { } } - private boolean isMedia(final UriResource pathSegment) { - return pathSegment instanceof UriResourceEntitySet - && ((UriResourceEntitySet) pathSegment).getEntityType().hasStream() - || pathSegment instanceof UriResourceNavigation - && ((EdmEntityType) ((UriResourceNavigation) pathSegment).getType()).hasStream(); - } - - private <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException { + <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException { for (final Processor processor : processors) { if (cls.isAssignableFrom(processor.getClass())) { processor.init(odata, serviceMetadata); @@ -664,4 +172,16 @@ public class ODataHandler { public void register(final CustomContentTypeSupport customContentTypeSupport) { this.customContentTypeSupport = customContentTypeSupport; } + + public CustomContentTypeSupport getCustomContentTypeSupport() { + return customContentTypeSupport; + } + + public void register(CustomETagSupport customETagSupport) { + this.customETagSupport = customETagSupport; + } + + public CustomETagSupport getCustomETagSupport() { + return customETagSupport; + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java index 2e6a645..191b522 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.server.api.CustomETagSupport; import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataHttpHandler; @@ -242,4 +243,9 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { public void register(final CustomContentTypeSupport customContentTypeSupport) { handler.register(customContentTypeSupport); } + + @Override + public void register(final CustomETagSupport customConcurrencyControlSupport) { + handler.register(customConcurrencyControlSupport); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java new file mode 100644 index 0000000..a9b6754 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java @@ -0,0 +1,51 @@ +/* + * 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; + +import org.apache.olingo.server.api.ODataTranslatedException; + +public class PreconditionRequiredException extends ODataTranslatedException { + private static final long serialVersionUID = -8112658467394158700L; + + public static enum MessageKeys implements MessageKey { + /** parameter: required header */ + PRECONDITION_REQUIRED; + + @Override + public String getKey() { + return name(); + } + } + + public PreconditionRequiredException(final String developmentMessage, final MessageKey messageKey, + final String... parameters) { + super(developmentMessage, messageKey, parameters); + } + + public PreconditionRequiredException(final String developmentMessage, final Throwable cause, + final MessageKey messageKey, + final String... parameters) { + super(developmentMessage, cause, messageKey, parameters); + } + + @Override + protected String getBundleName() { + return DEFAULT_SERVER_BUNDLE_NAME; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java new file mode 100644 index 0000000..fe1875e --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java @@ -0,0 +1,138 @@ +/* + * 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; + +import java.util.List; + +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmFunctionImport; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.server.api.CustomETagSupport; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.UriResourceFunction; +import org.apache.olingo.server.api.uri.UriResourceKind; +import org.apache.olingo.server.api.uri.UriResourceNavigation; + +public class PreconditionsValidator { + + ; + private final CustomETagSupport customETagSupport; + private final UriInfo uriInfo; + private final String ifMatch; + private final String ifNoneMatch; + + public PreconditionsValidator(CustomETagSupport customETagSupport, UriInfo uriInfo, String ifMatch, + String ifNoneMatch) { + this.customETagSupport = customETagSupport; + this.uriInfo = uriInfo; + this.ifMatch = ifMatch; + this.ifNoneMatch = ifNoneMatch; + } + + public void validatePreconditions(boolean isMediaValue) throws PreconditionRequiredException { + EdmEntitySet affectedEntitySet = extractInformation(); + if (affectedEntitySet != null) { + if ((isMediaValue && customETagSupport.hasMediaETag(affectedEntitySet.getName())) || + (!isMediaValue && customETagSupport.hasETag(affectedEntitySet.getName()))) { + checkETagHeaderPresent(); + } + } + } + + private void checkETagHeaderPresent() throws PreconditionRequiredException { + if (ifMatch == null && ifNoneMatch == null) { + throw new PreconditionRequiredException("Expected an if-match or if-none-match header", + PreconditionRequiredException.MessageKeys.PRECONDITION_REQUIRED); + } + } + + private EdmEntitySet extractInformation() { + EdmEntitySet affectedEntitySet = null; + List<UriResource> uriResourceParts = uriInfo.getUriResourceParts(); + if (uriResourceParts.size() > 0) { + UriResource uriResourcePart = uriResourceParts.get(uriResourceParts.size() - 1); + switch (uriResourcePart.getKind()) { + case entitySet: + affectedEntitySet = ((UriResourceEntitySet) uriResourcePart).getEntitySet(); + break; + case navigationProperty: + affectedEntitySet = getEntitySetFromBeginning(); + break; + case value: + affectedEntitySet = getEntitySetOrNavigationEntitySet(uriResourceParts); + break; + case action: + affectedEntitySet = getEntitySetOrNavigationEntitySet(uriResourceParts); + break; + default: + // TODO: Cannot happen but should we throw an exception? + break; + } + } else { + // TODO: Cannot happen but should we throw an exception? + } + return affectedEntitySet; + } + + private EdmEntitySet getEntitySetOrNavigationEntitySet(List<UriResource> uriResourceParts) { + EdmEntitySet affectedEntitySet = null; + UriResource previousResourcePart = uriResourceParts.get(uriResourceParts.size() - 2); + if (previousResourcePart.getKind() == UriResourceKind.entitySet) { + affectedEntitySet = ((UriResourceEntitySet) previousResourcePart).getEntitySet(); + } else if (previousResourcePart.getKind() == UriResourceKind.navigationProperty) { + affectedEntitySet = getEntitySetFromBeginning(); + } + return affectedEntitySet; + } + + private EdmEntitySet getEntitySetFromBeginning() { + EdmEntitySet lastFoundES = null; + for (UriResource uriResourcePart : uriInfo.getUriResourceParts()) { + if (UriResourceKind.function == uriResourcePart.getKind()) { + EdmFunctionImport functionImport = ((UriResourceFunction) uriResourcePart).getFunctionImport(); + if (functionImport != null && functionImport.getReturnedEntitySet() != null) { + lastFoundES = functionImport.getReturnedEntitySet(); + } else { + lastFoundES = null; + break; + } + } else if (UriResourceKind.entitySet == uriResourcePart.getKind()) { + lastFoundES = ((UriResourceEntitySet) uriResourcePart).getEntitySet(); + } else if (UriResourceKind.navigationProperty == uriResourcePart.getKind()) { + EdmNavigationProperty navProp = ((UriResourceNavigation) uriResourcePart).getProperty(); + if (lastFoundES != null) { + lastFoundES = (EdmEntitySet) lastFoundES.getRelatedBindingTarget(navProp.getName()); + if (lastFoundES == null) { + break; + } + } + } else if (UriResourceKind.value == uriResourcePart.getKind() + || UriResourceKind.action == uriResourcePart.getKind()) { + // TODO: Should we validate that we are at the end of the resource path + break; + } else { + lastFoundES = null; + break; + } + } + return lastFoundES; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/29b169de/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java new file mode 100644 index 0000000..3a1c131 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java @@ -0,0 +1,149 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.core.edm.EdmProviderImpl; +import org.apache.olingo.server.api.CustomETagSupport; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.core.uri.parser.Parser; +import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; +import org.junit.Test; + +public class PreconditionsValidatorTest { + + @Test + public void simpleEntity() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESAllPrim(1)", null, null, getEdm()); + new PreconditionsValidator(new ETagSupport(), uriInfo, "*", "*").validatePreconditions(false); + } + + @Test + public void boundActionOnEsKeyNav() throws Exception { + UriInfo uriInfo = + new Parser().parseUri("ESKeyNav(1)/Namespace1_Alias.BAETTwoKeyNavRTETTwoKeyNav", null, null, getEdm()); + new PreconditionsValidator(new ETagSupport(), uriInfo, "*", "*").validatePreconditions(false); + } + + @Test + public void simpleEntityValue() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESMedia(1)/$value", null, null, getEdm()); + new PreconditionsValidator(new ETagSupport(), uriInfo, "*", "*").validatePreconditions(true); + } + + @Test + public void simpleEntityValueValidationNotActiveForMedia() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESMedia(1)/$value", null, null, getEdm()); + new PreconditionsValidator(new ETagSupport(true, false), uriInfo, null, null).validatePreconditions(true); + } + + @Test + public void EntityAndToOneNavigation() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESAllPrim(1)/NavPropertyETTwoPrimOne", null, null, getEdm()); + new PreconditionsValidator(new ETagSupport("ESTwoPrim"), uriInfo, "*", "*").validatePreconditions(false); + } + + @Test + public void simpleEntityPreconditionsReqException() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESAllPrim(1)", null, null, getEdm()); + try { + new PreconditionsValidator(new ETagSupport(), uriInfo, null, null).validatePreconditions(false); + fail("Expected a PreconditionRequiredException but was not thrown"); + } catch (PreconditionRequiredException e) { + assertEquals(PreconditionRequiredException.MessageKeys.PRECONDITION_REQUIRED, e.getMessageKey()); + } + } + + @Test + public void boundActionOnEsKeyNavPreconditionsRequired() throws Exception { + UriInfo uriInfo = + new Parser().parseUri("ESKeyNav(1)/Namespace1_Alias.BAETTwoKeyNavRTETTwoKeyNav", null, null, getEdm()); + try { + new PreconditionsValidator(new ETagSupport("ESKeyNav"), uriInfo, null, null).validatePreconditions(false); + fail("Expected a PreconditionRequiredException but was not thrown"); + } catch (PreconditionRequiredException e) { + assertEquals(PreconditionRequiredException.MessageKeys.PRECONDITION_REQUIRED, e.getMessageKey()); + } + } + + @Test + public void simpleEntityValuePreconditionsRequired() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESMedia(1)/$value", null, null, getEdm()); + try { + new PreconditionsValidator(new ETagSupport(), uriInfo, null, null).validatePreconditions(true); + fail("Expected a PreconditionRequiredException but was not thrown"); + } catch (PreconditionRequiredException e) { + assertEquals(PreconditionRequiredException.MessageKeys.PRECONDITION_REQUIRED, e.getMessageKey()); + } + } + + @Test + public void EntityAndToOneNavigationPreconditionsRequired() throws Exception { + UriInfo uriInfo = new Parser().parseUri("ESAllPrim(1)/NavPropertyETTwoPrimOne", null, null, getEdm()); + try { + new PreconditionsValidator(new ETagSupport(), uriInfo, null, null).validatePreconditions(false); + fail("Expected a PreconditionRequiredException but was not thrown"); + } catch (PreconditionRequiredException e) { + assertEquals(PreconditionRequiredException.MessageKeys.PRECONDITION_REQUIRED, e.getMessageKey()); + } + } + + private Edm getEdm() { + return new EdmProviderImpl(new EdmTechProvider()); + } + + public class ETagSupport implements CustomETagSupport { + + private boolean eTag = true; + private boolean mediaETag = true; + private String entitySetName; + + public ETagSupport() { + } + + public ETagSupport(String entitySetName) { + this.entitySetName = entitySetName; + } + + public ETagSupport(boolean eTag, boolean mediaETag) { + this.eTag = eTag; + this.mediaETag = mediaETag; + } + + + @Override + public boolean hasETag(String entitySetName) { + if(this.entitySetName != null){ + assertEquals(this.entitySetName, entitySetName); + } + return eTag; + } + + @Override + public boolean hasMediaETag(String entitySetName) { + if(this.entitySetName != null){ + assertEquals(this.entitySetName, entitySetName); + } + return mediaETag; + } + } +}
