| /* |
| * 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.io.IOException; |
| 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.EdmSingleton; |
| 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.ODataLibraryException; |
| import org.apache.olingo.server.api.ODataRequest; |
| import org.apache.olingo.server.api.ODataResponse; |
| import org.apache.olingo.server.api.etag.CustomETagSupport; |
| import org.apache.olingo.server.api.etag.PreconditionException; |
| 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.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.api.uri.UriResourceSingleton; |
| import org.apache.olingo.server.core.batchhandler.BatchHandler; |
| import org.apache.olingo.server.core.etag.PreconditionsValidator; |
| |
| public class ODataDispatcher { |
| |
| private static final String NOT_IMPLEMENTED_MESSAGE = "not implemented"; |
| private final UriInfo uriInfo; |
| private final ODataHandlerImpl handler; |
| private static final String RETURN_MINIMAL = "return=minimal"; |
| private static final String RETURN_REPRESENTATION = "return=representation"; |
| private static final String EDMSTREAM = "Edm.Stream"; |
| |
| public ODataDispatcher(final UriInfo uriInfo, final ODataHandlerImpl handler) { |
| this.uriInfo = uriInfo; |
| this.handler = handler; |
| } |
| |
| public void dispatch(final ODataRequest request, final ODataResponse response) throws ODataApplicationException, |
| ODataLibraryException { |
| switch (uriInfo.getKind()) { |
| case metadata: |
| checkMethods(request.getMethod(), HttpMethod.GET, HttpMethod.HEAD); |
| final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), RepresentationType.METADATA); |
| handler.selectProcessor(MetadataProcessor.class) |
| .readMetadata(request, response, uriInfo, requestedContentType); |
| break; |
| |
| case service: |
| checkMethods(request.getMethod(), HttpMethod.GET, HttpMethod.HEAD); |
| 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: |
| case entityId: |
| handleResourceDispatching(request, response); |
| break; |
| |
| case batch: |
| checkMethod(request.getMethod(), HttpMethod.POST); |
| new BatchHandler(handler, handler.selectProcessor(BatchProcessor.class)) |
| .process(request, response, true); |
| break; |
| |
| default: |
| throw new ODataHandlerException(NOT_IMPLEMENTED_MESSAGE, |
| ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); |
| } |
| } |
| |
| private void handleResourceDispatching(final ODataRequest request, final ODataResponse response) |
| throws ODataApplicationException, ODataLibraryException { |
| |
| 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(), isEntityOrNavigationMedia(lastPathSegment)); |
| break; |
| |
| case singleton: |
| handleSingleEntityDispatching(request, response, isSingletonMedia(lastPathSegment), true); |
| 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_MESSAGE, |
| ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); |
| } |
| } |
| |
| private void handleFunctionDispatching(final ODataRequest request, final ODataResponse response, |
| final UriResourceFunction uriResourceFunction) throws ODataApplicationException, ODataLibraryException { |
| 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_MESSAGE, |
| ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); |
| } |
| } |
| |
| private void handleActionDispatching(final ODataRequest request, final ODataResponse response, |
| final UriResourceAction uriResourceAction) throws ODataApplicationException, ODataLibraryException { |
| final EdmAction action = uriResourceAction.getAction(); |
| if (action.isBound()) { |
| // Only bound actions can have ETag control for the binding parameter. |
| validatePreconditions(request, false); |
| } |
| final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.ACTION_PARAMETERS, false); |
| final EdmReturnType returnType = action.getReturnType(); |
| if (returnType == null) { |
| handler.selectProcessor(ActionVoidProcessor.class) |
| .processActionVoid(request, response, uriInfo, requestFormat); |
| } else { |
| final boolean isCollection = returnType.isCollection(); |
| ContentType responseFormat; |
| 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_MESSAGE, |
| ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED); |
| } |
| } |
| } |
| |
| private void handleReferenceDispatching(final ODataRequest request, final ODataResponse response, |
| final int lastPathSegmentIndex) throws ODataApplicationException, ODataLibraryException { |
| final HttpMethod httpMethod = request.getMethod(); |
| final boolean isCollection = ((UriResourcePartTyped) uriInfo.getUriResourceParts() |
| .get(lastPathSegmentIndex - 1)) |
| .isCollection(); |
| |
| if (isCollection && httpMethod == HttpMethod.GET) { |
| validatePreferHeader(request); |
| final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_REFERENCE); |
| handler.selectProcessor(ReferenceCollectionProcessor.class) |
| .readReferenceCollection(request, response, uriInfo, responseFormat); |
| |
| } else if (isCollection && httpMethod == HttpMethod.POST) { |
| final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.REFERENCE, true); |
| handler.selectProcessor(ReferenceProcessor.class) |
| .createReference(request, response, uriInfo, requestFormat); |
| |
| } else if (!isCollection && httpMethod == HttpMethod.GET) { |
| validatePreferHeader(request); |
| final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), RepresentationType.REFERENCE); |
| handler.selectProcessor(ReferenceProcessor.class).readReference(request, response, uriInfo, responseFormat); |
| |
| } else if (!isCollection && (httpMethod == HttpMethod.PUT || httpMethod == HttpMethod.PATCH)) { |
| final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.REFERENCE, true); |
| handler.selectProcessor(ReferenceProcessor.class) |
| .updateReference(request, response, uriInfo, requestFormat); |
| |
| } else if (httpMethod == HttpMethod.DELETE) { |
| validatePreferHeader(request); |
| handler.selectProcessor(ReferenceProcessor.class) |
| .deleteReference(request, response, uriInfo); |
| |
| } else { |
| throwMethodNotAllowed(httpMethod); |
| } |
| } |
| |
| private void handleValueDispatching(final ODataRequest request, final ODataResponse response, |
| final int lastPathSegmentIndex) throws ODataApplicationException, ODataLibraryException { |
| // The URI Parser already checked if $value is allowed here so we only have to dispatch to the correct processor |
| final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); |
| if (resource instanceof UriResourceProperty |
| || resource instanceof UriResourceFunction |
| && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { |
| handlePrimitiveValueDispatching(request, response, resource); |
| } else { |
| handleMediaValueDispatching(request, response, resource); |
| } |
| } |
| |
| private void handleMediaValueDispatching(final ODataRequest request, final ODataResponse response, |
| final UriResource resource) throws ContentNegotiatorException, |
| ODataApplicationException, ODataLibraryException, |
| ODataHandlerException, PreconditionException { |
| final HttpMethod method = request.getMethod(); |
| validatePreferHeader(request); |
| if (method == HttpMethod.GET) { |
| // This can be a GET on an EntitySet, Navigation or Function |
| final ContentType requestedContentType = ContentNegotiator. |
| doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), RepresentationType.MEDIA); |
| handler.selectProcessor(MediaEntityProcessor.class) |
| .readMediaEntity(request, response, uriInfo, requestedContentType); |
| // PUT and DELETE can only be called on EntitySets or Navigation properties which are media resources |
| } else if (method == HttpMethod.PUT && (isEntityOrNavigationMedia(resource) |
| || isSingletonMedia(resource))) { |
| 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 && isEntityOrNavigationMedia(resource)) { |
| validatePreconditions(request, true); |
| handler.selectProcessor(MediaEntityProcessor.class) |
| .deleteMediaEntity(request, response, uriInfo); |
| } else { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| private void handlePrimitiveValueDispatching(final ODataRequest request, final ODataResponse response, |
| final UriResource resource) throws ContentNegotiatorException, |
| ODataApplicationException, ODataLibraryException, |
| ODataHandlerException, PreconditionException { |
| final HttpMethod method = request.getMethod(); |
| 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) { |
| validatePreferHeader(request); |
| 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) { |
| validatePreconditions(request, false); |
| final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), |
| valueRepresentationType, true); |
| final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), valueRepresentationType); |
| handler.selectProcessor(PrimitiveValueProcessor.class) |
| .updatePrimitiveValue(request, response, uriInfo, requestFormat, responseFormat); |
| } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { |
| validatePreferHeader(request); |
| validatePreconditions(request, false); |
| handler.selectProcessor(PrimitiveValueProcessor.class) |
| .deletePrimitiveValue(request, response, uriInfo); |
| } else { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| private void handleComplexDispatching(final ODataRequest request, final ODataResponse response, |
| final boolean isCollection) throws ODataApplicationException, ODataLibraryException { |
| final HttpMethod method = request.getMethod(); |
| final RepresentationType complexRepresentationType = isCollection ? RepresentationType.COLLECTION_COMPLEX |
| : RepresentationType.COMPLEX; |
| if (method == HttpMethod.GET) { |
| validatePreferHeader(request); |
| 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) { |
| validatePreconditions(request, false); |
| final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), |
| complexRepresentationType, true); |
| 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) { |
| validatePreferHeader(request); |
| validatePreconditions(request, false); |
| if (isCollection) { |
| handler.selectProcessor(ComplexCollectionProcessor.class) |
| .deleteComplexCollection(request, response, uriInfo); |
| } else { |
| handler.selectProcessor(ComplexProcessor.class) |
| .deleteComplex(request, response, uriInfo); |
| } |
| } else { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| private void handlePrimitiveDispatching(final ODataRequest request, final ODataResponse response, |
| final boolean isCollection) throws ODataApplicationException, ODataLibraryException { |
| final HttpMethod method = request.getMethod(); |
| final RepresentationType representationType = isCollection ? RepresentationType.COLLECTION_PRIMITIVE |
| : RepresentationType.PRIMITIVE; |
| if (method == HttpMethod.GET) { |
| validatePreferHeader(request); |
| 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) { |
| validatePreconditions(request, false); |
| ContentType requestFormat = null; |
| List<UriResource> uriResources = uriInfo.getUriResourceParts(); |
| UriResource uriResource = uriResources.get(uriResources.size() - 1); |
| if (uriResource instanceof UriResourcePrimitiveProperty && |
| ((UriResourcePrimitiveProperty)uriResource).getType() |
| .getFullQualifiedName().getFullQualifiedNameAsString().equalsIgnoreCase(EDMSTREAM)) { |
| requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); |
| } else { |
| requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), |
| representationType, true); |
| } |
| 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) { |
| validatePreferHeader(request); |
| validatePreconditions(request, false); |
| if (isCollection) { |
| handler.selectProcessor(PrimitiveCollectionProcessor.class) |
| .deletePrimitiveCollection(request, response, uriInfo); |
| } else { |
| handler.selectProcessor(PrimitiveProcessor.class) |
| .deletePrimitive(request, response, uriInfo); |
| } |
| } else { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| private void handleCountDispatching(final ODataRequest request, final ODataResponse response, |
| final int lastPathSegmentIndex) throws ODataApplicationException, ODataLibraryException { |
| validatePreferHeader(request); |
| 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); |
| } |
| } |
| |
| private void handleEntityDispatching(final ODataRequest request, final ODataResponse response, |
| final boolean isCollection, final boolean isMedia) throws ODataApplicationException, ODataLibraryException { |
| if (isCollection) { |
| handleEntityCollectionDispatching(request, response, isMedia); |
| } else { |
| handleSingleEntityDispatching(request, response, isMedia, false); |
| } |
| } |
| |
| |
| private void handleEntityCollectionDispatching(final ODataRequest request, final ODataResponse response, |
| final boolean isMedia |
| ) throws ContentNegotiatorException, ODataApplicationException, ODataLibraryException, |
| ODataHandlerException { |
| final HttpMethod method = request.getMethod(); |
| if (method == HttpMethod.GET) { |
| validatePreferHeader(request); |
| 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 responseFormat = ContentNegotiator. |
| doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); |
| if (isMedia) { |
| validatePreferHeader(request); |
| final ContentType requestFormat = ContentType.parse( |
| request.getHeader(HttpHeader.CONTENT_TYPE)); |
| handler.selectProcessor(MediaEntityProcessor.class) |
| .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat); |
| } else { |
| try { |
| final ContentType requestFormat = (request.getHeader(HttpHeader.CONTENT_TYPE) == null && |
| (request.getBody() == null || request.getBody().available() == 0)) ? |
| getSupportedContentType( |
| request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.ENTITY, false) : getSupportedContentType( |
| request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.ENTITY, true); |
| handler.selectProcessor(EntityProcessor.class) |
| .createEntity(request, response, uriInfo, requestFormat, responseFormat); |
| } catch (IOException e) { |
| throw new ODataHandlerException("There is problem in the payload.", |
| ODataHandlerException.MessageKeys.INVALID_PAYLOAD); |
| } |
| } |
| } else if (method == HttpMethod.PUT && uriInfo.getUriResourceParts().size()==2) { |
| if (isMedia) { |
| validatePreferHeader(request); |
| } |
| validatePreconditions(request, false); |
| final ContentType requestFormat = getSupportedContentType( |
| request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.ENTITY, true); |
| final ContentType responseFormat = ContentNegotiator. |
| doContentNegotiation(uriInfo.getFormatOption(), |
| request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); |
| handler.selectProcessor(EntityProcessor.class) |
| .updateEntity(request, response, uriInfo, requestFormat, responseFormat); |
| } else { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| /**Checks if Prefer header is set with return=minimal or |
| * return=representation for GET and DELETE requests |
| * @param request |
| * @throws ODataHandlerException |
| */ |
| private void validatePreferHeader(final ODataRequest request) throws ODataHandlerException { |
| final List<String> returnPreference = request.getHeaders(HttpHeader.PREFER); |
| if (null != returnPreference) { |
| for (String preference : returnPreference) { |
| if (preference.equals(RETURN_MINIMAL) || preference.equals(RETURN_REPRESENTATION)) { |
| throw new ODataHandlerException("Prefer Header not supported: " + preference, |
| ODataHandlerException.MessageKeys.INVALID_PREFER_HEADER, preference); |
| } |
| } |
| } |
| } |
| |
| private boolean isSingletonMedia(final UriResource pathSegment) { |
| return pathSegment instanceof UriResourceSingleton |
| && ((UriResourceSingleton) pathSegment).getEntityType().hasStream(); |
| } |
| |
| |
| |
| private void handleSingleEntityDispatching(final ODataRequest request, final ODataResponse response, |
| final boolean isMedia, final boolean isSingleton) throws |
| ContentNegotiatorException, ODataApplicationException, |
| ODataLibraryException, ODataHandlerException, PreconditionException { |
| final HttpMethod method = request.getMethod(); |
| if (method == HttpMethod.GET) { |
| validatePreferHeader(request); |
| 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) { |
| if (isMedia) { |
| validatePreferHeader(request); |
| } |
| validatePreconditions(request, false); |
| final ContentType requestFormat = getSupportedContentType( |
| request.getHeader(HttpHeader.CONTENT_TYPE), |
| RepresentationType.ENTITY, true); |
| 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 && !isSingleton) { |
| validateIsSingleton(method); |
| validatePreconditions(request, false); |
| validatePreferHeader(request); |
| if (isMedia) { |
| ((MediaEntityProcessor) handler.selectProcessor(MediaEntityProcessor.class)) |
| .deleteEntity(request, response, uriInfo); |
| } else { |
| ((EntityProcessor) handler.selectProcessor(EntityProcessor.class)) |
| .deleteEntity(request, response, uriInfo); |
| } |
| } else { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| /*Delete method is not allowed for Entities navigating to Singleton*/ |
| private void validateIsSingleton(HttpMethod method) throws ODataHandlerException { |
| final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1; |
| final UriResource pathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); |
| if (pathSegment instanceof UriResourceNavigation |
| && uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1) instanceof UriResourceEntitySet |
| && ((UriResourceEntitySet) uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1)).getEntitySet() |
| .getRelatedBindingTarget( |
| pathSegment.getSegmentValue()) instanceof EdmSingleton) { |
| throwMethodNotAllowed(method); |
| } |
| } |
| |
| |
| private void validatePreconditions(final ODataRequest request, final boolean isMediaValue) |
| throws PreconditionException { |
| // If needed perform preconditions validation. |
| final CustomETagSupport eTagSupport = handler.getCustomETagSupport(); |
| if (eTagSupport != null |
| && new PreconditionsValidator(uriInfo).mustValidatePreconditions(eTagSupport, isMediaValue) |
| && request.getHeader(HttpHeader.IF_MATCH) == null |
| && request.getHeader(HttpHeader.IF_NONE_MATCH) == null) { |
| throw new PreconditionException("Expected an if-match or if-none-match header.", |
| PreconditionException.MessageKeys.MISSING_HEADER); |
| } |
| } |
| |
| private void checkMethod(final HttpMethod requestMethod, final HttpMethod allowedMethod) |
| throws ODataHandlerException { |
| if (requestMethod != allowedMethod) { |
| throwMethodNotAllowed(requestMethod); |
| } |
| } |
| |
| private void checkMethods(final HttpMethod requestMethod, final HttpMethod... allowedMethods) |
| throws ODataHandlerException { |
| //Check if the request method is one of the allowed ones |
| for (int i = 0; i < allowedMethods.length; i++) { |
| if (requestMethod == allowedMethods[i]) { |
| return; |
| } |
| } |
| //request method does not match any allowed method |
| throwMethodNotAllowed(requestMethod); |
| } |
| |
| private void throwMethodNotAllowed(final HttpMethod httpMethod) throws ODataHandlerException { |
| throw new ODataHandlerException("HTTP method " + httpMethod + " is not allowed.", |
| ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, httpMethod.toString()); |
| } |
| |
| private ContentType getSupportedContentType(final String contentTypeHeader, |
| final RepresentationType representationType, final boolean mustNotBeNull) |
| throws ODataHandlerException, ContentNegotiatorException { |
| if (contentTypeHeader == null) { |
| if (mustNotBeNull) { |
| throw new ODataHandlerException("ContentTypeHeader parameter is null", |
| ODataHandlerException.MessageKeys.MISSING_CONTENT_TYPE); |
| } |
| return ContentType.APPLICATION_JSON; |
| } |
| ContentType contentType; |
| try { |
| contentType = ContentType.create(contentTypeHeader); |
| } catch (final IllegalArgumentException e) { |
| throw new ODataHandlerException("Illegal content type.", e, |
| ODataHandlerException.MessageKeys.INVALID_CONTENT_TYPE, contentTypeHeader); |
| } |
| ContentNegotiator.checkSupport(contentType, handler.getCustomContentTypeSupport(), representationType); |
| return contentType; |
| } |
| |
| private boolean isEntityOrNavigationMedia(final UriResource pathSegment) { |
| // This method MUST NOT check if the resource is of type function since these are handled differently |
| return pathSegment instanceof UriResourceEntitySet |
| && ((UriResourceEntitySet) pathSegment).getEntityType().hasStream() |
| || pathSegment instanceof UriResourceNavigation |
| && ((EdmEntityType) ((UriResourceNavigation) pathSegment).getType()).hasStream(); |
| } |
| |
| } |