You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2016/02/24 15:28:46 UTC

olingo-odata4 git commit: [OLINGO-884] Allow PUT on navigation for media resource values

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 0879bfbe5 -> 73f465102


[OLINGO-884] Allow PUT on navigation for media resource values


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/73f46510
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/73f46510
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/73f46510

Branch: refs/heads/master
Commit: 73f465102a8ea74a4ca6eb3ecc0c8fffbcc3a039
Parents: 0879bfb
Author: Christian Amend <ch...@sap.com>
Authored: Wed Feb 24 15:26:52 2016 +0100
Committer: Christian Amend <ch...@sap.com>
Committed: Wed Feb 24 15:26:52 2016 +0100

----------------------------------------------------------------------
 .../olingo/server/core/ODataDispatcher.java     |  92 +++++++--------
 .../core/uri/parser/ResourcePathParser.java     |  30 ++++-
 .../uri/parser/UriParserSemanticException.java  |   4 +-
 .../server-core-exceptions-i18n.properties      |   1 +
 .../olingo/server/core/ODataHandlerTest.java    |  54 ++++++++-
 .../core/uri/parser/TestFullResourcePath.java   | 113 ++++++++++---------
 .../core/uri/testutil/TestUriValidator.java     |   1 +
 7 files changed, 189 insertions(+), 106 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/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
index 4971c8e..ceacd10 100644
--- 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
@@ -6,9 +6,9 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License. You may obtain a copy of the License at
- *
+ * 
  * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * 
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -83,26 +83,26 @@ public class ODataDispatcher {
   }
 
   public void dispatch(final ODataRequest request, final ODataResponse response) throws ODataApplicationException,
