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;
+    }
+  }
+}

Reply via email to