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 379eb9555..2ac9a278b 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:
*
* Accept = #( media-range [ accept-params ] )
@@ -41,7 +41,7 @@ import java.util.TreeMap;
* qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
*
*
- * Once created a {@link AcceptType} is IMMUTABLE.
+ * Once created an {@link AcceptType} is IMMUTABLE.
*/
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 (format
).
+ * Creates a list of {@link AcceptType} objects based on given input string (format
).
* @param format accept types, comma-separated, as specified for the HTTP header Accept
* @return a list of AcceptType
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 AcceptType
objects that matches only the given content type
+ */
+ public static List fromContentType(final ContentType contentType) {
+ return Collections.singletonList(new AcceptType(
+ contentType.getType(), contentType.getSubtype(), contentType.getParameters(), 1F));
+ }
+
public String getType() {
return type;
}
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 1adabf70d..c6fb1bf9b 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
- getDefaultSupportedContentTypes(final Class extends Processor> processorClass) {
+ private static List getDefaultSupportedContentTypes(final Class extends Processor> processorClass) {
List defaults = new ArrayList();
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 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 acceptedContentTypes,
final List 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;
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 61d0bc85e..017a098d4 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 createCustomContentTypes(final String contentTypeString) {