-  ODataLibraryException {
+      ODataLibraryException {
     switch (uriInfo.getKind()) {
     case metadata:
       checkMethod(request.getMethod(), HttpMethod.GET);
       final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
           request, handler.getCustomContentTypeSupport(), RepresentationType.METADATA);
       handler.selectProcessor(MetadataProcessor.class)
-      .readMetadata(request, response, uriInfo, requestedContentType);
+          .readMetadata(request, response, uriInfo, requestedContentType);
       break;
 
     case service:
       checkMethod(request.getMethod(), HttpMethod.GET);
       if ("".equals(request.getRawODataPath())) {
         handler.selectProcessor(RedirectProcessor.class)
-        .redirect(request, response);
+            .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);
+            .readServiceDocument(request, response, uriInfo, serviceContentType);
       }
       break;
 
@@ -113,7 +113,7 @@ public class ODataDispatcher {
     case batch:
       checkMethod(request.getMethod(), HttpMethod.POST);
       new BatchHandler(handler, handler.selectProcessor(BatchProcessor.class))
-      .process(request, response, true);
+          .process(request, response, true);
       break;
 
     default:
@@ -142,7 +142,7 @@ public class ODataDispatcher {
     case entitySet:
     case navigationProperty:
       handleEntityDispatching(request, response,
-          ((UriResourcePartTyped) lastPathSegment).isCollection(), isMedia(lastPathSegment));
+          ((UriResourcePartTyped) lastPathSegment).isCollection(), isEntityOrNavigationMedia(lastPathSegment));
       break;
 
     case count:
@@ -211,7 +211,7 @@ public class ODataDispatcher {
     final EdmReturnType returnType = action.getReturnType();
     if (returnType == null) {
       handler.selectProcessor(ActionVoidProcessor.class)
-      .processActionVoid(request, response, uriInfo, requestFormat);
+          .processActionVoid(request, response, uriInfo, requestFormat);
     } else {
       final boolean isCollection = returnType.isCollection();
       ContentType responseFormat;
@@ -222,10 +222,10 @@ public class ODataDispatcher {
             isCollection ? RepresentationType.COLLECTION_ENTITY : RepresentationType.ENTITY);
         if (isCollection) {
           handler.selectProcessor(ActionEntityCollectionProcessor.class)
-          .processActionEntityCollection(request, response, uriInfo, requestFormat, responseFormat);
+              .processActionEntityCollection(request, response, uriInfo, requestFormat, responseFormat);
         } else {
           handler.selectProcessor(ActionEntityProcessor.class)
-          .processActionEntity(request, response, uriInfo, requestFormat, responseFormat);
+              .processActionEntity(request, response, uriInfo, requestFormat, responseFormat);
         }
         break;
 
@@ -235,10 +235,10 @@ public class ODataDispatcher {
             isCollection ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE);
         if (isCollection) {
           handler.selectProcessor(ActionPrimitiveCollectionProcessor.class)
-          .processActionPrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
+              .processActionPrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
         } else {
           handler.selectProcessor(ActionPrimitiveProcessor.class)
-          .processActionPrimitive(request, response, uriInfo, requestFormat, responseFormat);
+              .processActionPrimitive(request, response, uriInfo, requestFormat, responseFormat);
         }
         break;
 
@@ -248,10 +248,10 @@ public class ODataDispatcher {
             isCollection ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX);
         if (isCollection) {
           handler.selectProcessor(ActionComplexCollectionProcessor.class)
-          .processActionComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
+              .processActionComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
         } else {
           handler.selectProcessor(ActionComplexProcessor.class)
-          .processActionComplex(request, response, uriInfo, requestFormat, responseFormat);
+              .processActionComplex(request, response, uriInfo, requestFormat, responseFormat);
         }
         break;
 
@@ -273,13 +273,13 @@ public class ODataDispatcher {
       final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
           request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_REFERENCE);
       handler.selectProcessor(ReferenceCollectionProcessor.class)
-      .readReferenceCollection(request, response, uriInfo, responseFormat);
+          .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);
+          .createReference(request, response, uriInfo, requestFormat);
 
     } else if (!isCollection && httpMethod == HttpMethod.GET) {
       final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
@@ -290,11 +290,11 @@ public class ODataDispatcher {
       final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
           RepresentationType.REFERENCE, true);
       handler.selectProcessor(ReferenceProcessor.class)
-      .updateReference(request, response, uriInfo, requestFormat);
+          .updateReference(request, response, uriInfo, requestFormat);
 
     } else if (httpMethod == HttpMethod.DELETE) {
       handler.selectProcessor(ReferenceProcessor.class)
-      .deleteReference(request, response, uriInfo);
+          .deleteReference(request, response, uriInfo);
 
     } else {
       throwMethodNotAllowed(httpMethod);
@@ -303,6 +303,7 @@ public class ODataDispatcher {
 
   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 HttpMethod method = request.getMethod();
     final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1);
     if (resource instanceof UriResourceProperty
@@ -336,18 +337,20 @@ public class ODataDispatcher {
           }
     } else {
       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);
-      } else if (method == HttpMethod.PUT && resource instanceof UriResourceEntitySet) {
+        //PUT and DELETE can only be called on EntitySets or Navigation properties which are media resources
+      } else if (method == HttpMethod.PUT && isEntityOrNavigationMedia(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 && resource instanceof UriResourceEntitySet) {
+      } else if (method == HttpMethod.DELETE && isEntityOrNavigationMedia(resource)) {
         validatePreconditions(request, true);
         handler.selectProcessor(MediaEntityProcessor.class)
         .deleteMediaEntity(request, response, uriInfo);
@@ -367,10 +370,10 @@ public class ODataDispatcher {
           request, handler.getCustomContentTypeSupport(), complexRepresentationType);
       if (isCollection) {
         handler.selectProcessor(ComplexCollectionProcessor.class)
-        .readComplexCollection(request, response, uriInfo, requestedContentType);
+            .readComplexCollection(request, response, uriInfo, requestedContentType);
       } else {
         handler.selectProcessor(ComplexProcessor.class)
-        .readComplex(request, response, uriInfo, requestedContentType);
+            .readComplex(request, response, uriInfo, requestedContentType);
       }
     } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) {
       validatePreconditions(request, false);
@@ -380,19 +383,19 @@ public class ODataDispatcher {
           request, handler.getCustomContentTypeSupport(), complexRepresentationType);
       if (isCollection) {
         handler.selectProcessor(ComplexCollectionProcessor.class)
-        .updateComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
+            .updateComplexCollection(request, response, uriInfo, requestFormat, responseFormat);
       } else {
         handler.selectProcessor(ComplexProcessor.class)
-        .updateComplex(request, response, uriInfo, requestFormat, responseFormat);
+            .updateComplex(request, response, uriInfo, requestFormat, responseFormat);
       }
     } else if (method == HttpMethod.DELETE) {
       validatePreconditions(request, false);
       if (isCollection) {
         handler.selectProcessor(ComplexCollectionProcessor.class)
-        .deleteComplexCollection(request, response, uriInfo);
+            .deleteComplexCollection(request, response, uriInfo);
       } else {
         handler.selectProcessor(ComplexProcessor.class)
-        .deleteComplex(request, response, uriInfo);
+            .deleteComplex(request, response, uriInfo);
       }
     } else {
       throwMethodNotAllowed(method);
@@ -409,10 +412,10 @@ public class ODataDispatcher {
           request, handler.getCustomContentTypeSupport(), representationType);
       if (isCollection) {
         handler.selectProcessor(PrimitiveCollectionProcessor.class)
-        .readPrimitiveCollection(request, response, uriInfo, requestedContentType);
+            .readPrimitiveCollection(request, response, uriInfo, requestedContentType);
       } else {
         handler.selectProcessor(PrimitiveProcessor.class)
-        .readPrimitive(request, response, uriInfo, requestedContentType);
+            .readPrimitive(request, response, uriInfo, requestedContentType);
       }
     } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) {
       validatePreconditions(request, false);
@@ -422,19 +425,19 @@ public class ODataDispatcher {
           request, handler.getCustomContentTypeSupport(), representationType);
       if (isCollection) {
         handler.selectProcessor(PrimitiveCollectionProcessor.class)
-        .updatePrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
+            .updatePrimitiveCollection(request, response, uriInfo, requestFormat, responseFormat);
       } else {
         handler.selectProcessor(PrimitiveProcessor.class)
-        .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat);
+            .updatePrimitive(request, response, uriInfo, requestFormat, responseFormat);
       }
     } else if (method == HttpMethod.DELETE) {
       validatePreconditions(request, false);
       if (isCollection) {
         handler.selectProcessor(PrimitiveCollectionProcessor.class)
-        .deletePrimitiveCollection(request, response, uriInfo);
+            .deletePrimitiveCollection(request, response, uriInfo);
       } else {
         handler.selectProcessor(PrimitiveProcessor.class)
-        .deletePrimitive(request, response, uriInfo);
+            .deletePrimitive(request, response, uriInfo);
       }
     } else {
       throwMethodNotAllowed(method);
@@ -449,15 +452,15 @@ public class ODataDispatcher {
         || resource instanceof UriResourceFunction
         && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.ENTITY) {
       handler.selectProcessor(CountEntityCollectionProcessor.class)
-      .countEntityCollection(request, response, uriInfo);
+          .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);
+          .countPrimitiveCollection(request, response, uriInfo);
     } else {
       handler.selectProcessor(CountComplexCollectionProcessor.class)
-      .countComplexCollection(request, response, uriInfo);
+          .countComplexCollection(request, response, uriInfo);
     }
   }
 
