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 2014/12/05 14:42:40 UTC

olingo-odata4 git commit: [OLINGO-507] server support for reading, changing, and deleting media entities

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 297950a8d -> 19a7738a7


[OLINGO-507] server support for reading, changing, and deleting media entities

Change-Id: Ibe16c54cf68e25f0c1e8fb19c212d7c312f5ca85

Signed-off-by: Christian Amend <ch...@apache.org>


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

Branch: refs/heads/master
Commit: 19a7738a718bc52fbe24cb4988cb336923cf06a0
Parents: 297950a
Author: Klaus Straubinger <kl...@sap.com>
Authored: Fri Dec 5 14:28:20 2014 +0100
Committer: Christian Amend <ch...@apache.org>
Committed: Fri Dec 5 14:36:06 2014 +0100

----------------------------------------------------------------------
 .../olingo/fit/tecsvc/client/MediaITCase.java   | 127 +++++++++++++++++++
 .../api/deserializer/DeserializerException.java |  66 ++++++++++
 .../deserializer/FixedFormatDeserializer.java   |   8 ++
 .../server/api/processor/MediaProcessor.java    |  80 ++++++++++++
 .../apache/olingo/server/core/ODataHandler.java |  67 ++++++++--
 .../FixedFormatDeserializerImpl.java            |  20 +++
 .../server-core-exceptions-i18n.properties      |   3 +
 .../deserializer/BatchRequestParserTest.java    |  25 ++--
 .../FixedFormatDeserializerTest.java            |  53 ++++++++
 .../server/core/deserializer/StringUtil.java    |  53 --------
 .../serializer/BatchResponseSerializerTest.java |   6 +-
 .../olingo/server/tecsvc/data/DataProvider.java |  57 ++++++++-
 .../processor/TechnicalEntityProcessor.java     | 109 +++++++++++++---
 .../olingo/server/core/ODataHandlerTest.java    |  44 +++++++
 .../json/ODataJsonSerializerTest.java           |  10 +-
 15 files changed, 626 insertions(+), 102 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
