You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/10/23 10:24:07 UTC

git commit: [OLINGO-444] improved server content negotiation

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 5a9995570 -> 9e5b284a5


[OLINGO-444] improved server content negotiation

Change-Id: I30f53dc05eec9ff669a029f1382a37dd220abe94

Signed-off-by: Michael Bolz <mi...@sap.com>


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

Branch: refs/heads/master
Commit: 9e5b284a570ea20c3bc0de446cb21da1e8f73e4d
Parents: 5a99955
Author: Klaus Straubinger <kl...@sap.com>
Authored: Wed Oct 22 15:23:28 2014 +0200
Committer: Michael Bolz <mi...@sap.com>
Committed: Thu Oct 23 10:22:19 2014 +0200

----------------------------------------------------------------------
 .../olingo/fit/tecsvc/client/BasicITCase.java   |   6 +-
 .../olingo/commons/api/format/ContentType.java  | 164 +++++++------------
 .../olingo/server/api/ODataHttpHandler.java     |   8 +
 .../olingo/server/api/ServiceMetadata.java      |   2 +-
 .../api/processor/CustomContentTypeSupport.java |  45 -----
 .../server/api/processor/DefaultProcessor.java  |   8 +-
 .../api/processor/ExceptionProcessor.java       |   4 +-
 .../olingo/server/api/processor/Processor.java  |   7 +-
 .../serializer/CustomContentTypeSupport.java    |  46 ++++++
 .../api/serializer/RepresentationType.java      |  44 +++++
 .../olingo/server/core/ContentNegotiator.java   |  64 ++++----
 .../server/core/ContentNegotiatorException.java |   1 +
 .../server/core/DefaultRedirectProcessor.java   |   2 +-
 .../apache/olingo/server/core/ODataHandler.java | 161 +++++++++---------
 .../server/core/ODataHttpHandlerImpl.java       |   8 +-
 .../core/uri/parser/UriParseTreeVisitor.java    |   2 +-
 .../server-core-exceptions-i18n.properties      |   1 +
 .../server/core/ContentNegotiatorTest.java      | 137 +++++-----------
 .../tecsvc/processor/TechnicalProcessor.java    |  32 ++--
 19 files changed, 358 insertions(+), 384 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