@@ -469,19 +472,19 @@ public class ODataDispatcher {
         final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
             request, handler.getCustomContentTypeSupport(), RepresentationType.COLLECTION_ENTITY);
         handler.selectProcessor(EntityCollectionProcessor.class)
-        .readEntityCollection(request, response, uriInfo, requestedContentType);
+            .readEntityCollection(request, response, uriInfo, requestedContentType);
       } else if (method == HttpMethod.POST) {
         final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
             request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY);
         if (isMedia) {
           final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE));
           handler.selectProcessor(MediaEntityProcessor.class)
-          .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat);
+              .createMediaEntity(request, response, uriInfo, requestFormat, responseFormat);
         } else {
           final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
               RepresentationType.ENTITY, true);
           handler.selectProcessor(EntityProcessor.class)
-          .createEntity(request, response, uriInfo, requestFormat, responseFormat);
+              .createEntity(request, response, uriInfo, requestFormat, responseFormat);
         }
       } else {
         throwMethodNotAllowed(method);
@@ -491,7 +494,7 @@ public class ODataDispatcher {
         final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
             request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY);
         handler.selectProcessor(EntityProcessor.class)
-        .readEntity(request, response, uriInfo, requestedContentType);
+            .readEntity(request, response, uriInfo, requestedContentType);
       } else if (method == HttpMethod.PUT || method == HttpMethod.PATCH) {
         validatePreconditions(request, false);
         final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE),