new file mode 100644
index 0000000..8a1ac8f
--- /dev/null
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/MediaITCase.java
@@ -0,0 +1,127 @@
+/* 
+ * 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.fit.tecsvc.client;
+
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.io.InputStream;
+import java.net.URI;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.client.api.CommonODataClient;
+import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataMediaRequest;
+import org.apache.olingo.client.api.communication.request.streamed.ODataMediaEntityUpdateRequest;
+import org.apache.olingo.client.api.communication.response.ODataDeleteResponse;
+import org.apache.olingo.client.api.communication.response.ODataMediaEntityUpdateResponse;
+import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
+import org.apache.olingo.client.api.v4.ODataClient;
+import org.apache.olingo.client.core.ODataClientFactory;
+import org.apache.olingo.commons.api.domain.CommonODataEntity;
+import org.apache.olingo.commons.api.domain.CommonODataProperty;
+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.HttpStatusCode;
+import org.apache.olingo.fit.AbstractBaseTestITCase;
+import org.apache.olingo.fit.tecsvc.TecSvcConst;
+import org.junit.Test;
+
+public final class MediaITCase extends AbstractBaseTestITCase {
+
+  @Test
+  public void read() throws Exception {
+    final CommonODataClient<?> client = getClient();
+    final ODataMediaRequest request = client.getRetrieveRequestFactory().getMediaRequest(
+        client.newURIBuilder(TecSvcConst.BASE_URI)
+            .appendEntitySetSegment("ESMedia").appendKeySegment(1).appendValueSegment().build());
+    assertNotNull(request);
+
+    final ODataRetrieveResponse<InputStream> response = request.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+    assertEquals("image/svg+xml", response.getContentType());
+
+    InputStream media = response.getBody();
+    assertNotNull(media);
+    assertThat(IOUtils.toString(media), startsWith("<?xml"));
+  }
+
+  @Test
+  public void delete() {
+    final CommonODataClient<?> client = getClient();
+    final URI uri = client.newURIBuilder(TecSvcConst.BASE_URI)
+        .appendEntitySetSegment("ESMedia").appendKeySegment(4).appendValueSegment().build();
+    final ODataDeleteRequest request = client.getCUDRequestFactory().getDeleteRequest(uri);
+    assertNotNull(request);
+
+    final ODataDeleteResponse response = request.execute();
+    assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), response.getStatusCode());
+
+    // Check that the media stream is really gone.
+    // This check has to be in the same session in order to access the same data provider.
+    ODataMediaRequest mediaRequest = client.getRetrieveRequestFactory().getMediaRequest(uri);
+    mediaRequest.addCustomHeader(HttpHeader.COOKIE, response.getHeader(HttpHeader.SET_COOKIE).iterator().next());
+    try {
+      mediaRequest.execute();
+    } catch (final ODataClientErrorException e) {
+      assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), e.getStatusLine().getStatusCode());
+    }
+  }
+
+  @Test
+  public void update() throws Exception {
+    final CommonODataClient<?> client = getClient();
+    final URI uri = client.newURIBuilder(TecSvcConst.BASE_URI)
+        .appendEntitySetSegment("ESMedia").appendKeySegment(4).appendValueSegment().build();
+    ODataMediaEntityUpdateRequest<CommonODataEntity> request =
+        client.getCUDRequestFactory().getMediaEntityUpdateRequest(uri,
+            IOUtils.toInputStream("just a test"));
+    request.setContentType(ContentType.TEXT_PLAIN.toContentTypeString());
+    assertNotNull(request);
+
+    final ODataMediaEntityUpdateResponse<CommonODataEntity> response = request.payloadManager().getResponse();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
+    final CommonODataEntity entity = response.getBody();
+    assertNotNull(entity);
+    final CommonODataProperty property = entity.getProperty("PropertyInt16");
+    assertNotNull(property);
+    assertNotNull(property.getPrimitiveValue());
+    assertEquals(4, property.getPrimitiveValue().toValue());
+
+    // Check that the media stream has changed.
+    // This check has to be in the same session in order to access the same data provider.
+    ODataMediaRequest mediaRequest = client.getRetrieveRequestFactory().getMediaRequest(uri);
+    mediaRequest.addCustomHeader(HttpHeader.COOKIE, response.getHeader(HttpHeader.SET_COOKIE).iterator().next());
+    ODataRetrieveResponse<InputStream> mediaResponse = mediaRequest.execute();
+    assertEquals(HttpStatusCode.OK.getStatusCode(), mediaResponse.getStatusCode());
+    assertEquals(ContentType.TEXT_PLAIN.toContentTypeString(), mediaResponse.getContentType());
+    assertEquals("just a test", IOUtils.toString(mediaResponse.getBody()));
+  }
+
+  @Override
+  protected CommonODataClient<?> getClient() {
+    ODataClient odata = ODataClientFactory.getV4();
+    odata.getConfiguration().setDefaultPubFormat(ODataFormat.JSON);
+    return odata;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java
new file mode 100644
index 0000000..2e4e7bc
--- /dev/null
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java
@@ -0,0 +1,66 @@
+/*
+ * 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.deserializer;
+
+import org.apache.olingo.server.api.ODataTranslatedException;
+
+/** Exception thrown by deserializers. */
+public class DeserializerException extends ODataTranslatedException {
+
+  private static final long serialVersionUID = 6341270437497060906L;
+
+  /** Keys for exception texts in the resource bundle. */
+  public static enum MessageKeys implements MessageKey {
+    NOT_IMPLEMENTED,
+    IO_EXCEPTION;
+
+    @Override
+    public String getKey() {
+      return name();
+    }
+  }
+
+  /**
+   * Creates deserializer exception.
+   * @param developmentMessage message text as fallback and for debugging purposes
+   * @param messageKey         one of the {@link MessageKeys} for the exception text in the resource bundle
+   * @param parameters         parameters for the exception text
+   */
+  public DeserializerException(final String developmentMessage,
+      final MessageKey messageKey, final String... parameters) {
+    super(developmentMessage, messageKey, parameters);
+  }
+
+  /**
+   * Creates deserializer exception.
+   * @param developmentMessage message text as fallback and for debugging purposes
+   * @param cause              the cause of this exception
+   * @param messageKey         one of the {@link MessageKeys} for the exception text in the resource bundle
+   * @param parameters         parameters for the exception text
+   */
+  public DeserializerException(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/19a7738a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/FixedFormatDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/FixedFormatDeserializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/FixedFormatDeserializer.java
index 1eba75c..0182d6d 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/FixedFormatDeserializer.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/FixedFormatDeserializer.java
@@ -26,6 +26,14 @@ import org.apache.olingo.server.api.deserializer.batch.BatchOptions;
 import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart;
 
 public interface FixedFormatDeserializer {
+
+  /**
+   * Reads binary data from an InputStream.
+   * @param content the binary data as input stream
+   * @return the binary data
+   */
+  byte[] binary(InputStream content) throws DeserializerException;
+
   public List<BatchRequestPart> parseBatchRequest(InputStream content, String boundary, BatchOptions options)
       throws BatchDeserializerException;
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/MediaProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/MediaProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/MediaProcessor.java
new file mode 100644
index 0000000..fb8ce7d
--- /dev/null
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/MediaProcessor.java
@@ -0,0 +1,80 @@
+/*
+ * 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.processor;
+
+import org.apache.olingo.commons.api.format.ContentType;
+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.serializer.SerializerException;
+import org.apache.olingo.server.api.uri.UriInfo;
+
+/**
+ * Processor interface for handling Entity Media.
+ */
+public interface MediaProcessor extends Processor {
+
+  /**
+   * Reads entity media data from persistence and puts content and status into the response.
+   * @param request  OData request object containing raw HTTP information
+   * @param response OData response object for collecting response data
+   * @param uriInfo  information of a parsed OData URI
+   * @param format   requested content type after content negotiation
+   * @throws ODataApplicationException if the service implementation encounters a failure
+   * @throws SerializerException       if serialization failed
+   */
+  void readMedia(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format)
+      throws ODataApplicationException, SerializerException;
+
+  /**
+   * Creates entity media data in the persistence and puts content, status, and Location into the response.
+   * @param request  OData request object containing raw HTTP information
+   * @param response OData response object for collecting response data
+   * @param uriInfo  information of a parsed OData URI
+   * @param format   requested content type after content negotiation
+   * @throws ODataApplicationException if the service implementation encounters a failure
+   * @throws DeserializerException     if deserialization failed
+   * @throws SerializerException       if serialization failed
+   */
+  void createMedia(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format)
+      throws ODataApplicationException, DeserializerException, SerializerException;
+
+  /**
+   * Updates entity media data in the persistence and puts content and status into the response.
+   * @param request  OData request object containing raw HTTP information
+   * @param response OData response object for collecting response data
+   * @param uriInfo  information of a parsed OData URI
+   * @param format   requested content type after content negotiation
+   * @throws ODataApplicationException if the service implementation encounters a failure
+   * @throws DeserializerException     if deserialization failed
+   * @throws SerializerException       if serialization failed
+   */
+  void updateMedia(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType format)
+      throws ODataApplicationException, DeserializerException, SerializerException;
+
+  /**
+   * Deletes entity media data from persistence and puts the status into the response.
+   * @param request  OData request object containing raw HTTP information
+   * @param response OData response object for collecting response data
+   * @param uriInfo  information of a parsed OData URI
+   * @throws ODataApplicationException if the service implementation encounters a failure
+   */
+  void deleteMedia(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException;
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/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 63d0dce..51b328b 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,6 +21,7 @@ package org.apache.olingo.server.core;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.olingo.commons.api.edm.EdmEntityType;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@@ -36,6 +37,7 @@ 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.batch.exception.BatchException;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
 import org.apache.olingo.server.api.processor.BatchProcessor;
 import org.apache.olingo.server.api.processor.ComplexCollectionProcessor;
 import org.apache.olingo.server.api.processor.ComplexProcessor;
@@ -44,6 +46,7 @@ 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.ExceptionProcessor;
+import org.apache.olingo.server.api.processor.MediaProcessor;
 import org.apache.olingo.server.api.processor.MetadataProcessor;
 import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor;
 import org.apache.olingo.server.api.processor.PrimitiveProcessor;
@@ -107,6 +110,9 @@ public class ODataHandler {
     } catch (SerializerException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
       handleException(request, response, serverError);
+    } catch (DeserializerException e) {
+      ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
+      handleException(request, response, serverError);
     } catch (ODataHandlerException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
       handleException(request, response, serverError);
@@ -122,7 +128,7 @@ public class ODataHandler {
 
   private void processInternal(final ODataRequest request, final ODataResponse response)
       throws ODataHandlerException, UriParserException, UriValidationException, ContentNegotiatorException,
-      ODataApplicationException, SerializerException, BatchException {
+      ODataApplicationException, SerializerException, DeserializerException, BatchException {
     validateODataVersion(request, response);
 
     uriInfo = new Parser().parseUri(request.getRawODataPath(), request.getRawQueryPath(), null,
@@ -195,7 +201,8 @@ public class ODataHandler {
   }
 
   private void handleResourceDispatching(final ODataRequest request, final ODataResponse response)
-      throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, SerializerException {
+      throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException,
+      SerializerException, DeserializerException {
     final HttpMethod method = request.getMethod();
     final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1;
     final UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex);
@@ -210,9 +217,19 @@ public class ODataHandler {
 
           selectProcessor(EntityCollectionProcessor.class)
               .readEntityCollection(request, response, uriInfo, requestedContentType);
+        } else if (method.equals(HttpMethod.POST)) {
+          if (isMedia(lastPathSegment)) {
+            final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+                request, customContentTypeSupport, RepresentationType.ENTITY);
+            selectProcessor(MediaProcessor.class)
+                .createMedia(request, response, uriInfo, requestedContentType);
+          } else {
+            throw new ODataHandlerException("not implemented",
+                ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+          }
         } else {
-          throw new ODataHandlerException("not implemented",
-              ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+          throw new ODataHandlerException("HTTP method not allowed.",
+              ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString());
         }
       } else {
         if (method.equals(HttpMethod.GET)) {
@@ -221,6 +238,14 @@ public class ODataHandler {
 
           selectProcessor(EntityProcessor.class)
               .readEntity(request, response, uriInfo, requestedContentType);
+        } else if (method.equals(HttpMethod.DELETE)) {
+          if (isMedia(lastPathSegment)) {
+            selectProcessor(MediaProcessor.class)
+                .deleteMedia(request, response, uriInfo);
+          } else {
+            throw new ODataHandlerException("not implemented",
+                ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+          }
         } else {
           throw new ODataHandlerException("not implemented",
               ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
@@ -286,9 +311,9 @@ public class ODataHandler {
       break;
 
     case value:
-      if (method.equals(HttpMethod.GET)) {
-        final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1);
-        if (resource instanceof UriResourceProperty) {
+      final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1);
+      if (resource instanceof UriResourceProperty) {
+        if (method.equals(HttpMethod.GET)) {
           final RepresentationType representationType =
               (EdmPrimitiveType) ((UriResourceProperty) resource).getType() ==
               EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ?
@@ -299,12 +324,27 @@ public class ODataHandler {
           selectProcessor(PrimitiveProcessor.class)
               .readPrimitiveAsValue(request, response, uriInfo, requestedContentType);
         } else {
-          throw new ODataHandlerException("Media Entity is not implemented.",
+          throw new ODataHandlerException("not implemented",
               ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
         }
       } else {
-        throw new ODataHandlerException("not implemented",
-            ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+        if (method.equals(HttpMethod.GET)) {
+          final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+              request, customContentTypeSupport, RepresentationType.MEDIA);
+          selectProcessor(MediaProcessor.class)
+              .readMedia(request, response, uriInfo, requestedContentType);
+        } else if (method.equals(HttpMethod.PUT)) {
+          final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+              request, customContentTypeSupport, RepresentationType.ENTITY);
+          selectProcessor(MediaProcessor.class)
+              .updateMedia(request, response, uriInfo, requestedContentType);
+        } else if (method.equals(HttpMethod.DELETE)) {
+          selectProcessor(MediaProcessor.class)
+              .deleteMedia(request, response, uriInfo);
+        } else {
+          throw new ODataHandlerException("not implemented",
+              ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+        }
       }
       break;
 
@@ -327,6 +367,13 @@ 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 {
     for (final Processor processor : processors) {
       if (cls.isAssignableFrom(processor.getClass())) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerImpl.java
index 65feae7..a34aa95 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerImpl.java
@@ -18,10 +18,13 @@
  */
 package org.apache.olingo.server.core.deserializer;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
 import org.apache.olingo.server.api.batch.exception.BatchDeserializerException;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
 import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer;
 import org.apache.olingo.server.api.deserializer.batch.BatchOptions;
 import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart;
@@ -29,6 +32,23 @@ import org.apache.olingo.server.core.deserializer.batch.BatchParser;
 
 public class FixedFormatDeserializerImpl implements FixedFormatDeserializer {
 
+  @Override
+  public byte[] binary(InputStream content) throws DeserializerException {
+    ByteArrayOutputStream result = new ByteArrayOutputStream();
+    byte[] buffer = new byte[128];
+    int count = -1;
+    try {
+      while ((count = content.read(buffer)) > -1) {
+        result.write(buffer, 0, count);
+      }
+      result.flush();
+    } catch (final IOException e) {
+      throw new DeserializerException("An I/O exception occurred.", e,
+          DeserializerException.MessageKeys.IO_EXCEPTION);
+    }
+    return result.toByteArray();
+  }
+
   // TODO: Deserializer
   @Override
   public List<BatchRequestPart> parseBatchRequest(InputStream content, String boundary, BatchOptions options)

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/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 ebba201..e28c3e5 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
@@ -96,6 +96,9 @@ SerializerException.MISSING_PROPERTY=The non-nullable property '%1$s' is missing
 SerializerException.WRONG_PROPERTY_VALUE=The value '%2$s' is not valid for property '%1$s'.
 SerializerException.WRONG_PRIMITIVE_VALUE=The value '%2$s' is not valid for the primitive type '%1$s' and the given facets.
 
+DeserializerException.NOT_IMPLEMENTED=The requested deserialization method has not been implemented yet.
+DeserializerException.IO_EXCEPTION=An I/O exception occurred.
+
 BatchDeserializerException.INVALID_BOUNDARY=Invalid boundary at line '%1$s'.
 BatchDeserializerException.INVALID_CHANGESET_METHOD=Invalid method: a ChangeSet cannot contain retrieve requests at line '%1$s'.
 BatchDeserializerException.INVALID_CONTENT=Retrieve requests must not contain any body content '%1$s'.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/BatchRequestParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/BatchRequestParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/BatchRequestParserTest.java
index 7a63874..219167d 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/BatchRequestParserTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/BatchRequestParserTest.java
@@ -28,6 +28,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpMethod;
 import org.apache.olingo.server.api.ODataRequest;
@@ -101,7 +102,7 @@ public class BatchRequestParserTest {
   @Test
   public void testImageInContent() throws Exception {
     final InputStream contentInputStream = readFile("/batchWithContent.batch");
-    final String content = StringUtil.toString(contentInputStream);
+    final String content = IOUtils.toString(contentInputStream);
     final String batch = ""
         + "--batch_8194-cf13-1f56" + CRLF
         + MIME_HEADERS
@@ -148,7 +149,7 @@ public class BatchRequestParserTest {
           assertEquals("application/octet-stream", request.getHeader(HttpHeader.CONTENT_TYPE));
 
           final InputStream body = request.getBody();
-          assertEquals(content, StringUtil.toString(body));
+          assertEquals(content, IOUtils.toString(body));
         }
       }
     }
@@ -199,7 +200,7 @@ public class BatchRequestParserTest {
     final BatchParser parser = new BatchParser();
     final BatchOptions batchOptions = BatchOptions.with().isStrict(true).rawBaseUri(SERVICE_ROOT).build();
     final List<BatchRequestPart> batchRequestParts =
-        parser.parseBatchRequest(StringUtil.toInputStream(batch), boundary, batchOptions);
+        parser.parseBatchRequest(IOUtils.toInputStream(batch), boundary, batchOptions);
 
     assertNotNull(batchRequestParts);
     assertFalse(batchRequestParts.isEmpty());
@@ -754,7 +755,7 @@ public class BatchRequestParserTest {
         assertEquals(1, multipart.getRequests().size());
 
         final ODataRequest request = multipart.getRequests().get(0);
-        assertEquals("{\"EmployeeName\":\"Peter Fall\"}", StringUtil.toString(request.getBody()));
+        assertEquals("{\"EmployeeName\":\"Peter Fall\"}", IOUtils.toString(request.getBody()));
       }
     }
   }
@@ -786,7 +787,7 @@ public class BatchRequestParserTest {
         assertEquals(1, multipart.getRequests().size());
 
         final ODataRequest request = multipart.getRequests().get(0);
-        assertEquals("{\"Employee", StringUtil.toString(request.getBody()));
+        assertEquals("{\"Employee", IOUtils.toString(request.getBody()));
       }
     }
   }
@@ -841,7 +842,7 @@ public class BatchRequestParserTest {
 
     final ODataRequest changeRequest = part.getRequests().get(0);
     assertEquals("{\"EmployeeName\":\"Frederic Fall MODIFIED\"}",
-        StringUtil.toString(changeRequest.getBody()));
+        IOUtils.toString(changeRequest.getBody()));
     assertEquals("application/json;odata=verbose", changeRequest.getHeader(HttpHeader.CONTENT_TYPE));
     assertEquals(HttpMethod.PUT, changeRequest.getMethod());
   }
@@ -1016,9 +1017,9 @@ public class BatchRequestParserTest {
     assertEquals(2, changeSetPart.getRequests().size());
     assertEquals("/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA"
         + CRLF,
-        StringUtil.toString(changeSetPart.getRequests().get(0).getBody()));
+        IOUtils.toString(changeSetPart.getRequests().get(0).getBody()));
     assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
-        StringUtil.toString(changeSetPart.getRequests().get(1).getBody()));
+        IOUtils.toString(changeSetPart.getRequests().get(1).getBody()));
   }
 
   @Test
@@ -1126,9 +1127,9 @@ public class BatchRequestParserTest {
     assertEquals(2, changeSetPart.getRequests().size());
     assertEquals("/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA"
         + CRLF,
-        StringUtil.toString(changeSetPart.getRequests().get(0).getBody()));
+        IOUtils.toString(changeSetPart.getRequests().get(0).getBody()));
     assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
-        StringUtil.toString(changeSetPart.getRequests().get(1).getBody()));
+        IOUtils.toString(changeSetPart.getRequests().get(1).getBody()));
   }
 
   @Test
@@ -1247,7 +1248,7 @@ public class BatchRequestParserTest {
   }
 
   private List<BatchRequestPart> parse(final String batch, final boolean isStrict) throws Exception {
-    return parse(StringUtil.toInputStream(batch), isStrict);
+    return parse(IOUtils.toInputStream(batch), isStrict);
   }
 
   private void parseInvalidBatchBody(final String batch, final MessageKeys key, final boolean isStrict)
@@ -1255,7 +1256,7 @@ public class BatchRequestParserTest {
     final BatchParser parser = new BatchParser();
     final BatchOptions options = BatchOptions.with().isStrict(isStrict).rawBaseUri(SERVICE_ROOT).build();
     try {
-      parser.parseBatchRequest(StringUtil.toInputStream(batch), BOUNDARY, options);
+      parser.parseBatchRequest(IOUtils.toInputStream(batch), BOUNDARY, options);
       fail("No exception thrown. Expect: " + key.toString());
     } catch (BatchDeserializerException e) {
       assertMessageKey(e, key);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerTest.java
new file mode 100644
index 0000000..27f6a5d
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/FixedFormatDeserializerTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.deserializer;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.junit.Test;
+
+public class FixedFormatDeserializerTest {
+
+  private final FixedFormatDeserializer deserializer;
+
+  public FixedFormatDeserializerTest() throws SerializerException {
+    deserializer = OData.newInstance().createFixedFormatDeserializer();
+  }
+
+  @Test
+  public void binary() throws Exception {
+    assertArrayEquals(new byte [] { 0x41, 0x42, 0x43 },
+        deserializer.binary(IOUtils.toInputStream("ABC")));
+  }
+
+  @Test
+  public void binaryLong() throws Exception {
+    assertEquals(4 * 3 * 26,
+        deserializer.binary(IOUtils.toInputStream(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            + "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            + "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            + "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ")).length);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/StringUtil.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/StringUtil.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/StringUtil.java
deleted file mode 100644
index f9ec664..0000000
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/StringUtil.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.deserializer;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-
-import org.apache.olingo.commons.api.ODataRuntimeException;
-import org.apache.olingo.server.core.deserializer.batch.BufferedReaderIncludingLineEndings;
-
-public class StringUtil {
-  
-  
-  public static String toString(final InputStream in) throws Exception {
-    final StringBuilder builder = new StringBuilder();
-    final BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(in));
-    String currentLine;
-    
-    while((currentLine = reader.readLine()) != null) {
-      builder.append(currentLine);
-    }
-    
-    reader.close();
-    
-    return builder.toString();
-  }
-
-  public static InputStream toInputStream(final String string) {
-    try {
-      return new ByteArrayInputStream(string.getBytes("UTF-8"));
-    } catch (UnsupportedEncodingException e) {
-      throw new ODataRuntimeException("Charset UTF-8 not found");
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
index d889cac..ce88c92 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
@@ -28,11 +28,11 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.server.api.ODataResponse;
 import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart;
-import org.apache.olingo.server.core.deserializer.StringUtil;
 import org.apache.olingo.server.core.deserializer.batch.BatchParserCommon;
 import org.apache.olingo.server.core.deserializer.batch.BufferedReaderIncludingLineEndings;
 import org.apache.olingo.server.core.serializer.BatchResponseSerializer;
@@ -48,7 +48,7 @@ public class BatchResponseSerializerTest {
     ODataResponse response = new ODataResponse();
     response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     response.setHeader(HttpHeader.CONTENT_TYPE, "application/json");
-    response.setContent(StringUtil.toInputStream("Walter Winter" + CRLF));
+    response.setContent(IOUtils.toInputStream("Walter Winter" + CRLF));
 
     List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
     responses.add(response);
@@ -104,7 +104,7 @@ public class BatchResponseSerializerTest {
     ODataResponse response = new ODataResponse();
     response.setStatusCode(HttpStatusCode.OK.getStatusCode());
     response.setHeader(HttpHeader.CONTENT_TYPE, "application/json");
-    response.setContent(StringUtil.toInputStream("Walter Winter"));
+    response.setContent(IOUtils.toInputStream("Walter Winter"));
 
     List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
     responses.add(response);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
index 6010e95..34e36a2 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
@@ -18,11 +18,13 @@
  */
 package org.apache.olingo.server.tecsvc.data;
 
+import java.nio.charset.Charset;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -52,6 +54,7 @@ import org.apache.olingo.server.api.uri.UriParameter;
 public class DataProvider {
 
   private static final UUID GUID = UUID.fromString("01234567-89ab-cdef-0123-456789abcdef");
+  private static final String MEDIA_PROPERTY_NAME = "$value";
 
   private Map<String, EntitySet> data;
 
@@ -107,6 +110,43 @@ public class DataProvider {
     }
   }
 
+  public void delete(final EdmEntitySet edmEntitySet, final Entity entity) throws DataProviderException {
+    deleteLinksTo(entity);
+    data.get(edmEntitySet.getName()).getEntities().remove(entity);
+  }
+
+  public void deleteLinksTo(final Entity to) throws DataProviderException {
+    for (final String entitySet : data.keySet()) {
+      for (final Entity entity : data.get(entitySet).getEntities()) {
+        for (Iterator<Link> linkIterator = entity.getNavigationLinks().iterator(); linkIterator.hasNext();) {
+          final Link link = linkIterator.next();
+          if (to.equals(link.getInlineEntity())) {
+            linkIterator.remove();
+          } else if (link.getInlineEntitySet() != null) {
+            for (Iterator<Entity> iterator = link.getInlineEntitySet().getEntities().iterator(); iterator.hasNext();) {
+              if (to.equals(iterator.next())) {
+                iterator.remove();
+              }
+            }
+            if (link.getInlineEntitySet().getEntities().isEmpty()) {
+              linkIterator.remove();
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public byte[] readMedia(final Entity entity) {
+    return (byte[]) entity.getProperty(MEDIA_PROPERTY_NAME).asPrimitive();
+  }
+
+  public void setMedia(Entity entity, byte[] media, String type) {
+    entity.getProperties().remove(entity.getProperty(MEDIA_PROPERTY_NAME));
+    entity.addProperty(createPrimitive(MEDIA_PROPERTY_NAME, media));
+    entity.setMediaContentType(type);
+  }
+
   public static class DataProviderException extends ODataApplicationException {
     private static final long serialVersionUID = 5098059649321796156L;
 
@@ -455,27 +495,36 @@ public class DataProvider {
 
     Entity entity = new EntityImpl();
     entity.addProperty(createPrimitive("PropertyInt16", 1));
-    entity.setMediaContentType("image/png");
+    setMedia(entity, createImage("darkturquoise"), "image/svg+xml");
     entitySet.getEntities().add(entity);
 
     entity = new EntityImpl();
     entity.addProperty(createPrimitive("PropertyInt16", 2));
-    entity.setMediaContentType("image/bmp");
+    setMedia(entity, createImage("royalblue"), "image/svg+xml");
     entitySet.getEntities().add(entity);
 
     entity = new EntityImpl();
     entity.addProperty(createPrimitive("PropertyInt16", 3));
-    entity.setMediaContentType("image/jpeg");
+    setMedia(entity, createImage("crimson"), "image/svg+xml");
     entitySet.getEntities().add(entity);
 
     entity = new EntityImpl();
     entity.addProperty(createPrimitive("PropertyInt16", 4));
-    entity.setMediaContentType("foo");
+    setMedia(entity, createImage("black"), "image/svg+xml");
     entitySet.getEntities().add(entity);
 
     return entitySet;
   }
 
+  private static byte[] createImage(final String color) {
+    return ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+          + "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 100 100\">\n"
+          + "  <g stroke=\"darkmagenta\" stroke-width=\"16\" fill=\"" + color + "\">\n"
+          + "    <circle cx=\"50\" cy=\"50\" r=\"42\"/>\n"
+          + "  </g>\n"
+          + "</svg>\n").getBytes(Charset.forName("UTF-8"));
+  }
+
   private void linkESTwoPrim() {
     EntitySet entitySet = data.get("ESTwoPrim");
     final List<Entity> targetEntities = data.get("ESAllPrim").getEntities();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index 971c50a..700e3cf 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -33,9 +33,11 @@ import org.apache.olingo.commons.api.http.HttpStatusCode;
 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.CountEntityCollectionProcessor;
 import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
 import org.apache.olingo.server.api.processor.EntityProcessor;
+import org.apache.olingo.server.api.processor.MediaProcessor;
 import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
 import org.apache.olingo.server.api.serializer.ODataSerializer;
 import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
@@ -43,6 +45,7 @@ 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.UriResourceEntitySet;
+import org.apache.olingo.server.api.uri.UriResourceKind;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.tecsvc.data.DataProvider;
@@ -51,7 +54,7 @@ import org.apache.olingo.server.tecsvc.data.DataProvider;
  * Technical Processor for entity-related functionality.
  */
 public class TechnicalEntityProcessor extends TechnicalProcessor
-    implements EntityCollectionProcessor, CountEntityCollectionProcessor, EntityProcessor {
+    implements EntityCollectionProcessor, CountEntityCollectionProcessor, EntityProcessor, MediaProcessor {
 
   public TechnicalEntityProcessor(final DataProvider dataProvider) {
     super(dataProvider);
@@ -61,10 +64,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
   public void readEntityCollection(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
       final ContentType requestedContentType) throws ODataApplicationException, SerializerException {
     validateOptions(uriInfo.asUriInfoResource());
-    if (uriInfo.asUriInfoResource().getUriResourceParts().size() > 1) {
-      throw new ODataApplicationException("Invalid resource type.",
-          HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
-    }
+    blockNavigation(uriInfo);
 
     final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource());
     final EntitySet entitySet = readEntitySetInternal(edmEntitySet,
@@ -92,14 +92,12 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
   public void countEntityCollection(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
       final ContentType requestedContentType) throws ODataApplicationException, SerializerException {
     validateOptions(uriInfo.asUriInfoResource());
+    blockNavigation(uriInfo);
+
     final List<UriResource> resourceParts = uriInfo.asUriInfoResource().getUriResourceParts();
-    final int pos = resourceParts.size() - 2;
-    if (pos > 0) {
-      throw new ODataApplicationException("Invalid resource type.",
-          HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
-    }
     final EntitySet entitySet =
-        readEntitySetInternal(((UriResourceEntitySet) resourceParts.get(pos)).getEntitySet(), true);
+        readEntitySetInternal(((UriResourceEntitySet) resourceParts.get(resourceParts.size() - 2)).getEntitySet(),
+            true);
     if (entitySet == null) {
       throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
     } else {
@@ -113,10 +111,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
   public void readEntity(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
       final ContentType requestedContentType) throws ODataApplicationException, SerializerException {
     validateOptions(uriInfo.asUriInfoResource());
-    if (uriInfo.asUriInfoResource().getUriResourceParts().size() > 1) {
-      throw new ODataApplicationException("Invalid resource type.",
-          HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
-    }
+    blockNavigation(uriInfo);
 
     final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource());
     final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0);
@@ -140,6 +135,90 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
     }
   }
 
+  @Override
+  public void readMedia(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
+      final ContentType requestedContentType) throws ODataApplicationException, SerializerException {
+    blockNavigation(uriInfo);
+    final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0);
+    final Entity entity = dataProvider.read(resourceEntitySet.getEntitySet(), resourceEntitySet.getKeyPredicates());
+    if (entity == null) {
+      throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
+    } else {
+      response.setContent(odata.createFixedFormatSerializer()
+          .binary(dataProvider.readMedia(entity)));
+      response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+      response.setHeader(HttpHeader.CONTENT_TYPE, entity.getMediaContentType());
+    }
+  }
+
+  @Override
+  public void createMedia(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
+      final ContentType requestedContentType)
+      throws ODataApplicationException, DeserializerException, SerializerException {
+    blockNavigation(uriInfo);
+    final String contentType = request.getHeader(HttpHeader.CONTENT_TYPE);
+    if (contentType == null) {
+      throw new ODataApplicationException("The Content-Type HTTP header is missing.",
+          HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
+    }
+    throw new ODataApplicationException("Not yet supported.",
+        HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+  }
+
+  @Override
+  public void updateMedia(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
+      final ContentType requestedContentType)
+      throws ODataApplicationException, DeserializerException, SerializerException {
+    blockNavigation(uriInfo);
+    final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0);
+    final EdmEntitySet edmEntitySet = resourceEntitySet.getEntitySet();
+    final Entity entity = dataProvider.read(edmEntitySet, resourceEntitySet.getKeyPredicates());
+    if (entity == null) {
+      throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
+    }
+    final String contentType = request.getHeader(HttpHeader.CONTENT_TYPE);
+    if (contentType == null) {
+      throw new ODataApplicationException("The Content-Type HTTP header is missing.",
+          HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
+    }
+    dataProvider.setMedia(entity, odata.createFixedFormatDeserializer().binary(request.getBody()), contentType);
+
+    final ODataFormat format = ODataFormat.fromContentType(requestedContentType);
+    ODataSerializer serializer = odata.createSerializer(format);
+    response.setContent(serializer.entity(edmEntitySet.getEntityType(), entity,
+        EntitySerializerOptions.with()
+            .contextURL(format == ODataFormat.JSON_NO_METADATA ? null :
+                getContextUrl(serializer, edmEntitySet, true, null, null))
+            .build()));
+    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+    response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
+  }
+
+  @Override
+  public void deleteMedia(final ODataRequest request, ODataResponse response, final UriInfo uriInfo)
+      throws ODataApplicationException {
+    blockNavigation(uriInfo);
+    final UriResourceEntitySet resourceEntitySet = (UriResourceEntitySet) uriInfo.getUriResourceParts().get(0);
+    final Entity entity = dataProvider.read(resourceEntitySet.getEntitySet(), resourceEntitySet.getKeyPredicates());
+    if (entity == null) {
+      throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
+    } else {
+      dataProvider.delete(resourceEntitySet.getEntitySet(), entity);
+      response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
+    }
+  }
+
+  private void blockNavigation(final UriInfo uriInfo) throws ODataApplicationException {
+    final List<UriResource> parts = uriInfo.asUriInfoResource().getUriResourceParts();
+    if (parts.size() > 2
+        || parts.size() == 2
+            && parts.get(1).getKind() != UriResourceKind.count
+            && parts.get(1).getKind() != UriResourceKind.value) {
+      throw new ODataApplicationException("Invalid resource type.",
+          HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+    }
+  }
+
   private EntitySet readEntitySetInternal(final EdmEntitySet edmEntitySet, final boolean withCount)
       throws DataProvider.DataProviderException {
     EntitySet entitySet = dataProvider.readAll(edmEntitySet);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/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 b4e04fb..cad616d 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
@@ -53,6 +53,7 @@ import org.apache.olingo.server.api.processor.ComplexProcessor;
 import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor;
 import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
 import org.apache.olingo.server.api.processor.EntityProcessor;
+import org.apache.olingo.server.api.processor.MediaProcessor;
 import org.apache.olingo.server.api.processor.MetadataProcessor;
 import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor;
 import org.apache.olingo.server.api.processor.PrimitiveProcessor;
@@ -257,6 +258,49 @@ public class ODataHandlerTest {
   }
 
   @Test
+  public void dispatchMedia() throws Exception {
+    final MediaProcessor processor = mock(MediaProcessor.class);
+    dispatch(HttpMethod.GET, "ESMedia(1)/$value", processor);
+
+    verify(processor).readMedia(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class));
+  }
+
+  @Test
+  public void dispatchMediaCreate() throws Exception {
+    final MediaProcessor processor = mock(MediaProcessor.class);
+    dispatch(HttpMethod.POST, "ESMedia", processor);
+
+    verify(processor).createMedia(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class));
+  }
+
+  @Test
+  public void dispatchMediaPut() throws Exception {
+    final MediaProcessor processor = mock(MediaProcessor.class);
+    dispatch(HttpMethod.PUT, "ESMedia(1)/$value", processor);
+
+    verify(processor).updateMedia(
+        any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class));
+  }
+
+  @Test
+  public void dispatchMediaDelete() throws Exception {
+    final MediaProcessor processor = mock(MediaProcessor.class);
+    dispatch(HttpMethod.DELETE, "ESMedia(1)/$value", processor);
+
+    verify(processor).deleteMedia(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class));
+  }
+
+  @Test
+  public void dispatchMediaDeleteIndirect() throws Exception {
+    final MediaProcessor processor = mock(MediaProcessor.class);
+    dispatch(HttpMethod.DELETE, "ESMedia(1)", processor);
+
+    verify(processor).deleteMedia(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class));
+  }
+
+  @Test
   public void dispatchPrimitiveProperty() throws Exception {
     final PrimitiveProcessor processor = mock(PrimitiveProcessor.class);
     dispatch(HttpMethod.GET, "ESAllPrim(0)/PropertyInt16", processor);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/19a7738a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
index f0801d8..7ca442a 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
@@ -325,7 +325,7 @@ public class ODataJsonSerializerTest {
             .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
             .build()));
     final String expectedResult = "{\"@odata.context\":\"$metadata#ESMedia/$entity\","
-        + "\"@odata.mediaEtag\":\"theMediaETag\",\"@odata.mediaContentType\":\"image/png\","
+        + "\"@odata.mediaEtag\":\"theMediaETag\",\"@odata.mediaContentType\":\"image/svg+xml\","
         + "\"PropertyInt16\":1}";
     Assert.assertEquals(expectedResult, resultString);
   }
@@ -338,10 +338,10 @@ public class ODataJsonSerializerTest {
         EntityCollectionSerializerOptions.with()
             .contextURL(ContextURL.with().entitySet(edmEntitySet).build()).build()));
     final String expectedResult = "{\"@odata.context\":\"$metadata#ESMedia\",\"value\":["
-        + "{\"@odata.mediaContentType\":\"image/png\",\"PropertyInt16\":1},"
-        + "{\"@odata.mediaContentType\":\"image/bmp\",\"PropertyInt16\":2},"
-        + "{\"@odata.mediaContentType\":\"image/jpeg\",\"PropertyInt16\":3},"
-        + "{\"@odata.mediaContentType\":\"foo\",\"PropertyInt16\":4}]}";
+        + "{\"@odata.mediaContentType\":\"image/svg+xml\",\"PropertyInt16\":1},"
+        + "{\"@odata.mediaContentType\":\"image/svg+xml\",\"PropertyInt16\":2},"
+        + "{\"@odata.mediaContentType\":\"image/svg+xml\",\"PropertyInt16\":3},"
+        + "{\"@odata.mediaContentType\":\"image/svg+xml\",\"PropertyInt16\":4}]}";
     Assert.assertEquals(expectedResult, resultString);
   }