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/09/25 15:32:42 UTC

[3/3] git commit: better handling of parameters in server content negotiation

better handling of parameters in server content negotiation

Change-Id: Ie2771940b3afea6efb3dc84f8322bc60069052d1

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/7e0b013c
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/7e0b013c
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/7e0b013c

Branch: refs/heads/master
Commit: 7e0b013cef37c8353bcb29a25adf74721ba0afef
Parents: 788036d
Author: Klaus Straubinger <kl...@sap.com>
Authored: Thu Sep 25 13:45:39 2014 +0200
Committer: Christian Amend <ch...@apache.org>
Committed: Thu Sep 25 15:26:38 2014 +0200

----------------------------------------------------------------------
 .../olingo/commons/api/format/AcceptType.java   | 18 ++++--
 .../olingo/server/core/ContentNegotiator.java   | 58 ++++++++++----------
 .../server/core/ContentNegotiatorTest.java      | 43 ++++++++++-----
 3 files changed, 70 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7e0b013c/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java
index 379eb95..2ac9a27 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java
@@ -27,7 +27,7 @@ import java.util.TreeMap;
 
 /**
  * Internally used {@link AcceptType} for OData library.
- * 
+ *
  * See RFC 7231, chapter 5.3.2:
  * <pre>
  * Accept = #( media-range [ accept-params ] )
@@ -41,7 +41,7 @@ import java.util.TreeMap;
  * qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
  * </pre>
  * 
- * Once created a {@link AcceptType} is <b>IMMUTABLE</b>.
+ * Once created an {@link AcceptType} is <b>IMMUTABLE</b>.
  */
 public class AcceptType {
 
@@ -82,7 +82,7 @@ public class AcceptType {
     if (TypeUtil.MEDIA_TYPE_WILDCARD.equals(this.type) && !TypeUtil.MEDIA_TYPE_WILDCARD.equals(subtype)) {
       throw new IllegalArgumentException("Illegal combination of WILDCARD type with NONE WILDCARD subtype.");
     }
-    
+
     final String q = parameters.get(TypeUtil.PARAMETER_Q);
     if (q == null) {
       quality = 1F;
@@ -124,7 +124,7 @@ public class AcceptType {
   }
 
   /**
-   * Create a list of {@link AcceptType} objects based on given input string (<code>format</code>).
+   * Creates a list of {@link AcceptType} objects based on given input string (<code>format</code>).
    * @param format accept types, comma-separated, as specified for the HTTP header <code>Accept</code>
    * @return a list of <code>AcceptType</code> objects
    * @throws IllegalArgumentException if input string is not parseable
@@ -142,6 +142,16 @@ public class AcceptType {
     return result;
   }
 
+  /**
+   * Creates a list of {@link AcceptType} objects based on given content type.
+   * @param contentType the content type
+   * @return an immutable one-element list of <code>AcceptType</code> objects that matches only the given content type
+   */
+  public static List<AcceptType> fromContentType(final ContentType contentType) {
+    return Collections.singletonList(new AcceptType(
+        contentType.getType(), contentType.getSubtype(), contentType.getParameters(), 1F));
+  }
+
   public String getType() {
     return type;
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7e0b013c/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 1adabf7..c6fb1bf 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
@@ -36,8 +36,7 @@ public class ContentNegotiator {
 
   private ContentNegotiator() {}
 
-  private static List<ContentType>
-      getDefaultSupportedContentTypes(final Class<? extends Processor> processorClass) {
+  private static List<ContentType> getDefaultSupportedContentTypes(final Class<? extends Processor> processorClass) {
     List<ContentType> defaults = new ArrayList<ContentType>();
 
     if (processorClass == MetadataProcessor.class) {
@@ -75,35 +74,23 @@ public class ContentNegotiator {
           ODataFormat.JSON.name().equalsIgnoreCase(formatString) ? ODataFormat.JSON :
           ODataFormat.XML.name().equalsIgnoreCase(formatString) ? ODataFormat.XML :
           ODataFormat.ATOM.name().equalsIgnoreCase(formatString) ? ODataFormat.ATOM : null;
-      result = getSupportedContentType(format == null ?
-          ContentType.create(formatOption.getFormat()) : format.getContentType(ODataServiceVersion.V40),
-          supportedContentTypes);
+      try {
+        result = getAcceptedType(
+            AcceptType.fromContentType(format == null ?
+                ContentType.create(formatOption.getFormat()) : format.getContentType(ODataServiceVersion.V40)),
+            supportedContentTypes);
+      } catch (final IllegalArgumentException e) {}
       if (result == null) {
         throw new ContentNegotiatorException("Unsupported $format = " + formatString,
             ContentNegotiatorException.MessageKeys.UNSUPPORTED_FORMAT_OPTION, formatString);
       }
     } else if (acceptHeaderValue != null) {
       final List<AcceptType> acceptedContentTypes = AcceptType.create(acceptHeaderValue);
-      for (AcceptType acceptedType : acceptedContentTypes) {
-        for (final ContentType supportedContentType : supportedContentTypes) {
-          ContentType contentType = supportedContentType;
-          if (acceptedType.getParameters().containsKey("charset")) {
-            final String value = acceptedType.getParameters().get("charset");
-            if ("utf8".equalsIgnoreCase(value) || "utf-8".equalsIgnoreCase(value)) {
-              contentType = ContentType.create(contentType, ContentType.PARAMETER_CHARSET_UTF8);
-            } else {
-              throw new ContentNegotiatorException("charset in accept header not supported: " + acceptHeaderValue,
-                  ContentNegotiatorException.MessageKeys.WRONG_CHARSET_IN_HEADER, HttpHeader.ACCEPT, acceptHeaderValue);
-            }
-          }
-          if (acceptedType.matches(contentType)) {
-            result = contentType;
-            break;
-          }
-        }
-        if (result != null) {
-          break;
-        }
+      try {
+        result = getAcceptedType(acceptedContentTypes, supportedContentTypes);
+      } catch (final IllegalArgumentException e) {
+        throw new ContentNegotiatorException("charset in accept header not supported: " + acceptHeaderValue, e,
+            ContentNegotiatorException.MessageKeys.WRONG_CHARSET_IN_HEADER, HttpHeader.ACCEPT, acceptHeaderValue);
       }
       if (result == null) {
         throw new ContentNegotiatorException(
@@ -114,7 +101,7 @@ public class ContentNegotiator {
       final ContentType requestedContentType = processorClass == MetadataProcessor.class ?
           ODataFormat.XML.getContentType(ODataServiceVersion.V40) :
           ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
-      result = getSupportedContentType(requestedContentType, supportedContentTypes);
+      result = getAcceptedType(AcceptType.fromContentType(requestedContentType), supportedContentTypes);
       if (result == null) {
         throw new ContentNegotiatorException(
             "unsupported accept content type: " + requestedContentType + " != " + supportedContentTypes,
@@ -125,11 +112,22 @@ public class ContentNegotiator {
     return result;
   }
 
-  private static ContentType getSupportedContentType(final ContentType requestedContentType,
+  private static ContentType getAcceptedType(final List<AcceptType> acceptedContentTypes,
       final List<ContentType> supportedContentTypes) {
-    for (final ContentType supportedContentType : supportedContentTypes) {
-      if (requestedContentType.isCompatible(supportedContentType)) {
-        return supportedContentType;
+    for (final AcceptType acceptedType : acceptedContentTypes) {
+      for (final ContentType supportedContentType : supportedContentTypes) {
+        ContentType contentType = supportedContentType;
+        if (acceptedType.getParameters().containsKey("charset")) {
+          final String value = acceptedType.getParameters().get("charset");
+          if ("utf8".equalsIgnoreCase(value) || "utf-8".equalsIgnoreCase(value)) {
+            contentType = ContentType.create(contentType, ContentType.PARAMETER_CHARSET_UTF8);
+          } else {
+            throw new IllegalArgumentException("charset not supported: " + acceptedType);
+          }
+        }
+        if (acceptedType.matches(contentType)) {
+          return contentType;
+        }
       }
     }
     return null;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/7e0b013c/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 61d0bc8..017a098 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
@@ -49,6 +49,7 @@ public class ContentNegotiatorTest {
   static final private String ACCEPT_CASE_MIN = "application/json;odata.metadata=minimal";
   static final private String ACCEPT_CASE_MIN_UTF8 = "application/json;charset=UTF-8;odata.metadata=minimal";
   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 = "*/*";
@@ -62,6 +63,7 @@ public class ContentNegotiatorTest {
       { ACCEPT_CASE_MIN,        null,             null,                  null             },
       { ACCEPT_CASE_MIN,        "json",           null,                  null             },
       { ACCEPT_CASE_MIN,        "json",           ACCEPT_CASE_JSONQ,     null             },
+      { ACCEPT_CASE_NONE,       ACCEPT_CASE_NONE, null,                  null             },
       { "a/a",                  "a/a",            null,                  "a/a"            },
       { ACCEPT_CASE_MIN,        null,             ACCEPT_CASE_JSONQ,     null             },
       { ACCEPT_CASE_MIN,        null,             ACCEPT_CASE_WILDCARD1, null             },
@@ -69,9 +71,11 @@ public class ContentNegotiatorTest {
       { "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" },
       { ACCEPT_CASE_MIN,        "json",           ACCEPT_CASE_MIN,       null             },
       { ACCEPT_CASE_FULL,       null,             ACCEPT_CASE_FULL,      ACCEPT_CASE_FULL }, 
-      { ACCEPT_CASE_MIN_UTF8,   null,             ACCEPT_CASE_MIN_UTF8,  null             },
+      { ACCEPT_CASE_MIN_UTF8,   null,             ACCEPT_CASE_MIN_UTF8,  null             }
   };                                                                                          
 
   String[][] casesMetadata = {                                                                 
@@ -85,24 +89,28 @@ public class ContentNegotiatorTest {
       { 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"        },
+      { "a/a;x=y",              "a/a",            ACCEPT_CASE_WILDCARD1, "a/a;x=y"        }
   };
 
   String[][] casesFail = {                                                                 
       /* expected               $format           accept                 additional content types */
-      { ACCEPT_CASE_XML,        "xxx/yyy",        null,                  null             },
-      { "a/a",                  "a/a",            null,                  "b/b"            },
-      { ACCEPT_CASE_XML,        null,             ACCEPT_CASE_JSONQ,     null             },
-      { "application/json",     null,             ACCEPT_CASE_FULL,      null             }, // not yet supported
+      { null,                   "xxx/yyy",        null,                  null             },
+      { null,                   "a/a",            null,                  "b/b"            },
+      { null,                   "a/a;x=y",        null,                  "a/a;v=w"        },
+      { null,                   null,             "a/a;x=y",             "a/a;v=w"        },
+      { 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"         }
   };
   //CHECKSTYLE:ON
   //@formatter:on
 
   @Test
   public void testServiceDocumentSingleCase() throws Exception {
-    String[] useCase = { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null };
-
-    testContentNegotiation(useCase, ServiceDocumentProcessor.class);
+    testContentNegotiation(
+        new String[] { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null },
+        ServiceDocumentProcessor.class);
   }
 
   @Test
@@ -114,9 +122,12 @@ public class ContentNegotiatorTest {
 
   @Test
   public void testMetadataSingleCase() throws Exception {
-    String[] useCase = { ACCEPT_CASE_XML, null, null, null };
+    testContentNegotiation(new String[] { ACCEPT_CASE_XML, null, null, null }, MetadataProcessor.class);
+  }
 
-    testContentNegotiation(useCase, MetadataProcessor.class);
+  @Test(expected = ContentNegotiatorException.class)
+  public void testMetadataJsonFail() throws Exception {
+    testContentNegotiation(new String[] { null, "json", null, null }, MetadataProcessor.class);
   }
 
   @Test
@@ -127,11 +138,11 @@ public class ContentNegotiatorTest {
   }
 
   @Test
-  public void testMetadataFail() throws Exception {
+  public void testEntityCollectionFail() throws Exception {
     for (String[] useCase : casesFail) {
       try {
-        testContentNegotiation(useCase, MetadataProcessor.class);
-        fail("Exception expected!");
+        testContentNegotiation(useCase, EntityCollectionProcessor.class);
+        fail("Exception expected for '" + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!");
       } catch (final ContentNegotiatorException e) {}
     }
   }
@@ -157,7 +168,9 @@ public class ContentNegotiatorTest {
     final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(fo, request, p, processorClass);
 
     assertNotNull(requestedContentType);
-    assertEquals(ContentType.create(useCase[0]), requestedContentType);
+    if (useCase[0] != null) {
+      assertEquals(ContentType.create(useCase[0]), requestedContentType);
+    }
   }
 
   private List<ContentType> createCustomContentTypes(final String contentTypeString) {