@@ -499,11 +502,11 @@ public class ODataDispatcher {
         final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
             request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY);
         handler.selectProcessor(EntityProcessor.class)
-        .updateEntity(request, response, uriInfo, requestFormat, responseFormat);
+            .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);
+            .deleteEntity(request, response, uriInfo);
       } else {
         throwMethodNotAllowed(method);
       }
@@ -537,7 +540,7 @@ public class ODataDispatcher {
 
   private ContentType getSupportedContentType(final String contentTypeHeader,
       final RepresentationType representationType, final boolean mustNotBeNull)
-          throws ODataHandlerException, ContentNegotiatorException {
+      throws ODataHandlerException, ContentNegotiatorException {
     if (contentTypeHeader == null) {
       if (mustNotBeNull) {
         throw new ODataHandlerException(contentTypeHeader, ODataHandlerException.MessageKeys.MISSING_CONTENT_TYPE);
@@ -555,7 +558,8 @@ public class ODataDispatcher {
     return contentType;
   }
 
-  private boolean isMedia(final UriResource pathSegment) {
+  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

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
index 8d6d52d..5c280c8 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ResourcePathParser.java
@@ -6,9 +6,9 @@
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License. You may obtain a copy of the License at
- *
+ * 
  * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * 
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -39,6 +39,9 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
 import org.apache.olingo.server.api.uri.UriParameter;
 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.UriResourceNavigation;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
 import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
 import org.apache.olingo.server.core.uri.UriResourceActionImpl;
@@ -120,7 +123,7 @@ public class ResourcePathParser {
     tokenizer = new UriTokenizer(pathSegment);
     ParserHelper.requireNext(tokenizer, TokenKind.CROSSJOIN);
     ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
-    // At least one entity-set name is mandatory.  Try to fetch all.
+    // At least one entity-set name is mandatory. Try to fetch all.
     List<String> entitySetNames = new ArrayList<String>();
     do {
       ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
@@ -153,6 +156,7 @@ public class ResourcePathParser {
     ParserHelper.requireTokenEnd(tokenizer);
     requireTyped(previous, "$value");
     if (!((UriResourcePartTyped) previous).isCollection()) {
+      requireMediaResourceInCaseOfEntity(previous);
       return new UriResourceValueImpl();
     } else {
       throw new UriParserSemanticException("$value is only allowed on typed path segments.",
@@ -160,6 +164,26 @@ public class ResourcePathParser {
     }
   }
 
+  private void requireMediaResourceInCaseOfEntity(UriResource resource) throws UriParserSemanticException {
+    // If the resource is an entity or navigatio
+    if (resource instanceof UriResourceEntitySet && !((UriResourceEntitySet) resource).getEntityType().hasStream()
+        || resource instanceof UriResourceNavigation
+        && !((EdmEntityType) ((UriResourceNavigation) resource).getType()).hasStream()) {
+      throw new UriParserSemanticException("$value on entity is only allowed on media resources.",
+          UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE, resource.getSegmentValue());
+    }
+
+    // Functions can also deliver an entity. In this case we have to check if the returned entity is a media resource
+    if (resource instanceof UriResourceFunction) {
+      EdmType returnType = ((UriResourceFunction) resource).getFunction().getReturnType().getType();
+      //Collection check is above so not needed here
+      if (returnType instanceof EdmEntityType && !((EdmEntityType) returnType).hasStream()) {
+        throw new UriParserSemanticException("$value on returned entity is only allowed on media resources.",
+            UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE, resource.getSegmentValue());
+      }
+    }
+  }
+
   private UriResource count(final UriResource previous) throws UriParserException {
     ParserHelper.requireTokenEnd(tokenizer);
     requireTyped(previous, "$count");

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
index 423c875..db33a6a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParserSemanticException.java
@@ -66,7 +66,9 @@ public class UriParserSemanticException extends UriParserException {
     /** parameter: complex parameter value */
     COMPLEX_PARAMETER_IN_RESOURCE_PATH,
     /** parameters: left type, right type */
-    TYPES_NOT_COMPATIBLE;
+    TYPES_NOT_COMPATIBLE,
+    /** parameter: addressed resource name*/
+    NOT_A_MEDIA_RESOURCE;
     
     @Override
     public String getKey() {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
index ef5e744..c563a67 100644
--- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
+++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
@@ -72,6 +72,7 @@ UriParserSemanticException.NOT_IMPLEMENTED='%1$s' is not implemented!
 UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is not allowed for Entity Sets, Singletons, Action Imports and Function Imports; found '%1$s'.
 UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments; found: '%1$s'.
 UriParserSemanticException.TYPES_NOT_COMPATIBLE=The types '%1$s' and '%2$s' are not compatible.
+UriParserSemanticException.NOT_A_MEDIA_RESOURCE=The resource '%1$s' is not a media resource. $value can only be applied on media resources.
 
 UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not supported.
 UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not supported.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
index d4b87d4..a228e40 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java
@@ -318,7 +318,7 @@ public class ODataHandlerTest {
     dispatch(HttpMethod.GET, "FICRTString()", primitiveProcessor);
     verify(primitiveProcessor).readPrimitive(
         any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class));
-    
+
     // FINRTInt16 is not composable so /$value is not allowed
     final String valueUri = "FINRTInt16()/$value";
     final PrimitiveValueProcessor primitiveValueProcessor = mock(PrimitiveValueProcessor.class);
@@ -327,7 +327,7 @@ public class ODataHandlerTest {
     dispatchMethodWithError(HttpMethod.PATCH, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST);
     dispatchMethodWithError(HttpMethod.PUT, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST);
     dispatchMethodWithError(HttpMethod.DELETE, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST);
-    
+
     final String primitiveCollectionUri = "FICRTCollString()";
     PrimitiveCollectionProcessor primitiveCollectionProcessor = mock(PrimitiveCollectionProcessor.class);
     dispatch(HttpMethod.GET, primitiveCollectionUri, primitiveCollectionProcessor);
@@ -495,6 +495,52 @@ public class ODataHandlerTest {
     dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
     dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor);
   }
+  
+  @Test
+  public void dispatchValueOnNoMedia() throws Exception {
+    final String uri = "ESAllPrim(1)/$value";
+    final MediaEntityProcessor processor = mock(MediaEntityProcessor.class);
+
+    dispatch(HttpMethod.GET, uri, processor);
+    verifyZeroInteractions(processor);
+
+    dispatch(HttpMethod.POST, uri, processor);
+    verifyZeroInteractions(processor);
+
+    dispatch(HttpMethod.PUT, uri, processor);
+    verifyZeroInteractions(processor);
+
+    dispatch(HttpMethod.DELETE, uri, processor);
+    verifyZeroInteractions(processor);
+  }
+
+  @Test
+  public void dispatchMediaWithNavigation() throws Exception {
+    /*
+     * In Java we decided that any kind of navigation will be accepted. This means that a $value on a media resource
+     * must be dispatched as well
+     */
+    final String uri = "ESKeyNav(1)/NavPropertyETMediaOne/$value";
+    final MediaEntityProcessor processor = mock(MediaEntityProcessor.class);
+
+    dispatch(HttpMethod.GET, uri, processor);
+    verify(processor).readMediaEntity(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class));
+
+    dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne", processor);
+    
+    dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne/$value", processor);
+
+    dispatch(HttpMethod.PUT, uri, processor);
+    verify(processor).updateMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class),
+        any(ContentType.class), any(ContentType.class));
+
+    dispatch(HttpMethod.DELETE, uri, processor);
+    verify(processor).deleteMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class));
+
+    dispatchMethodNotAllowed(HttpMethod.POST, uri, processor);
+    dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor);
+  }
 
   @Test
   public void dispatchMediaDeleteIndirect() throws Exception {
@@ -758,8 +804,8 @@ public class ODataHandlerTest {
     assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode());
     assertNotNull(response.getContent());
   }
-  
-  private void dispatchMethodWithError(final HttpMethod method, final String path, final Processor processor, 
+
+  private void dispatchMethodWithError(final HttpMethod method, final String path, final Processor processor,
       final HttpStatusCode statusCode) {
     final ODataResponse response = dispatch(method, path, processor);
     assertEquals(statusCode.getStatusCode(), response.getStatusCode());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
index fc9a0b8..32c56b4 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/TestFullResourcePath.java
@@ -58,6 +58,20 @@ public class TestFullResourcePath {
   private final FilterValidator testFilter = new FilterValidator().setEdm(edm);
 
   @Test
+  public void valueOnNonMediaEntity() throws Exception {
+    testUri.runEx("ESAllPrim/$value").isExSemantic(UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS);
+    testUri.runEx("ESAllPrim(1)/NavPropertyETTwoPrimMany/$value").isExSemantic(
+        UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS);
+    testUri.runEx("FICRTCollESMedia()/$value")
+        .isExSemantic(UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS);
+
+    testUri.runEx("ESAllPrim(1)/$value").isExSemantic(UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE);
+    testUri.runEx("ESAllPrim(1)/NavPropertyETTwoPrimOne/$value").isExSemantic(
+        UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE);
+    testUri.runEx("FICRTETKeyNav()/$value").isExSemantic(UriParserSemanticException.MessageKeys.NOT_A_MEDIA_RESOURCE);
+  }
+
+  @Test
   public void enumAndTypeDefAsKey() throws Exception {
     testUri
         .run("ESMixEnumDefCollComp(PropertyEnumString=olingo.odata.test1.ENString'String1',PropertyDefString='abc')")
@@ -3205,15 +3219,6 @@ public class TestFullResourcePath {
         .isType(EntityTypeProvider.nameETTwoKeyNav)
         .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
         .n().isRef();
-
-    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')/olingo.odata.test1.ETBaseTwoKeyNav/$value")
-        .goPath().first()
-        .isEntitySet("ESTwoKeyNav")
-        .isKeyPredicate(0, "PropertyInt16", "1")
-        .isKeyPredicate(1, "PropertyString", "'2'")
-        .isType(EntityTypeProvider.nameETTwoKeyNav)
-        .isTypeFilterOnEntry(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .n().isValue();
   }
 
   @Test
@@ -3768,7 +3773,7 @@ public class TestFullResourcePath {
   public void filterFunctions() throws Exception {
     testFilter.runOnETAllPrim(
         "olingo.odata.test1.UFCRTETTwoKeyNavParamCTTwoPrim(ParameterCTTwoPrim=@ParamAlias) eq null"
-        + "&@ParamAlias={}")
+            + "&@ParamAlias={}")
         .is("<<UFCRTETTwoKeyNavParamCTTwoPrim> eq <null>>")
         .left().goPath()
         .first()
@@ -4687,7 +4692,7 @@ public class TestFullResourcePath {
 
     testFilter.runOnETKeyNav("NavPropertyETTwoKeyNavMany/any(d:d/PropertyInt16 eq 1 or "
         + "d/CollPropertyString/any(e:e eq 'SomeString'))")
-        .is("<NavPropertyETTwoKeyNavMany/<ANY;<<<d/PropertyInt16> eq <1>>" 
+        .is("<NavPropertyETTwoKeyNavMany/<ANY;<<<d/PropertyInt16> eq <1>>"
             + " or <d/CollPropertyString/<ANY;<<e> eq <'SomeString'>>>>>>>")
         .root().goPath()
         .first().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
@@ -4708,7 +4713,7 @@ public class TestFullResourcePath {
         .isType(EntityTypeProvider.nameETTwoKeyNav, false)
         .n().isUriPathInfoKind(UriResourceKind.primitiveProperty)
         .isPrimitiveProperty("CollPropertyString", PropertyProvider.nameString, true)
-        .at(2).isUriPathInfoKind(UriResourceKind.lambdaAny)        
+        .at(2).isUriPathInfoKind(UriResourceKind.lambdaAny)
         .goLambdaExpression()
         .root().left().goPath()
         .first().isUriPathInfoKind(UriResourceKind.lambdaVariable)
@@ -4743,7 +4748,7 @@ public class TestFullResourcePath {
         .isType(PropertyProvider.nameString, false);
 
     testFilter.runOnETKeyNav("NavPropertyETTwoKeyNavMany/any(d:d/PropertyString eq 'SomeString' and "
-            + "d/CollPropertyString/any(e:e eq d/PropertyString))")
+        + "d/CollPropertyString/any(e:e eq d/PropertyString))")
         .is("<NavPropertyETTwoKeyNavMany/<ANY;<<<d/PropertyString> eq <'SomeString'>> and "
             + "<d/CollPropertyString/<ANY;<<e> eq <d/PropertyString>>>>>>>")
         .root().goPath()
@@ -5415,82 +5420,82 @@ public class TestFullResourcePath {
    */
   @Test
   public void searchQueryPhraseAbnfTestcases() throws Exception {
-    //    <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
+    // <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
     testUri.run("ESTwoKeyNav", "$search=\"blue%20green\"");
-    //    <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
+    // <TestCase Name="5.1.7 Search - simple phrase" Rule="queryOptions">
     testUri.run("ESTwoKeyNav", "$search=\"blue%20green%22");
-    //    <TestCase Name="5.1.7 Search - phrase with escaped double-quote" Rule="queryOptions">
-    //    <Input>$search="blue\"green"</Input>
+    // <TestCase Name="5.1.7 Search - phrase with escaped double-quote" Rule="queryOptions">
+    // <Input>$search="blue\"green"</Input>
     testUri.run("ESTwoKeyNav", "$search=\"blue\\\"green\"");
 
-    //    <TestCase Name="5.1.7 Search - phrase with escaped backslash" Rule="queryOptions">
-    //    <Input>$search="blue\\green"</Input>
+    // <TestCase Name="5.1.7 Search - phrase with escaped backslash" Rule="queryOptions">
+    // <Input>$search="blue\\green"</Input>
     testUri.run("ESTwoKeyNav", "$search=\"blue\\\\green\"");
-    //    <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="14">
+    // <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="14">
     testUri.runEx("ESTwoKeyNav", "$search=\"blue\"green\"")
-            .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
-    //    <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="16">
+        .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
+    // <TestCase Name="5.1.7 Search - phrase with unescaped double-quote" Rule="queryOptions" FailAt="16">
     testUri.runEx("ESTwoKeyNav", "$search=\"blue%22green\"")
-            .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
+        .isExceptionMessage(SearchParserException.MessageKeys.TOKENIZER_EXCEPTION);
 
-    //    <TestCase Name="5.1.7 Search - implicit AND" Rule="queryOptions">
-    //    <Input>$search=blue green</Input>
-    //    SearchassertQuery("\"blue%20green\"").resultsIn();
+    // <TestCase Name="5.1.7 Search - implicit AND" Rule="queryOptions">
+    // <Input>$search=blue green</Input>
+    // SearchassertQuery("\"blue%20green\"").resultsIn();
     testUri.run("ESTwoKeyNav", "$search=blue green");
-    //    <TestCase Name="5.1.7 Search - implicit AND, encoced" Rule="queryOptions">
-    //    SearchassertQuery("blue%20green").resultsIn();
+    // <TestCase Name="5.1.7 Search - implicit AND, encoced" Rule="queryOptions">
+    // SearchassertQuery("blue%20green").resultsIn();
     testUri.run("ESTwoKeyNav", "$search=blue%20green");
 
-    //    <TestCase Name="5.1.7 Search - AND" Rule="queryOptions">
-    //    <Input>$search=blue AND green</Input>
+    // <TestCase Name="5.1.7 Search - AND" Rule="queryOptions">
+    // <Input>$search=blue AND green</Input>
     testUri.run("ESTwoKeyNav", "$search=blue AND green");
 
-    //    <TestCase Name="5.1.7 Search - OR" Rule="queryOptions">
-    //    <Input>$search=blue OR green</Input>
+    // <TestCase Name="5.1.7 Search - OR" Rule="queryOptions">
+    // <Input>$search=blue OR green</Input>
     testUri.run("ESTwoKeyNav", "$search=blue OR green");
 
-    //    <TestCase Name="5.1.7 Search - NOT" Rule="queryOptions">
-    //    <Input>$search=blue NOT green</Input>
+    // <TestCase Name="5.1.7 Search - NOT" Rule="queryOptions">
+    // <Input>$search=blue NOT green</Input>
     testUri.run("ESTwoKeyNav", "$search=blue NOT green");
 
-    //    <TestCase Name="5.1.7 Search - only NOT" Rule="queryOptions">
-    //    <Input>$search=NOT blue</Input>
+    // <TestCase Name="5.1.7 Search - only NOT" Rule="queryOptions">
+    // <Input>$search=NOT blue</Input>
     testUri.run("ESTwoKeyNav", "$search=NOT blue");
 
-    //    <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
-    //    <Input>$search=foo AND bar OR foo AND baz OR that AND bar OR that AND baz</Input>
+    // <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
+    // <Input>$search=foo AND bar OR foo AND baz OR that AND bar OR that AND baz</Input>
     testUri.run("ESTwoKeyNav", "$search=foo AND bar OR foo AND baz OR that AND bar OR that AND baz");
 
-    //    <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
-    //    <Input>$search=(foo OR that) AND (bar OR baz)</Input>
+    // <TestCase Name="5.1.7 Search - multiple" Rule="queryOptions">
+    // <Input>$search=(foo OR that) AND (bar OR baz)</Input>
     testUri.run("ESTwoKeyNav", "$search=(foo OR that) AND (bar OR baz)");
 
-    //    <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
-    //    <Input>$search=foo AND (bar OR baz)</Input>
+    // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+    // <Input>$search=foo AND (bar OR baz)</Input>
     testUri.run("ESTwoKeyNav", "$search=foo AND (bar OR baz)");
 
-    //    <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
-    //    <Input>$search=(foo AND bar) OR baz</Input>
+    // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+    // <Input>$search=(foo AND bar) OR baz</Input>
     testUri.run("ESTwoKeyNav", "$search=(foo AND bar) OR baz");
 
-    //    <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
-    //    <Input>$search=(NOT foo) OR baz</Input>
+    // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+    // <Input>$search=(NOT foo) OR baz</Input>
     testUri.run("ESTwoKeyNav", "$search=(NOT foo) OR baz");
 
-    //    <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
-    //    <Input>$search=(NOT foo)</Input>
+    // <TestCase Name="5.1.7 Search - grouping" Rule="queryOptions">
+    // <Input>$search=(NOT foo)</Input>
     testUri.run("ESTwoKeyNav", "$search=(NOT foo)");
 
-    //    <TestCase Name="5.1.7 Search - on entity set" Rule="odataUri">
-    //    <Input>http://serviceRoot/Products?$search=blue</Input>
+    // <TestCase Name="5.1.7 Search - on entity set" Rule="odataUri">
+    // <Input>http://serviceRoot/Products?$search=blue</Input>
     testUri.run("ESTwoKeyNav", "$search=blue");
 
-    //    <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri">
-    //    <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input>
+    // <TestCase Name="5.1.7 Search - on entity container" Rule="odataUri">
+    // <Input>http://serviceRoot/Model.Container/$all?$search=blue</Input>
     testUri.run("$all", "$search=blue");
 
-    //    <TestCase Name="5.1.7 Search - on service" Rule="odataUri">
-    //    <Input>http://serviceRoot/$all?$search=blue</Input>
+    // <TestCase Name="5.1.7 Search - on service" Rule="odataUri">
+    // <Input>http://serviceRoot/$all?$search=blue</Input>
     testUri.run("$all", "$search=blue");
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/73f46510/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
index fbce5c9..fa1e440 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
@@ -90,6 +90,7 @@ public class TestUriValidator implements TestValidator {
       exception = e;
     } catch (UriValidationException e) {
       exception = e;
+      e.printStackTrace();
     }
     return this;
   }