index 4d55013..adb1d91 100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java
@@ -61,7 +61,6 @@ import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
 import org.apache.olingo.fit.AbstractBaseTestITCase;
 import org.apache.olingo.fit.tecsvc.TecSvcConst;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class BasicITCase extends AbstractBaseTestITCase {
@@ -213,7 +212,7 @@ public class BasicITCase extends AbstractBaseTestITCase {
     assertNotNull(property.getPrimitiveValue());
     assertEquals("Test String1", property.getPrimitiveValue().toValue());
   }
-  
+
   @Test
   public void readSimplePropertyContextURL() throws Exception {
     ODataPropertyRequest<ODataProperty> request = getClient().getRetrieveRequestFactory()
@@ -228,7 +227,7 @@ public class BasicITCase extends AbstractBaseTestITCase {
         "\"value\":\"Test String1\"}";
     assertEquals(expectedResult, IOUtils.toString(response.getRawResponse(), "UTF-8"));    
   }  
-  
+
   @Test
   public void readComplexProperty() throws Exception {
     ODataPropertyRequest<ODataProperty> request = getClient().getRetrieveRequestFactory()
@@ -290,7 +289,6 @@ public class BasicITCase extends AbstractBaseTestITCase {
     assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), response.getStatusCode());
   }   
 
-  @Ignore("Content Negotiation!")
   @Test
   public void readPropertyValue() throws Exception {
     final ODataValueRequest request = getClient().getRetrieveRequestFactory()

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java
index 60a1ce6..e2a05d1 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/ContentType.java
@@ -46,42 +46,38 @@ import java.util.TreeMap;
  */
 public final class ContentType {
 
-  public static final ContentType APPLICATION_XML = create("application/xml");
-
-  public static final ContentType APPLICATION_ATOM_XML = create("application/atom+xml");
+  private static final String APPLICATION = "application";
+  private static final String TEXT = "text";
+  private static final String MULTIPART = "multipart";
 
+  public static final ContentType APPLICATION_XML = new ContentType(APPLICATION, "xml", null);
+  public static final ContentType APPLICATION_ATOM_XML = new ContentType(APPLICATION, "atom+xml", null);
   public static final ContentType APPLICATION_ATOM_XML_ENTRY = create(APPLICATION_ATOM_XML, "type=entry");
-
   public static final ContentType APPLICATION_ATOM_XML_FEED = create(APPLICATION_ATOM_XML, "type=feed");
+  public static final ContentType APPLICATION_ATOM_SVC = new ContentType(APPLICATION, "atomsvc+xml", null);
 
-  public static final ContentType APPLICATION_ATOM_SVC = create("application/atomsvc+xml");
-
-  public static final ContentType APPLICATION_JSON = create("application/json");
-
-  public static final ContentType APPLICATION_OCTET_STREAM = create("application/octet-stream");
-
-  public static final ContentType TEXT_PLAIN = create("text/plain");
-
-  public static final ContentType MULTIPART_MIXED = create("multipart/mixed");
+  public static final ContentType APPLICATION_JSON = new ContentType(APPLICATION, "json", null);
 
-  public static final ContentType APPLICATION_XHTML_XML = create("application/xhtml+xml");
+  public static final ContentType APPLICATION_OCTET_STREAM = new ContentType(APPLICATION, "octet-stream", null);
 
-  public static final ContentType APPLICATION_SVG_XML = create("application/svg+xml");
+  public static final ContentType APPLICATION_XHTML_XML = new ContentType(APPLICATION, "xhtml+xml", null);
+  public static final ContentType TEXT_HTML = new ContentType(TEXT, "html", null);
+  public static final ContentType TEXT_XML = new ContentType(TEXT, "xml", null);
+  public static final ContentType TEXT_PLAIN = new ContentType(TEXT, "plain", null);
 
-  public static final ContentType APPLICATION_FORM_URLENCODED = create("application/x-www-form-urlencoded");
+  public static final ContentType APPLICATION_SVG_XML = new ContentType(APPLICATION, "svg+xml", null);
 
-  public static final ContentType MULTIPART_FORM_DATA = create("multipart/form-data");
+  public static final ContentType APPLICATION_FORM_URLENCODED =
+      new ContentType(APPLICATION, "x-www-form-urlencoded", null);
 
-  public static final ContentType TEXT_XML = create("text/xml");
+  public static final ContentType MULTIPART_MIXED = new ContentType(MULTIPART, "mixed", null);
 
-  public static final ContentType TEXT_HTML = create("text/html");
+  public static final ContentType MULTIPART_FORM_DATA = new ContentType(MULTIPART, "form-data", null);
 
   public static final String PARAMETER_CHARSET_UTF8 = "charset=utf-8";
 
   private final String type;
-
   private final String subtype;
-
   private final Map<String, String> parameters;
 
   /**
@@ -133,7 +129,7 @@ public final class ContentType {
   }
 
   /**
-   * Creates a content type from format and key-value pairs for parameters
+   * Creates a content type from format and key-value pairs for parameters.
    *
    * @param format for example "application/json"
    * @param parameters for example "a=b", "c=d"
@@ -151,7 +147,7 @@ public final class ContentType {
   }
 
   /**
-   * Creates a content type from format and key-value pairs for parameters
+   * Creates a content type from an existing content type and additional key-value pairs for parameters.
    *
    * @param contentType for example "application/json"
    * @param parameters for example "a=b", "c=d"
@@ -203,7 +199,7 @@ public final class ContentType {
     }
   }
 
-  private static void parse(final String format, final List<String> typeSubtype, final Map<String, String> parameters) {
+  private static void parse(final String format, List<String> typeSubtype, Map<String, String> parameters) {
     final String[] typesAndParameters = format.split(TypeUtil.PARAMETER_SEPARATOR, 2);
     final String types = typesAndParameters[0];
     final String params = (typesAndParameters.length > 1 ? typesAndParameters[1] : null);
@@ -253,50 +249,44 @@ public final class ContentType {
   }
 
   /**
-   * {@link ContentType}s are equal
-   * <ul>
-   * <li>if <code>type</code>, <code>subtype</code> and all <code>parameters</code> have the same value.</li>
-   * <li>if <code>type</code> and/or <code>subtype</code> is set to "*" (in such a case the <code>parameters</code> are
-   * ignored).</li>
-   * </ul>
-   *
-   * @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>.
+   * {@link ContentType}s are equal if <code>type</code>, <code>subtype</code>, and all <code>parameters</code>
+   * have the same value.
    */
   @Override
   public boolean equals(final Object obj) {
-    // NULL validation is done in method 'isEqualWithoutParameters(obj)'
-   final Boolean compatible = isEqualWithoutParameters(obj);
+    // basic checks
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
 
-    if (compatible == null) {
-      ContentType other = (ContentType) obj;
+    final ContentType other = (ContentType) obj;
+
+    // type/subtype checks
+    if (!isCompatible(other)) {
+      return false;
+    }
 
-      // parameter checks
-      if (parameters == null) {
-        if (other.parameters != null) {
+    // parameter checks
+    if (parameters.size() == other.parameters.size()) {
+      final Iterator<Entry<String, String>> entries = parameters.entrySet().iterator();
+      final Iterator<Entry<String, String>> otherEntries = other.parameters.entrySet().iterator();
+      while (entries.hasNext()) {
+        final Entry<String, String> e = entries.next();
+        final Entry<String, String> oe = otherEntries.next();
+         if (!areEqual(e.getKey(), oe.getKey())) {
           return false;
         }
-      } else if (parameters.size() == other.parameters.size()) {
-        final Iterator<Entry<String, String>> entries = parameters.entrySet().iterator();
-        final Iterator<Entry<String, String>> otherEntries = other.parameters.entrySet().iterator();
-        while (entries.hasNext()) {
-          final Entry<String, String> e = entries.next();
-          final Entry<String, String> oe = otherEntries.next();
-
-          if (!areEqual(e.getKey(), oe.getKey())) {
-            return false;
-          }
-          if (!areEqual(e.getValue(), oe.getValue())) {
-            return false;
-          }
+        if (!areEqual(e.getValue(), oe.getValue())) {
+          return false;
         }
-      } else {
-        return false;
       }
-      return true;
     } else {
-      // all tests run
-      return compatible.booleanValue();
+      return false;
     }
+    return true;
   }
 
   /**
@@ -304,71 +294,27 @@ public final class ContentType {
    * if <code>type</code> and <code>subtype</code> have the same value.</p>
    * <p>The set <code>parameters</code> are <b>always</b> ignored
    * (for compare with parameters see {@link #equals(Object)}).</p>
-   * @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>.
+   * @return <code>true</code> if both instances are compatible (see definition above), otherwise <code>false</code>.
    */
-  public boolean isCompatible(final ContentType obj) {
-    final Boolean compatible = isEqualWithoutParameters(obj);
-    return compatible == null || compatible.booleanValue();
-  }
-
-  /**
-   * Check equal without parameters. It is possible that no decision about <code>equal/none equal</code> can be
-   * determined a <code>NULL</code> is returned.
-   *
-   * @param obj to checked object
-   * @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code> or
-   * <code>NULL</code> if no decision about <code>equal/none equal</code> could be determined.
-   */
-  private Boolean isEqualWithoutParameters(final Object obj) {
-    // basic checks
-    if (this == obj) {
-      return true;
-    }
-    if (obj == null) {
-      return false;
-    }
-    if (getClass() != obj.getClass()) {
-      return false;
-    }
-
-    ContentType other = (ContentType) obj;
-
-    // subtype checks
-    if (subtype == null) {
-      if (other.subtype != null) {
-        return false;
-      }
-    } else if (!subtype.equalsIgnoreCase(other.subtype)) {
-      return false;
-    }
-
-    // type checks
-    if (type == null) {
-      if (other.type != null) {
-        return false;
-      }
-    } else if (!type.equalsIgnoreCase(other.type)) {
-      return false;
-    }
-
-    return null;
+  public boolean isCompatible(final ContentType other) {
+    return type.equalsIgnoreCase(other.type) && subtype.equalsIgnoreCase(other.subtype);
   }
 
   /**
-   * Check whether both string are equal ignoring the case of the strings.
+   * Checks whether both strings are equal ignoring the case of the strings.
    *
    * @param first first string
    * @param second second string
-   * @return <code>true</code> if both strings are equal (by ignoring the case), otherwise <code>false</code> is
-   * returned
+   * @return <code>true</code> if both strings are equal (ignoring the case), otherwise <code>false</code>
    */
   private static boolean areEqual(final String first, final String second) {
     return first == null && second == null || first.equalsIgnoreCase(second);
   }
 
   /**
-   * Get {@link ContentType} as string as defined in RFC 7231 (http://www.ietf.org/rfc/rfc7231.txt, chapter 3.1.1.1:
-   * Media Type)
+   * Gets {@link ContentType} as string as defined in
+   * <a href="http://www.ietf.org/rfc/rfc7231.txt">RFC 7231</a>, chapter 3.1.1.1:
+   * Media Type.
    *
    * @return string representation of <code>ContentType</code> object
    */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java
index ad2e8ab..8e4f257 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.server.api;
 
 import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -46,6 +47,13 @@ public interface ODataHttpHandler {
   void register(Processor processor);
 
   /**
+   * Registers a service implementation for modifying the standard list of supported
+   * content types.
+   * @see CustomContentTypeSupport
+   */
+  void register(CustomContentTypeSupport customContentTypeSupport);
+
+  /**
    * Sets the split parameter which is used for service resolution.
    * @param split the number of path segments reserved for service resolution; default is 0
    */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
index f0ece5c..dc06b94 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ServiceMetadata.java
@@ -25,7 +25,7 @@ import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 import java.util.List;
 
 /**
- *
+ * Metadata of an OData service like the Entity Data Model.
  */
 public interface ServiceMetadata {
   /**

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CustomContentTypeSupport.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CustomContentTypeSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CustomContentTypeSupport.java
deleted file mode 100644
index 4759919..0000000
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/CustomContentTypeSupport.java
+++ /dev/null
@@ -1,45 +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.api.processor;
-
-import java.util.List;
-
-import org.apache.olingo.commons.api.format.ContentType;
-
-/**
- * A processor which supports custom content types can implement this interface.
- * The processor can also remove default content types if the default serializers
- * of Olingo are not used. By default this interface is not implemented and
- * a processor supports content types implemented by Olingo's default serializer
- * (e.g., <code>application/xml</code> for the metadata and
- * </code>application/json</code> for the service document).
- * Requesting a content type that is not supported results in an HTTP error
- * 406 (Not Acceptable).
- */
-public interface CustomContentTypeSupport {
-
-  /**
-   * Returns a list of supported content types.
-   * @param defaultContentTypes content types supported by Olingo's serializer
-   * @param processorClass the {@link Processor} of the current request
-   * @return modified list of supported content types
-   */
-  public List<ContentType> modifySupportedContentTypes(
-      List<ContentType> defaultContentTypes, Class<? extends Processor> processorClass);
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
index 3e02540..473c904 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
@@ -19,9 +19,7 @@
 package org.apache.olingo.server.api.processor;
 
 import java.io.ByteArrayInputStream;
-import java.io.InputStream;
 
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.http.HttpHeader;
@@ -77,13 +75,9 @@ public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProce
   public void processException(ODataRequest request, ODataResponse response, ODataServerError serverError,
       ContentType requestedContentType) {
     try {
-      if (ContentType.APPLICATION_XML.equals(requestedContentType)) {
-        requestedContentType = ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
-      }
       ODataSerializer serializer = odata.createSerializer(ODataFormat.fromContentType(requestedContentType));
-      InputStream responseEntity = serializer.error(serverError);
+      response.setContent(serializer.error(serverError));
       response.setStatusCode(serverError.getStatusCode());
-      response.setContent(responseEntity);
       response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
     } catch (Exception e) {
       // This should never happen but to be sure we have this catch here to prevent sending a stacktrace to a client.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ExceptionProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ExceptionProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ExceptionProcessor.java
index 170d6a4..0c062c0 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ExceptionProcessor.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/ExceptionProcessor.java
@@ -26,7 +26,7 @@ import org.apache.olingo.server.api.ODataServerError;
 /**
  * Processor which is called if any exception occurs inside the library or another processor.
  */
-public interface ExceptionProcessor extends Processor{
+public interface ExceptionProcessor extends Processor {
 
   /**
    * Processes an exception. MUST NOT throw an exception!
@@ -36,5 +36,5 @@ public interface ExceptionProcessor extends Processor{
    * @param requestedContentType the requested format for the error message
    */
   public void processException(ODataRequest request, ODataResponse response, ODataServerError serverError,
-      final ContentType requestedContentType);
+      ContentType requestedContentType);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/Processor.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/Processor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/Processor.java
index bec28b4..2b5eec2 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/Processor.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/Processor.java
@@ -30,8 +30,9 @@ public interface Processor {
 
   /**
    * Initializes the processor for each HTTP request - response cycle.
-   * @param odata - Olingo's root object, acting as a factory for various object types
-   * @param edm - the EDM which needs to be created before the OData request handling takes place
+   * @param odata           Olingo's root object, acting as a factory for various object types
+   * @param serviceMetadata metadata of the OData service like the EDM that have to be created
+   *                        before the OData request handling takes place
    */
-  void init(OData odata, ServiceMetadata edm);
+  void init(OData odata, ServiceMetadata serviceMetadata);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/CustomContentTypeSupport.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/CustomContentTypeSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/CustomContentTypeSupport.java
new file mode 100644
index 0000000..ca4f0cb
--- /dev/null
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/CustomContentTypeSupport.java
@@ -0,0 +1,46 @@
+/*
+ * 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.serializer;
+
+import java.util.List;
+
+import org.apache.olingo.commons.api.format.ContentType;
+
+/**
+ * <p>Processors that supports custom content types can implement this interface.</p>
+ * <p>The processor can also remove default content types if the default (de-)serializers
+ * of Olingo are not used. By default this interface is not implemented and
+ * a processor supports content types implemented by Olingo's default (de-)serializer
+ * (e.g., <code>application/xml</code> for the metadata and
+ * </code>application/json</code> for the service document).</p>
+ * <p>Requesting a content type that is not supported results in an HTTP error
+ * 406 (Not Acceptable); sending content of an unsupported type results in an
+ * HTTP error 415 (Unsupported Media Type).</p>
+ */
+public interface CustomContentTypeSupport {
+
+  /**
+   * Returns a list of supported content types.
+   * @param defaultContentTypes content types supported by Olingo's (de-)serializer
+   * @param type                the current type of representation
+   * @return modified list of supported content types
+   */
+  public List<ContentType> modifySupportedContentTypes(
+      List<ContentType> defaultContentTypes, RepresentationType type);
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/RepresentationType.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/RepresentationType.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/RepresentationType.java
new file mode 100644
index 0000000..22efa28
--- /dev/null
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/RepresentationType.java
@@ -0,0 +1,44 @@
+/*
+ * 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.serializer;
+
+/**
+ * The different types of representations that form the body of either the
+ * OData request or the OData response, primarily used for content negotiation.
+ */
+public enum RepresentationType {
+  /** service document */ SERVICE,
+  /** metadata document */ METADATA,
+  /** batch request or response */ BATCH,
+  /** error document */ ERROR,
+  /** single entity */ ENTITY,
+  /** collection of entities (entity set) */ COLLECTION_ENTITY,
+  /** single primitive-type instance */ PRIMITIVE,
+  /** collection of primitive-type instances */ COLLECTION_PRIMITIVE,
+  /** single complex-type instance */ COMPLEX,
+  /** collection of complex-type instances */ COLLECTION_COMPLEX,
+  /** differences */ DIFFERENCES,
+  /** media entity */ MEDIA,
+  /** binary-type instance */ BINARY,
+  /** single reference */ REFERENCE,
+  /** collection of references */ COLLECTION_REFERENCE,
+  /** textual raw value of a primitive-type instance (except binary) */ VALUE,
+  /** count of instances */ COUNT,
+  /** parameters of an action */ ACTION_PARAMETERS
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
index a4952ad..028b7b2 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
@@ -18,7 +18,7 @@
  */
 package org.apache.olingo.server.core;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@@ -27,44 +27,52 @@ 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.server.api.ODataRequest;
-import org.apache.olingo.server.api.processor.CustomContentTypeSupport;
-import org.apache.olingo.server.api.processor.MetadataProcessor;
-import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.serializer.RepresentationType;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
 
 public class ContentNegotiator {
 
   private ContentNegotiator() {}
 
-  private static List<ContentType> getDefaultSupportedContentTypes(final Class<? extends Processor> processorClass) {
-    List<ContentType> defaults = new ArrayList<ContentType>();
-
-    if (processorClass == MetadataProcessor.class) {
-      defaults.add(ODataFormat.XML.getContentType(ODataServiceVersion.V40));
-    } else {
-      defaults.add(ODataFormat.JSON.getContentType(ODataServiceVersion.V40));
-      defaults.add(ODataFormat.JSON_NO_METADATA.getContentType(ODataServiceVersion.V40));
+  private static List<ContentType> getDefaultSupportedContentTypes(final RepresentationType type) {
+    switch (type) {
+    case METADATA:
+      return Arrays.asList(ContentType.APPLICATION_XML);
+    case MEDIA:
+    case BINARY:
+      return Arrays.asList(ContentType.APPLICATION_OCTET_STREAM);
+    case VALUE:
+    case COUNT:
+      return Arrays.asList(ContentType.TEXT_PLAIN);
+    case BATCH:
+      return Arrays.asList(ContentType.MULTIPART_MIXED);
+    default:
+      return Arrays.asList(
+          ODataFormat.JSON.getContentType(ODataServiceVersion.V40),
+          ODataFormat.JSON_NO_METADATA.getContentType(ODataServiceVersion.V40));
     }
-
-    return defaults;
   }
 
-  private static List<ContentType> getSupportedContentTypes(final Processor processor,
-      final Class<? extends Processor> processorClass) {
-
-    List<ContentType> supportedContentTypes = getDefaultSupportedContentTypes(processorClass);
-
-    if (processor instanceof CustomContentTypeSupport) {
-      supportedContentTypes = ((CustomContentTypeSupport) processor)
-          .modifySupportedContentTypes(supportedContentTypes, processorClass);
+  private static List<ContentType> getSupportedContentTypes(
+      final CustomContentTypeSupport customContentTypeSupport, final RepresentationType representationType)
+          throws ContentNegotiatorException {
+    final List<ContentType> defaultSupportedContentTypes = getDefaultSupportedContentTypes(representationType);
+    final List<ContentType> result = customContentTypeSupport == null ? defaultSupportedContentTypes :
+        customContentTypeSupport.modifySupportedContentTypes(defaultSupportedContentTypes, representationType);
+    if (result == null || result.isEmpty()) {
+      throw new ContentNegotiatorException("No content type has been specified as supported.",
+          ContentNegotiatorException.MessageKeys.NO_CONTENT_TYPE_SUPPORTED);
+    } else {
+      return result;
     }
-
-    return supportedContentTypes;
   }
 
   public static ContentType doContentNegotiation(final FormatOption formatOption, final ODataRequest request,
-      final Processor processor, final Class<? extends Processor> processorClass) throws ContentNegotiatorException {
-    final List<ContentType> supportedContentTypes = getSupportedContentTypes(processor, processorClass);
+      final CustomContentTypeSupport customContentTypeSupport, final RepresentationType representationType)
+          throws ContentNegotiatorException {
+    final List<ContentType> supportedContentTypes =
+        getSupportedContentTypes(customContentTypeSupport, representationType);
     final String acceptHeaderValue = request.getHeader(HttpHeader.ACCEPT);
     ContentType result = null;
 
@@ -98,9 +106,7 @@ public class ContentNegotiator {
             ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPES, acceptedContentTypes.toString());
       }
     } else {
-      final ContentType requestedContentType = processorClass == MetadataProcessor.class ?
-          ODataFormat.XML.getContentType(ODataServiceVersion.V40) :
-          ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
+      final ContentType requestedContentType = getDefaultSupportedContentTypes(representationType).get(0);
       result = getAcceptedType(AcceptType.fromContentType(requestedContentType), supportedContentTypes);
       if (result == null) {
         throw new ContentNegotiatorException(

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java
index b30bd8a..efd597d 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java
@@ -30,6 +30,7 @@ public class ContentNegotiatorException extends ODataTranslatedException {
     UNSUPPORTED_CONTENT_TYPES,
     /** parameter: content type */
     UNSUPPORTED_CONTENT_TYPE,
+    NO_CONTENT_TYPE_SUPPORTED,
     /** parameter: format string */
     UNSUPPORTED_FORMAT_OPTION;
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-core/src/main/java/org/apache/olingo/server/core/DefaultRedirectProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/DefaultRedirectProcessor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/DefaultRedirectProcessor.java
index 1dc3e9c..23e539b 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/DefaultRedirectProcessor.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/DefaultRedirectProcessor.java
@@ -28,7 +28,7 @@ import org.apache.olingo.server.api.ServiceMetadata;
 public class DefaultRedirectProcessor implements RedirectProcessor {
 
   @Override
-  public void init(final OData odata, final ServiceMetadata edm) {}
+  public void init(final OData odata, final ServiceMetadata serviceMetadata) {}
 
   @Override
   public void redirect(final ODataRequest request, final ODataResponse response) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/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 4ac780d..a13ef6f 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
@@ -18,20 +18,22 @@
  */
 package org.apache.olingo.server.core;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.LinkedList;
+import java.util.List;
 
+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;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.ODataRequest;
 import org.apache.olingo.server.api.ODataResponse;
 import org.apache.olingo.server.api.ODataServerError;
-import org.apache.olingo.server.api.ODataTranslatedException;
 import org.apache.olingo.server.api.ServiceMetadata;
 import org.apache.olingo.server.api.processor.DefaultProcessor;
 import org.apache.olingo.server.api.processor.EntitySetProcessor;
@@ -41,11 +43,15 @@ import org.apache.olingo.server.api.processor.MetadataProcessor;
 import org.apache.olingo.server.api.processor.Processor;
 import org.apache.olingo.server.api.processor.PropertyProcessor;
 import org.apache.olingo.server.api.processor.ServiceDocumentProcessor;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.serializer.RepresentationType;
 import org.apache.olingo.server.api.serializer.SerializerException;
 import org.apache.olingo.server.api.uri.UriInfo;
 import org.apache.olingo.server.api.uri.UriResource;
+import org.apache.olingo.server.api.uri.UriResourceKind;
 import org.apache.olingo.server.api.uri.UriResourceNavigation;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.UriResourceProperty;
 import org.apache.olingo.server.core.uri.parser.Parser;
 import org.apache.olingo.server.core.uri.parser.UriParserException;
 import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
@@ -57,9 +63,10 @@ public class ODataHandler {
 
   private final OData odata;
   private final ServiceMetadata serviceMetadata;
-  private final Map<Class<? extends Processor>, Processor> processors =
-      new HashMap<Class<? extends Processor>, Processor>();
-  private ContentType requestedContentType;
+  private List<Processor> processors = new LinkedList<Processor>();
+  private CustomContentTypeSupport customContentTypeSupport = null;
+
+  private UriInfo uriInfo;
 
   public ODataHandler(final OData server, final ServiceMetadata serviceMetadata) {
     odata = server;
@@ -77,31 +84,31 @@ public class ODataHandler {
 
     } catch (final UriValidationException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, null);
+      handleException(request, response, serverError);
     } catch (final UriParserSemanticException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, null);
+      handleException(request, response, serverError);
     } catch (final UriParserSyntaxException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, null);
+      handleException(request, response, serverError);
     } catch (final UriParserException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, null);
+      handleException(request, response, serverError);
     } catch (ContentNegotiatorException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, null);
+      handleException(request, response, serverError);
     } catch (SerializerException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, requestedContentType);
+      handleException(request, response, serverError);
     } catch (ODataHandlerException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null);
-      handleException(request, response, serverError, requestedContentType);
+      handleException(request, response, serverError);
     } catch (ODataApplicationException e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e);
-      handleException(request, response, serverError, requestedContentType);
+      handleException(request, response, serverError);
     } catch (Exception e) {
       ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e);
-      handleException(request, response, serverError, requestedContentType);
+      handleException(request, response, serverError);
     }
     return response;
   }
@@ -111,48 +118,45 @@ public class ODataHandler {
       ODataApplicationException, SerializerException {
     validateODataVersion(request, response);
 
-    Parser parser = new Parser();
-    final UriInfo uriInfo = parser.parseUri(
-            request.getRawODataPath(), request.getRawQueryPath(),
-            null, serviceMetadata.getEdm());
+    uriInfo = new Parser().parseUri(request.getRawODataPath(), request.getRawQueryPath(), null,
+        serviceMetadata.getEdm());
 
-    UriValidator validator = new UriValidator();
-    validator.validate(uriInfo, request.getMethod());
+    final HttpMethod method = request.getMethod();
+    new UriValidator().validate(uriInfo, method);
 
     switch (uriInfo.getKind()) {
     case metadata:
-      if (request.getMethod().equals(HttpMethod.GET)) {
+      if (method.equals(HttpMethod.GET)) {
         MetadataProcessor mp = selectProcessor(MetadataProcessor.class);
 
-        requestedContentType =
-            ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, mp, MetadataProcessor.class);
+        ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+            customContentTypeSupport, RepresentationType.METADATA);
         mp.readMetadata(request, response, uriInfo, requestedContentType);
       } else {
-        throw new ODataHandlerException("HttpMethod " + request.getMethod() + " not allowed for metadata document",
-            ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, request.getMethod().toString());
+        throw new ODataHandlerException("HttpMethod " + method + " not allowed for metadata document",
+            ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString());
       }
       break;
     case service:
-      if (request.getMethod().equals(HttpMethod.GET)) {
+      if (method.equals(HttpMethod.GET)) {
         if ("".equals(request.getRawODataPath())) {
           RedirectProcessor rdp = selectProcessor(RedirectProcessor.class);
           rdp.redirect(request, response);
         } else {
           ServiceDocumentProcessor sdp = selectProcessor(ServiceDocumentProcessor.class);
 
-          requestedContentType =
-              ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, sdp,
-                  ServiceDocumentProcessor.class);
+          ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+              customContentTypeSupport, RepresentationType.SERVICE);
 
           sdp.readServiceDocument(request, response, uriInfo, requestedContentType);
         }
       } else {
-        throw new ODataHandlerException("HttpMethod " + request.getMethod() + " not allowed for service document",
-            ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, request.getMethod().toString());
+        throw new ODataHandlerException("HttpMethod " + method + " not allowed for service document",
+            ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString());
       }
       break;
     case resource:
-      handleResourceDispatching(request, response, uriInfo);
+      handleResourceDispatching(request, response);
       break;
     default:
       throw new ODataHandlerException("not implemented",
@@ -160,25 +164,28 @@ public class ODataHandler {
     }
   }
 
-  public void handleException(ODataRequest request, ODataResponse response, ODataServerError serverError,
-      ContentType requestedContentType) {
+  public void handleException(ODataRequest request, ODataResponse response, ODataServerError serverError) {
     ExceptionProcessor exceptionProcessor;
     try {
       exceptionProcessor = selectProcessor(ExceptionProcessor.class);
-    } catch (ODataTranslatedException e) {
-      // This cannot happen since there is always an ExceptionProcessor registered
+    } catch (ODataHandlerException e) {
+      // This cannot happen since there is always an ExceptionProcessor registered.
       exceptionProcessor = new DefaultProcessor();
     }
-    if (requestedContentType == null) {
+    ContentType requestedContentType;
+    try {
+      requestedContentType = ContentNegotiator.doContentNegotiation(
+          uriInfo == null ? null : uriInfo.getFormatOption(), request, customContentTypeSupport,
+          RepresentationType.ERROR);
+    } catch (final ContentNegotiatorException e) {
       requestedContentType = ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
     }
     exceptionProcessor.processException(request, response, serverError, requestedContentType);
   }
 
-  private void handleResourceDispatching(final ODataRequest request, final ODataResponse response,
-      final UriInfo uriInfo) throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException,
-      SerializerException {
-    int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1;
+  private void handleResourceDispatching(final ODataRequest request, final ODataResponse response)
+      throws ODataHandlerException, ContentNegotiatorException, ODataApplicationException, SerializerException {
+    final int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1;
     UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex);
 
     switch (lastPathSegment.getKind()) {
@@ -187,9 +194,8 @@ public class ODataHandler {
         if (request.getMethod().equals(HttpMethod.GET)) {
           EntitySetProcessor cp = selectProcessor(EntitySetProcessor.class);
 
-          requestedContentType =
-              ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, cp,
-                  EntitySetProcessor.class);
+          ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+              customContentTypeSupport, RepresentationType.COLLECTION_ENTITY);
 
           cp.readEntitySet(request, response, uriInfo, requestedContentType);
         } else {
@@ -200,8 +206,8 @@ public class ODataHandler {
         if (request.getMethod().equals(HttpMethod.GET)) {
           EntityProcessor ep = selectProcessor(EntityProcessor.class);
 
-          requestedContentType =
-              ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, ep, EntityProcessor.class);
+          ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+              customContentTypeSupport, RepresentationType.ENTITY);
 
           ep.readEntity(request, response, uriInfo, requestedContentType);
         } else {
@@ -215,9 +221,8 @@ public class ODataHandler {
         if (request.getMethod().equals(HttpMethod.GET)) {
           EntitySetProcessor cp = selectProcessor(EntitySetProcessor.class);
 
-          requestedContentType =
-              ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, cp,
-                  EntitySetProcessor.class);
+          ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+              customContentTypeSupport, RepresentationType.COLLECTION_ENTITY);
 
           cp.readEntitySet(request, response, uriInfo, requestedContentType);
         } else {
@@ -228,8 +233,8 @@ public class ODataHandler {
         if (request.getMethod().equals(HttpMethod.GET)) {
           EntityProcessor ep = selectProcessor(EntityProcessor.class);
 
-          requestedContentType =
-              ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, ep, EntityProcessor.class);
+          ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+              customContentTypeSupport, RepresentationType.ENTITY);
 
           ep.readEntity(request, response, uriInfo, requestedContentType);
         } else {
@@ -252,8 +257,14 @@ public class ODataHandler {
       if (request.getMethod().equals(HttpMethod.GET)) {
         PropertyProcessor ep = selectProcessor(PropertyProcessor.class);
 
-        requestedContentType =
-            ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, ep, PropertyProcessor.class);
+        final UriResourceProperty propertyResource = (UriResourceProperty) lastPathSegment;
+        final boolean isCollection = propertyResource.isCollection();
+        final boolean isComplex = propertyResource.getKind() == UriResourceKind.complexProperty;
+        final RepresentationType representationType =
+            isComplex ? isCollection ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX :
+                isCollection ? RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE;
+        ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+            customContentTypeSupport, representationType);
 
         ep.readProperty(request, response, uriInfo, requestedContentType);
       } else {
@@ -264,8 +275,17 @@ public class ODataHandler {
     case value:
       if (request.getMethod().equals(HttpMethod.GET)) {
         PropertyProcessor ep = selectProcessor(PropertyProcessor.class);
-        requestedContentType =
-                ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, ep, PropertyProcessor.class);
+
+        final UriResourceProperty propertyResource =
+            (UriResourceProperty) uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1);
+        final RepresentationType representationType =
+            (EdmPrimitiveType) propertyResource.getType() ==
+            EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ?
+                RepresentationType.BINARY :
+                RepresentationType.VALUE;
+        ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+            customContentTypeSupport, representationType);
+
         ep.readPropertyValue(request, response, uriInfo, requestedContentType);
       } else {
         throw new ODataHandlerException("not implemented",
@@ -280,7 +300,7 @@ public class ODataHandler {
 
   private void validateODataVersion(final ODataRequest request, final ODataResponse response)
       throws ODataHandlerException {
-    String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION);
+    final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION);
     response.setHeader(HttpHeader.ODATA_VERSION, ODataServiceVersion.V40.toString());
 
     if (maxVersion != null) {
@@ -292,26 +312,21 @@ public class ODataHandler {
   }
 
   private <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException {
-    @SuppressWarnings("unchecked")
-    T p = (T) processors.get(cls);
-
-    if (p == null) {
-      throw new ODataHandlerException("Processor: " + cls.getName() + " not registered.",
-          ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED, cls.getName());
+    for (final Processor processor : processors) {
+      if (cls.isAssignableFrom(processor.getClass())) {
+        processor.init(odata, serviceMetadata);
+        return cls.cast(processor);
+      }
     }
-
-    return p;
+    throw new ODataHandlerException("Processor: " + cls.getSimpleName() + " not registered.",
+        ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED, cls.getSimpleName());
   }
 
   public void register(final Processor processor) {
-    processor.init(odata, serviceMetadata);
+    processors.add(0, processor);
+  }
 
-    for (Class<?> cls : processor.getClass().getInterfaces()) {
-      if (Processor.class.isAssignableFrom(cls) && cls != Processor.class) {
-        @SuppressWarnings("unchecked")
-        Class<? extends Processor> procClass = (Class<? extends Processor>) cls;
-        processors.put(procClass, processor);
-      }
-    }
+  public void register(final CustomContentTypeSupport customContentTypeSupport) {
+    this.customContentTypeSupport = customContentTypeSupport;
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
index bb31e26..b06c3c8 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java
@@ -40,6 +40,7 @@ import org.apache.olingo.server.api.ODataServerError;
 import org.apache.olingo.server.api.ODataTranslatedException;
 import org.apache.olingo.server.api.ServiceMetadata;
 import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
 import org.apache.olingo.server.api.serializer.SerializerException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -85,7 +86,7 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
     } else {
       serverError = ODataExceptionHelper.createServerErrorObject(e);
     }
-    handler.handleException(odRequest, resp, serverError, null);
+    handler.handleException(odRequest, resp, serverError);
     return resp;
   }
 
@@ -230,4 +231,9 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
   public void register(final Processor processor) {
     handler.register(processor);
   }
+
+  @Override
+  public void register(final CustomContentTypeSupport customContentTypeSupport) {
+    handler.register(customContentTypeSupport);
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
index 63d8fb9..f97b7e0 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriParseTreeVisitor.java
@@ -651,7 +651,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
   }
 
   private String getName(final EdmType type) {
-    return type.getNamespace() + "." + type.getName();
+    return type.getFullQualifiedName().getFullQualifiedNameAsString();
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/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 87d08f8..07d9250 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
@@ -79,6 +79,7 @@ UriValidationException.UNALLOWED_KIND_BEFORE_COUNT=The kind '%1$s' is not allowe
 ContentNegotiatorException.WRONG_CHARSET_IN_HEADER=The HTTP header '%1$s' with value '%2$s' contains an invalid character-set specification.
 ContentNegotiatorException.UNSUPPORTED_CONTENT_TYPES=The content-type range '%1$s' is not supported.
 ContentNegotiatorException.UNSUPPORTED_CONTENT_TYPE=The content type '%1$s' is not supported.
+ContentNegotiatorException.NO_CONTENT_TYPE_SUPPORTED=No content type has been specified as supported.
 ContentNegotiatorException.UNSUPPORTED_FORMAT_OPTION=The $format option '%1$s' is not supported.
 
 SerializerException.NOT_IMPLEMENTED=The requested serialization method has not been implemented yet.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
index 0a958e2..46111cc 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
@@ -21,6 +21,8 @@ package org.apache.olingo.server.core;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -29,18 +31,11 @@ import java.util.Arrays;
 import java.util.List;
 
 import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpContentType;
 import org.apache.olingo.commons.api.http.HttpHeader;
-import org.apache.olingo.commons.api.http.HttpMethod;
-import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.ODataRequest;
-import org.apache.olingo.server.api.ODataResponse;
-import org.apache.olingo.server.api.ServiceMetadata;
-import org.apache.olingo.server.api.processor.CustomContentTypeSupport;
-import org.apache.olingo.server.api.processor.EntitySetProcessor;
-import org.apache.olingo.server.api.processor.MetadataProcessor;
-import org.apache.olingo.server.api.processor.Processor;
-import org.apache.olingo.server.api.processor.ServiceDocumentProcessor;
-import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.serializer.RepresentationType;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
 import org.junit.Test;
 
@@ -51,15 +46,15 @@ public class ContentNegotiatorTest {
   static final private String ACCEPT_CASE_FULL = "application/json;odata.metadata=full";
   static final private String ACCEPT_CASE_NONE = "application/json;odata.metadata=none";
   static final private String ACCEPT_CASE_JSONQ = "application/json;q=0.2";
-  static final private String ACCEPT_CASE_XML = "application/xml";
-  static final private String ACCEPT_CASE_WILDCARD1 = "*/*";
+  static final private String ACCEPT_CASE_XML = HttpContentType.APPLICATION_XML;
+  static final private String ACCEPT_CASE_WILDCARD1 = HttpContentType.WILDCARD;
   static final private String ACCEPT_CASE_WILDCARD2 = "application/*";
 
   //@formatter:off (Eclipse formatter)
   //CHECKSTYLE:OFF (Maven checkstyle)
 
   String[][] casesServiceDocument = {
-      /* expected               $format           accept                 additional content types */
+      /* expected               $format           accept                 modified content types */
       { ACCEPT_CASE_MIN,        null,             null,                  null             },
       { ACCEPT_CASE_MIN,        "json",           null,                  null             },
       { ACCEPT_CASE_MIN,        "json",           ACCEPT_CASE_JSONQ,     null             },
@@ -68,8 +63,8 @@ public class ContentNegotiatorTest {
       { ACCEPT_CASE_MIN,        null,             ACCEPT_CASE_JSONQ,     null             },
       { ACCEPT_CASE_MIN,        null,             ACCEPT_CASE_WILDCARD1, null             },
       { ACCEPT_CASE_MIN,        null,             ACCEPT_CASE_WILDCARD2, null             },
+      { ACCEPT_CASE_MIN,        null,             null,                  ACCEPT_CASE_MIN  },
       { "a/a",                  "a/a",            null,                  "a/a,b/b"        },
-      { "a/a",                  " a/a ",          null,                  " a/a , b/b "    },
       { "a/a;x=y",              "a/a",            ACCEPT_CASE_WILDCARD1, "a/a;x=y"        },
       { "a/a;v=w;x=y",          null,             "a/a;x=y",             "a/a;b=c,a/a;v=w;x=y" },
       { "a/a;v=w;x=y",          "a/a;x=y",        null,                  "a/a;b=c,a/a;v=w;x=y" },
@@ -79,7 +74,7 @@ public class ContentNegotiatorTest {
   };                                                                                          
 
   String[][] casesMetadata = {                                                                 
-      /* expected               $format           accept                 additional content types */
+      /* expected               $format           accept                 modified content types */
       { ACCEPT_CASE_XML,        null,             null,                  null             },
       { ACCEPT_CASE_XML,        "xml",            null,                  null             },
       { ACCEPT_CASE_XML,        "xml",            ACCEPT_CASE_XML,       null             },
@@ -88,12 +83,11 @@ public class ContentNegotiatorTest {
       { ACCEPT_CASE_XML,        null,             ACCEPT_CASE_WILDCARD1, null             },
       { ACCEPT_CASE_XML,        null,             ACCEPT_CASE_WILDCARD2, null             },
       { "a/a",                  "a/a",            null,                  "a/a,b/b"        },
-      { "a/a",                  " a/a ",          null,                  " a/a , b/b "    },
       { "a/a;x=y",              "a/a",            ACCEPT_CASE_WILDCARD1, "a/a;x=y"        }
   };
 
   String[][] casesFail = {                                                                 
-      /* expected               $format           accept                 additional content types */
+      /* expected               $format           accept                 modified content types */
       { null,                   "xxx/yyy",        null,                  null             },
       { null,                   "a/a",            null,                  "b/b"            },
       { null,                   "a/a;x=y",        null,                  "a/a;v=w"        },
@@ -101,71 +95,77 @@ public class ContentNegotiatorTest {
       { null,                   "atom",           null,                  null             }, // not yet supported
       { null,                   null,             ACCEPT_CASE_FULL,      null             }, // not yet supported
       { null,                   "a/b;charset=ISO-8859-1", null,          "a/b"            },
-      { null,                   null,             "a/b;charset=ISO-8859-1", "a/b"         }
+      { null,                   null,             "a/b;charset=ISO-8859-1", "a/b"         },
+      { null,                   null,             null,                  "text/plain"     }
   };
   //CHECKSTYLE:ON
   //@formatter:on
 
   @Test
-  public void testServiceDocumentSingleCase() throws Exception {
+  public void serviceDocumentSingleCase() throws Exception {
     testContentNegotiation(
         new String[] { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null },
-        ServiceDocumentProcessor.class);
+        RepresentationType.SERVICE);
   }
 
   @Test
-  public void testServiceDocument() throws Exception {
+  public void serviceDocument() throws Exception {
     for (String[] useCase : casesServiceDocument) {
-      testContentNegotiation(useCase, ServiceDocumentProcessor.class);
+      testContentNegotiation(useCase, RepresentationType.SERVICE);
     }
   }
 
   @Test
-  public void testMetadataSingleCase() throws Exception {
-    testContentNegotiation(new String[] { ACCEPT_CASE_XML, null, null, null }, MetadataProcessor.class);
+  public void metadataSingleCase() throws Exception {
+    testContentNegotiation(new String[] { ACCEPT_CASE_XML, null, null, null }, RepresentationType.METADATA);
   }
 
   @Test(expected = ContentNegotiatorException.class)
-  public void testMetadataJsonFail() throws Exception {
-    testContentNegotiation(new String[] { null, "json", null, null }, MetadataProcessor.class);
+  public void metadataJsonFail() throws Exception {
+    testContentNegotiation(new String[] { null, "json", null, null }, RepresentationType.METADATA);
   }
 
   @Test
-  public void testMetadata() throws Exception {
+  public void metadata() throws Exception {
     for (String[] useCase : casesMetadata) {
-      testContentNegotiation(useCase, MetadataProcessor.class);
+      testContentNegotiation(useCase, RepresentationType.METADATA);
     }
   }
 
   @Test
-  public void testEntityCollectionFail() throws Exception {
+  public void entityCollectionFail() throws Exception {
     for (String[] useCase : casesFail) {
       try {
-        testContentNegotiation(useCase, EntitySetProcessor.class);
+        testContentNegotiation(useCase, RepresentationType.COLLECTION_ENTITY);
         fail("Exception expected for '" + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!");
       } catch (final ContentNegotiatorException e) {}
     }
   }
 
-  private void testContentNegotiation(final String[] useCase, final Class<? extends Processor> processorClass)
+  private void testContentNegotiation(final String[] useCase, final RepresentationType representationType)
       throws ContentNegotiatorException {
-    ODataRequest request = new ODataRequest();
-    request.setMethod(HttpMethod.GET);
-    request.setRawODataPath("/" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
-
-    ProcessorStub p = new ProcessorStub(createCustomContentTypes(useCase[3]));
 
-    FormatOption fo = null;
+    FormatOption formatOption = null;
     if (useCase[1] != null) {
-      fo = mock(FormatOption.class);
-      when(fo.getFormat()).thenReturn(useCase[1].trim());
+      formatOption = mock(FormatOption.class);
+      when(formatOption.getFormat()).thenReturn(useCase[1]);
     }
 
+    ODataRequest request = new ODataRequest();
     if (useCase[2] != null) {
       request.addHeader(HttpHeader.ACCEPT, Arrays.asList(useCase[2]));
     }
 
-    final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(fo, request, p, processorClass);
+    CustomContentTypeSupport customContentTypeSupport = null;
+    if (useCase[3] != null) {
+      customContentTypeSupport = mock(CustomContentTypeSupport.class);
+      when(customContentTypeSupport.modifySupportedContentTypes(
+          anyListOf(ContentType.class), any(RepresentationType.class)))
+          .thenReturn(createCustomContentTypes(useCase[3]));
+    }
+
+    final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(
+        formatOption, request, customContentTypeSupport, representationType);
 
     assertNotNull(requestedContentType);
     if (useCase[0] != null) {
@@ -174,66 +174,13 @@ public class ContentNegotiatorTest {
   }
 
   private List<ContentType> createCustomContentTypes(final String contentTypeString) {
-
-    if (contentTypeString == null) {
-      return null;
-    }
-
-    String[] contentTypes = contentTypeString.split(",");
+    final String[] contentTypes = contentTypeString.split(",");
 
     List<ContentType> types = new ArrayList<ContentType>();
     for (int i = 0; i < contentTypes.length; i++) {
-      types.add(ContentType.create(contentTypes[i].trim()));
+      types.add(ContentType.create(contentTypes[i]));
     }
 
     return types;
   }
-
-  private class ProcessorStub implements ServiceDocumentProcessor, MetadataProcessor,
-      EntitySetProcessor, CustomContentTypeSupport {
-
-    List<ContentType> customTypes;
-
-    ProcessorStub(final List<ContentType> types) {
-      customTypes = types;
-    }
-
-    @Override
-    public void init(final OData odata, final ServiceMetadata edm) {}
-
-    @Override
-    public List<ContentType> modifySupportedContentTypes(final List<ContentType> supportedContentTypes,
-        final Class<? extends Processor> processorClass) {
-      if (customTypes == null) {
-        return supportedContentTypes;
-      } else {
-        List<ContentType> modifiedTypes = new ArrayList<ContentType>(supportedContentTypes);
-        modifiedTypes.addAll(customTypes);
-        return modifiedTypes;
-      }
-    }
-
-    @Override
-    public void readServiceDocument(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo,
-        final ContentType format) {
-      response.setHeader(HttpHeader.CONTENT_TYPE, format.toContentTypeString());
-    }
-
-    @Override
-    public void readEntitySet(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo,
-        final ContentType requestedContentType) {
-      response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
-    }
-
-    @Override
-    public void readMetadata(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo,
-        final ContentType requestedContentType) {
-      response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
-    }
-
-    @Override
-    public void countEntitySet(ODataRequest request, ODataResponse response, UriInfo uriInfo) {
-      response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.TEXT_PLAIN.toContentTypeString());
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e5b284a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
index ca87c6b..f05002e 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
@@ -33,12 +33,14 @@ import org.apache.olingo.commons.api.data.Property;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.edm.EdmProperty;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.http.HttpContentType;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
 import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.ODataRequest;
@@ -74,7 +76,7 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor,
   }
 
   @Override
-  public void init(final OData odata, final ServiceMetadata edm) {
+  public void init(final OData odata, final ServiceMetadata serviceMetadata) {
     this.odata = odata;
   }
 
@@ -280,20 +282,24 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor,
         response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
       } else {
         final EdmPrimitiveType type = (EdmPrimitiveType) edmProperty.getType();
-        try {
-          final String value = type.valueToString(property.getValue(),
-              edmProperty.isNullable(), edmProperty.getMaxLength(),
-              edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode());
-          response.setContent(new ByteArrayInputStream(value.getBytes("UTF-8")));
-        } catch (final EdmPrimitiveTypeException e) {
-          throw new ODataApplicationException("Error in value formatting.",
-              HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e);
-        } catch (final UnsupportedEncodingException e) {
-          throw new ODataApplicationException("Encoding exception.",
-              HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e);
+        if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary)) {
+          response.setContent(new ByteArrayInputStream((byte[]) property.getValue()));
+        } else {
+          try {
+            final String value = type.valueToString(property.getValue(),
+                edmProperty.isNullable(), edmProperty.getMaxLength(),
+                edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode());
+            response.setContent(new ByteArrayInputStream(value.getBytes("UTF-8")));
+          } catch (final EdmPrimitiveTypeException e) {
+            throw new ODataApplicationException("Error in value formatting.",
+                HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e);
+          } catch (final UnsupportedEncodingException e) {
+            throw new ODataApplicationException("Encoding exception.",
+                HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT, e);
+          }
         }
+        response.setHeader(HttpHeader.CONTENT_TYPE, contentType.toContentTypeString());
         response.setStatusCode(HttpStatusCode.OK.getStatusCode());
-        response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.TEXT_PLAIN.toContentTypeString());
       }
     }
   }