[OLINGO-356] Content negotiation change for custom format
This commit is contained in:
parent
9048d725a2
commit
266c7b4ae8
|
@ -45,9 +45,6 @@ import java.util.TreeMap;
|
||||||
*/
|
*/
|
||||||
public class AcceptType {
|
public class AcceptType {
|
||||||
|
|
||||||
// public static final AcceptType WILDCARD = new AcceptType(TypeUtil.MEDIA_TYPE_WILDCARD, TypeUtil.MEDIA_TYPE_WILDCARD,
|
|
||||||
// createParameterMap(), 1.0F);
|
|
||||||
|
|
||||||
private final String type;
|
private final String type;
|
||||||
private final String subtype;
|
private final String subtype;
|
||||||
private final Map<String, String> parameters;
|
private final Map<String, String> parameters;
|
||||||
|
@ -98,8 +95,8 @@ public class AcceptType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void
|
private static void parse(final String format, final List<String> typeSubtype,
|
||||||
parse(final String format, final List<String> typeSubtype, final Map<String, String> parameters) {
|
final Map<String, String> parameters) {
|
||||||
|
|
||||||
final String[] typesAndParameters = format.split(TypeUtil.PARAMETER_SEPARATOR, 2);
|
final String[] typesAndParameters = format.split(TypeUtil.PARAMETER_SEPARATOR, 2);
|
||||||
final String types = typesAndParameters[0];
|
final String types = typesAndParameters[0];
|
||||||
|
@ -127,9 +124,9 @@ public class AcceptType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an {@link AcceptType} based on given input string (<code>format</code>).
|
* Create a list of {@link AcceptType} objects based on given input string (<code>format</code>).
|
||||||
* @param format
|
* @param format accept types, comma-separated, as specified for the HTTP header <code>Accept</code>
|
||||||
* @return a new <code>AcceptType</code> object
|
* @return a list of <code>AcceptType</code> objects
|
||||||
* @throws IllegalArgumentException if input string is not parseable
|
* @throws IllegalArgumentException if input string is not parseable
|
||||||
*/
|
*/
|
||||||
public static List<AcceptType> create(final String format) {
|
public static List<AcceptType> create(final String format) {
|
||||||
|
@ -216,7 +213,7 @@ public class AcceptType {
|
||||||
* as defined in RFC 7231, chapters 3.1.1.1, 5.3.1, and 5.3.2.
|
* as defined in RFC 7231, chapters 3.1.1.1, 5.3.1, and 5.3.2.
|
||||||
* @param toSort list which is sorted and hence re-arranged
|
* @param toSort list which is sorted and hence re-arranged
|
||||||
*/
|
*/
|
||||||
private static void sort(final List<AcceptType> toSort) {
|
private static void sort(List<AcceptType> toSort) {
|
||||||
Collections.sort(toSort,
|
Collections.sort(toSort,
|
||||||
new Comparator<AcceptType>() {
|
new Comparator<AcceptType>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -302,22 +302,15 @@ public final class ContentType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ContentType}s are <b>compatible</b>
|
* <p>{@link ContentType}s are <b>compatible</b>
|
||||||
* <ul>
|
* if <code>type</code> and <code>subtype</code> have the same value.</p>
|
||||||
* <li>if <code>type</code>, <code>subtype</code> have the same value.</li>
|
* <p>The set <code>parameters</code> are <b>always</b> ignored
|
||||||
* <li>if <code>type</code> and/or <code>subtype</code> is set to "*"</li>
|
* (for compare with parameters see {@link #equals(Object)}).</p>
|
||||||
* </ul>
|
|
||||||
* The set <code>parameters</code> are <b>always</b> ignored (for compare with parameters see {@link #equals(Object)}
|
|
||||||
* ).
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>.
|
* @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public boolean isCompatible(final ContentType obj) {
|
public boolean isCompatible(final ContentType obj) {
|
||||||
Boolean compatible = isEqualWithoutParameters(obj);
|
final Boolean compatible = isEqualWithoutParameters(obj);
|
||||||
if (compatible == null) {
|
return compatible == null || compatible.booleanValue();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return compatible.booleanValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,6 +20,8 @@ package org.apache.olingo.server.api.processor;
|
||||||
|
|
||||||
import java.util.List;
|
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
|
* A processor which supports custom content types can implement this interface. The processor can also remove default
|
||||||
* content types if the default serializer of Olingo are not used. By default this interface is not implemented and
|
* content types if the default serializer of Olingo are not used. By default this interface is not implemented and
|
||||||
|
@ -35,7 +37,7 @@ public interface CustomContentTypeSupportProcessor {
|
||||||
* @return modified list of supported content types
|
* @return modified list of supported content types
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public List<FormatContentTypeMapping> modifySupportedContentTypes(
|
public List<ContentType> modifySupportedContentTypes(
|
||||||
List<FormatContentTypeMapping> defaultContentTypes, Class<? extends Processor> processorClass);
|
List<ContentType> defaultContentTypes, Class<? extends Processor> processorClass);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mapping between an uri $format option value and a content types. For instance the $format option "xml" maps to
|
|
||||||
* content type "application/xml".
|
|
||||||
*/
|
|
||||||
public class FormatContentTypeMapping {
|
|
||||||
|
|
||||||
private String formatOptionValue;
|
|
||||||
private String contentTypeValue;
|
|
||||||
|
|
||||||
public FormatContentTypeMapping(final String formatOptionValue, final String contentTypeValue) {
|
|
||||||
super();
|
|
||||||
this.formatOptionValue = formatOptionValue;
|
|
||||||
this.contentTypeValue = contentTypeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFormatAlias() {
|
|
||||||
return formatOptionValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContentType() {
|
|
||||||
return contentTypeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFormatAlias(final String formatOptionValue) {
|
|
||||||
this.formatOptionValue = formatOptionValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentType(final String contentTypeValue) {
|
|
||||||
this.contentTypeValue = contentTypeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "('" + formatOptionValue + "', '" + contentTypeValue + "')";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -28,7 +28,6 @@ import org.apache.olingo.commons.api.format.ODataFormat;
|
||||||
import org.apache.olingo.commons.api.http.HttpHeader;
|
import org.apache.olingo.commons.api.http.HttpHeader;
|
||||||
import org.apache.olingo.server.api.ODataRequest;
|
import org.apache.olingo.server.api.ODataRequest;
|
||||||
import org.apache.olingo.server.api.processor.CustomContentTypeSupportProcessor;
|
import org.apache.olingo.server.api.processor.CustomContentTypeSupportProcessor;
|
||||||
import org.apache.olingo.server.api.processor.FormatContentTypeMapping;
|
|
||||||
import org.apache.olingo.server.api.processor.MetadataProcessor;
|
import org.apache.olingo.server.api.processor.MetadataProcessor;
|
||||||
import org.apache.olingo.server.api.processor.Processor;
|
import org.apache.olingo.server.api.processor.Processor;
|
||||||
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
|
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
|
||||||
|
@ -37,29 +36,28 @@ public class ContentNegotiator {
|
||||||
|
|
||||||
private ContentNegotiator() {}
|
private ContentNegotiator() {}
|
||||||
|
|
||||||
private static List<FormatContentTypeMapping>
|
private static List<ContentType>
|
||||||
getDefaultSupportedContentTypes(final Class<? extends Processor> processorClass) {
|
getDefaultSupportedContentTypes(final Class<? extends Processor> processorClass) {
|
||||||
List<FormatContentTypeMapping> defaults = new ArrayList<FormatContentTypeMapping>();
|
List<ContentType> defaults = new ArrayList<ContentType>();
|
||||||
|
|
||||||
if (processorClass == MetadataProcessor.class) {
|
if (processorClass == MetadataProcessor.class) {
|
||||||
defaults.add(new FormatContentTypeMapping("xml", ContentType.APPLICATION_XML.toContentTypeString()));
|
defaults.add(ODataFormat.XML.getContentType(ODataServiceVersion.V40));
|
||||||
} else {
|
} else {
|
||||||
defaults.add(new FormatContentTypeMapping("json",
|
defaults.add(ODataFormat.JSON.getContentType(ODataServiceVersion.V40));
|
||||||
ODataFormat.JSON.getContentType(ODataServiceVersion.V40).toContentTypeString()));
|
defaults.add(ODataFormat.JSON_NO_METADATA.getContentType(ODataServiceVersion.V40));
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<FormatContentTypeMapping> getSupportedContentTypes(final Processor processor,
|
private static List<ContentType> getSupportedContentTypes(final Processor processor,
|
||||||
final Class<? extends Processor> processorClass) {
|
final Class<? extends Processor> processorClass) {
|
||||||
|
|
||||||
List<FormatContentTypeMapping> supportedContentTypes = getDefaultSupportedContentTypes(processorClass);
|
List<ContentType> supportedContentTypes = getDefaultSupportedContentTypes(processorClass);
|
||||||
|
|
||||||
if (processor instanceof CustomContentTypeSupportProcessor) {
|
if (processor instanceof CustomContentTypeSupportProcessor) {
|
||||||
supportedContentTypes =
|
supportedContentTypes = ((CustomContentTypeSupportProcessor) processor)
|
||||||
((CustomContentTypeSupportProcessor) processor).modifySupportedContentTypes(supportedContentTypes,
|
.modifySupportedContentTypes(supportedContentTypes, processorClass);
|
||||||
processorClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return supportedContentTypes;
|
return supportedContentTypes;
|
||||||
|
@ -67,98 +65,73 @@ public class ContentNegotiator {
|
||||||
|
|
||||||
public static ContentType doContentNegotiation(final FormatOption formatOption, final ODataRequest request,
|
public static ContentType doContentNegotiation(final FormatOption formatOption, final ODataRequest request,
|
||||||
final Processor processor, final Class<? extends Processor> processorClass) throws ContentNegotiatorException {
|
final Processor processor, final Class<? extends Processor> processorClass) throws ContentNegotiatorException {
|
||||||
ContentType requestedContentType = null;
|
final List<ContentType> supportedContentTypes = getSupportedContentTypes(processor, processorClass);
|
||||||
|
final String acceptHeaderValue = request.getHeader(HttpHeader.ACCEPT);
|
||||||
|
ContentType result = null;
|
||||||
|
|
||||||
List<FormatContentTypeMapping> supportedContentTypes = getSupportedContentTypes(processor, processorClass);
|
if (formatOption != null && formatOption.getFormat() != null) {
|
||||||
|
final String formatString = formatOption.getFormat().trim();
|
||||||
String acceptHeaderValue = request.getHeader(HttpHeader.ACCEPT);
|
final ODataFormat format =
|
||||||
|
ODataFormat.JSON.name().equalsIgnoreCase(formatString) ? ODataFormat.JSON :
|
||||||
boolean supported = false;
|
ODataFormat.XML.name().equalsIgnoreCase(formatString) ? ODataFormat.XML :
|
||||||
|
ODataFormat.ATOM.name().equalsIgnoreCase(formatString) ? ODataFormat.ATOM : null;
|
||||||
if (formatOption != null) {
|
result = getSupportedContentType(format == null ?
|
||||||
if ("json".equalsIgnoreCase(formatOption.getText().trim())) {
|
ContentType.create(formatOption.getFormat()) : format.getContentType(ODataServiceVersion.V40),
|
||||||
requestedContentType = ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
|
supportedContentTypes);
|
||||||
for (FormatContentTypeMapping entry : supportedContentTypes) {
|
if (result == null) {
|
||||||
if (requestedContentType.isCompatible(ContentType.create(entry.getContentType().trim()))) {
|
throw new ContentNegotiatorException("Unsupported $format = " + formatString,
|
||||||
supported = true;
|
ContentNegotiatorException.MessageKeys.UNSUPPORTED_FORMAT_OPTION, formatString);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ("xml".equalsIgnoreCase(formatOption.getText().trim())) {
|
|
||||||
requestedContentType = ContentType.APPLICATION_XML;
|
|
||||||
for (FormatContentTypeMapping entry : supportedContentTypes) {
|
|
||||||
if (requestedContentType.isCompatible(ContentType.create(entry.getContentType().trim()))) {
|
|
||||||
supported = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (FormatContentTypeMapping entry : supportedContentTypes) {
|
|
||||||
if (formatOption.getText().equalsIgnoreCase(entry.getFormatAlias().trim())) {
|
|
||||||
requestedContentType = ContentType.create(entry.getContentType().trim());
|
|
||||||
supported = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!supported) {
|
|
||||||
throw new ContentNegotiatorException("Unsupported $format = " + formatOption.getText(),
|
|
||||||
ContentNegotiatorException.MessageKeys.UNSUPPORTED_FORMAT_OPTION, formatOption.getText());
|
|
||||||
}
|
}
|
||||||
} else if (acceptHeaderValue != null) {
|
} else if (acceptHeaderValue != null) {
|
||||||
List<AcceptType> acceptedContentTypes = AcceptType.create(acceptHeaderValue);
|
final List<AcceptType> acceptedContentTypes = AcceptType.create(acceptHeaderValue);
|
||||||
|
|
||||||
for (AcceptType acceptedType : acceptedContentTypes) {
|
for (AcceptType acceptedType : acceptedContentTypes) {
|
||||||
for (FormatContentTypeMapping supportedType : supportedContentTypes) {
|
for (final ContentType supportedContentType : supportedContentTypes) {
|
||||||
|
ContentType contentType = supportedContentType;
|
||||||
ContentType ct = ContentType.create(supportedType.getContentType());
|
|
||||||
if (acceptedType.getParameters().containsKey("charset")) {
|
if (acceptedType.getParameters().containsKey("charset")) {
|
||||||
String value = acceptedType.getParameters().get("charset");
|
final String value = acceptedType.getParameters().get("charset");
|
||||||
if ("utf8".equalsIgnoreCase(value) || "utf-8".equalsIgnoreCase(value)) {
|
if ("utf8".equalsIgnoreCase(value) || "utf-8".equalsIgnoreCase(value)) {
|
||||||
ct = ContentType.create(ct, ContentType.PARAMETER_CHARSET_UTF8);
|
contentType = ContentType.create(contentType, ContentType.PARAMETER_CHARSET_UTF8);
|
||||||
} else {
|
} else {
|
||||||
throw new ContentNegotiatorException("charset in accept header not supported: " + acceptHeaderValue,
|
throw new ContentNegotiatorException("charset in accept header not supported: " + acceptHeaderValue,
|
||||||
ContentNegotiatorException.MessageKeys.WRONG_CHARSET_IN_HEADER, HttpHeader.ACCEPT, acceptHeaderValue);
|
ContentNegotiatorException.MessageKeys.WRONG_CHARSET_IN_HEADER, HttpHeader.ACCEPT, acceptHeaderValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (acceptedType.matches(contentType)) {
|
||||||
if (acceptedType.matches(ct)) {
|
result = contentType;
|
||||||
requestedContentType = ct;
|
|
||||||
supported = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (supported) {
|
if (result != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (result == null) {
|
||||||
if (!supported) {
|
|
||||||
throw new ContentNegotiatorException(
|
throw new ContentNegotiatorException(
|
||||||
"unsupported accept content type: " + acceptedContentTypes + " != " + supportedContentTypes,
|
"unsupported accept content type: " + acceptedContentTypes + " != " + supportedContentTypes,
|
||||||
ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPES, acceptedContentTypes.toString());
|
ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPES, acceptedContentTypes.toString());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (processorClass == MetadataProcessor.class) {
|
final ContentType requestedContentType = processorClass == MetadataProcessor.class ?
|
||||||
requestedContentType = ContentType.APPLICATION_XML;
|
ODataFormat.XML.getContentType(ODataServiceVersion.V40) :
|
||||||
} else {
|
ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
|
||||||
requestedContentType = ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
|
result = getSupportedContentType(requestedContentType, supportedContentTypes);
|
||||||
}
|
if (result == null) {
|
||||||
|
throw new ContentNegotiatorException(
|
||||||
for (FormatContentTypeMapping entry : supportedContentTypes) {
|
"unsupported accept content type: " + requestedContentType + " != " + supportedContentTypes,
|
||||||
if (requestedContentType.isCompatible(ContentType.create(entry.getContentType().trim()))) {
|
ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPE,
|
||||||
supported = true;
|
requestedContentType.toContentTypeString());
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (!supported) {
|
private static ContentType getSupportedContentType(final ContentType requestedContentType,
|
||||||
throw new ContentNegotiatorException(
|
final List<ContentType> supportedContentTypes) {
|
||||||
"unsupported accept content type: " + requestedContentType + " != " + supportedContentTypes,
|
for (final ContentType supportedContentType : supportedContentTypes) {
|
||||||
ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPE, requestedContentType.toContentTypeString());
|
if (requestedContentType.isCompatible(supportedContentType)) {
|
||||||
|
return supportedContentType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return requestedContentType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.apache.olingo.server.core;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
@ -36,9 +35,8 @@ import org.apache.olingo.commons.api.http.HttpMethod;
|
||||||
import org.apache.olingo.server.api.OData;
|
import org.apache.olingo.server.api.OData;
|
||||||
import org.apache.olingo.server.api.ODataRequest;
|
import org.apache.olingo.server.api.ODataRequest;
|
||||||
import org.apache.olingo.server.api.ODataResponse;
|
import org.apache.olingo.server.api.ODataResponse;
|
||||||
import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
|
|
||||||
import org.apache.olingo.server.api.processor.CustomContentTypeSupportProcessor;
|
import org.apache.olingo.server.api.processor.CustomContentTypeSupportProcessor;
|
||||||
import org.apache.olingo.server.api.processor.FormatContentTypeMapping;
|
import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
|
||||||
import org.apache.olingo.server.api.processor.MetadataProcessor;
|
import org.apache.olingo.server.api.processor.MetadataProcessor;
|
||||||
import org.apache.olingo.server.api.processor.Processor;
|
import org.apache.olingo.server.api.processor.Processor;
|
||||||
import org.apache.olingo.server.api.processor.ServiceDocumentProcessor;
|
import org.apache.olingo.server.api.processor.ServiceDocumentProcessor;
|
||||||
|
@ -60,49 +58,49 @@ public class ContentNegotiatorTest {
|
||||||
//CHECKSTYLE:OFF (Maven checkstyle)
|
//CHECKSTYLE:OFF (Maven checkstyle)
|
||||||
|
|
||||||
String[][] casesServiceDocument = {
|
String[][] casesServiceDocument = {
|
||||||
/* expected $format accept alias ct mapping */
|
/* expected $format accept additional content types */
|
||||||
{ ACCEPT_CASE_MIN, null, null, null ,null },
|
{ ACCEPT_CASE_MIN, null, null, null },
|
||||||
{ ACCEPT_CASE_MIN, "json", null, null ,null },
|
{ ACCEPT_CASE_MIN, "json", null, null },
|
||||||
{ ACCEPT_CASE_MIN, "json", ACCEPT_CASE_JSONQ, null ,null },
|
{ ACCEPT_CASE_MIN, "json", ACCEPT_CASE_JSONQ, null },
|
||||||
{ "a/a", "a", null, "a" ,"a/a" },
|
{ "a/a", "a/a", null, "a/a" },
|
||||||
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_JSONQ, null ,null },
|
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_JSONQ, null },
|
||||||
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_WILDCARD1, null ,null },
|
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_WILDCARD1, null },
|
||||||
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_WILDCARD2, null ,null },
|
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_WILDCARD2, null },
|
||||||
{ "a/a", "a", null, "a, b" ,"a/a,b/b" },
|
{ "a/a", "a/a", null, "a/a,b/b" },
|
||||||
{ "a/a", " a ", null, " a , b" ," a/a , b/b " },
|
{ "a/a", " a/a ", null, " a/a , b/b " },
|
||||||
{ "a/a;x=y", "a", ACCEPT_CASE_WILDCARD1, "a" ,"a/a;x=y" },
|
{ "a/a;x=y", "a/a", ACCEPT_CASE_WILDCARD1, "a/a;x=y" },
|
||||||
{ ACCEPT_CASE_MIN, "json", ACCEPT_CASE_MIN, null ,null },
|
{ ACCEPT_CASE_MIN, "json", ACCEPT_CASE_MIN, null },
|
||||||
{ ACCEPT_CASE_FULL, null, ACCEPT_CASE_FULL, "dummy" ,ACCEPT_CASE_FULL },
|
{ ACCEPT_CASE_FULL, null, ACCEPT_CASE_FULL, ACCEPT_CASE_FULL },
|
||||||
{ ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null ,null },
|
{ ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null },
|
||||||
};
|
};
|
||||||
|
|
||||||
String[][] casesMetadata = {
|
String[][] casesMetadata = {
|
||||||
/* expected $format accept alias ct mapping */
|
/* expected $format accept additional content types */
|
||||||
{ "application/xml", null, null, null ,null },
|
{ ACCEPT_CASE_XML, null, null, null },
|
||||||
{ "application/xml", "xml", null, null ,null },
|
{ ACCEPT_CASE_XML, "xml", null, null },
|
||||||
{ "application/xml", "xml", ACCEPT_CASE_XML, null ,null },
|
{ ACCEPT_CASE_XML, "xml", ACCEPT_CASE_XML, null },
|
||||||
{ "a/a", "a", null, "a" ,"a/a" },
|
{ "a/a", "a/a", null, "a/a" },
|
||||||
{ "application/xml", null, ACCEPT_CASE_XML, null ,null },
|
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_XML, null },
|
||||||
{ "application/xml", null, ACCEPT_CASE_WILDCARD1, null ,null },
|
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_WILDCARD1, null },
|
||||||
{ "application/xml", null, ACCEPT_CASE_WILDCARD2, null ,null },
|
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_WILDCARD2, null },
|
||||||
{ "a/a", "a", null, "a, b" ,"a/a,b/b" },
|
{ "a/a", "a/a", null, "a/a,b/b" },
|
||||||
{ "a/a", " a ", null, " a , b" ," a/a , b/b " },
|
{ "a/a", " a/a ", null, " a/a , b/b " },
|
||||||
{ "a/a;x=y", "a", ACCEPT_CASE_WILDCARD1, "a" ,"a/a;x=y" },
|
{ "a/a;x=y", "a/a", ACCEPT_CASE_WILDCARD1, "a/a;x=y" },
|
||||||
};
|
};
|
||||||
|
|
||||||
String[][] casesFail = {
|
String[][] casesFail = {
|
||||||
/* expected $format accept alias ct mapping */
|
/* expected $format accept additional content types */
|
||||||
{ "application/xml", "xxx", null, null ,null },
|
{ ACCEPT_CASE_XML, "xxx/yyy", null, null },
|
||||||
{ "a/a", "a", null, "b" ,"b/b" },
|
{ "a/a", "a/a", null, "b/b" },
|
||||||
{ "application/xml", null, ACCEPT_CASE_JSONQ, null ,null },
|
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_JSONQ, null },
|
||||||
{ "application/json", null, ACCEPT_CASE_FULL, null ,null }, // not jet supported
|
{ "application/json", null, ACCEPT_CASE_FULL, null }, // not yet supported
|
||||||
};
|
};
|
||||||
//CHECKSTYLE:ON
|
//CHECKSTYLE:ON
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServiceDocumentSingleCase() throws Exception {
|
public void testServiceDocumentSingleCase() throws Exception {
|
||||||
String[] useCase = { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null, null };
|
String[] useCase = { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null };
|
||||||
|
|
||||||
testContentNegotiation(useCase, ServiceDocumentProcessor.class);
|
testContentNegotiation(useCase, ServiceDocumentProcessor.class);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +114,7 @@ public class ContentNegotiatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMetadataSingleCase() throws Exception {
|
public void testMetadataSingleCase() throws Exception {
|
||||||
String[] useCase = { "application/xml", null, null, null, null };
|
String[] useCase = { ACCEPT_CASE_XML, null, null, null };
|
||||||
|
|
||||||
testContentNegotiation(useCase, MetadataProcessor.class);
|
testContentNegotiation(useCase, MetadataProcessor.class);
|
||||||
}
|
}
|
||||||
|
@ -129,86 +127,77 @@ public class ContentNegotiatorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMetadataFail() {
|
public void testMetadataFail() throws Exception {
|
||||||
for (String[] useCase : casesFail) {
|
for (String[] useCase : casesFail) {
|
||||||
try {
|
try {
|
||||||
testContentNegotiation(useCase, MetadataProcessor.class);
|
testContentNegotiation(useCase, MetadataProcessor.class);
|
||||||
fail("Exeption expected!");
|
fail("Exception expected!");
|
||||||
} catch (ContentNegotiatorException e) {
|
} catch (final ContentNegotiatorException e) {}
|
||||||
|
|
||||||
}catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("Wrong Exception: " + e.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testContentNegotiation(final String[] useCase, final Class<? extends Processor> processorClass)
|
private void testContentNegotiation(final String[] useCase, final Class<? extends Processor> processorClass)
|
||||||
throws Exception {
|
throws ContentNegotiatorException {
|
||||||
ODataRequest request = new ODataRequest();
|
ODataRequest request = new ODataRequest();
|
||||||
request.setMethod(HttpMethod.GET);
|
request.setMethod(HttpMethod.GET);
|
||||||
request.setRawODataPath("/" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
|
request.setRawODataPath("/" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
|
||||||
|
|
||||||
ProcessorStub p = new ProcessorStub(createCustomContentTypeMapping(useCase[3], useCase[4]));
|
ProcessorStub p = new ProcessorStub(createCustomContentTypes(useCase[3]));
|
||||||
|
|
||||||
FormatOption fo = null;
|
FormatOption fo = null;
|
||||||
if (useCase[1] != null) {
|
if (useCase[1] != null) {
|
||||||
fo = mock(FormatOption.class);
|
fo = mock(FormatOption.class);
|
||||||
when(fo.getText()).thenReturn(useCase[1].trim());
|
when(fo.getFormat()).thenReturn(useCase[1].trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useCase[2] != null) {
|
if (useCase[2] != null) {
|
||||||
request.addHeader(HttpHeader.ACCEPT, Arrays.asList(useCase[2]));
|
request.addHeader(HttpHeader.ACCEPT, Arrays.asList(useCase[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentType requestedContentType = ContentNegotiator.doContentNegotiation(fo, request, p, processorClass);
|
final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(fo, request, p, processorClass);
|
||||||
|
|
||||||
assertNotNull(requestedContentType);
|
assertNotNull(requestedContentType);
|
||||||
assertEquals(ContentType.create(useCase[0]), requestedContentType);
|
assertEquals(ContentType.create(useCase[0]), requestedContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FormatContentTypeMapping> createCustomContentTypeMapping(final String formatString,
|
private List<ContentType> createCustomContentTypes(final String contentTypeString) {
|
||||||
final String contentTypeString) {
|
|
||||||
List<FormatContentTypeMapping> map = null;
|
|
||||||
|
|
||||||
assertTrue(!(formatString == null ^ contentTypeString == null));
|
if (contentTypeString == null) {
|
||||||
|
return null;
|
||||||
if (formatString != null) {
|
|
||||||
String[] formats = formatString.split(",");
|
|
||||||
String[] contentTypes = contentTypeString.split(",");
|
|
||||||
|
|
||||||
assertEquals(formats.length, contentTypes.length);
|
|
||||||
|
|
||||||
map = new ArrayList<FormatContentTypeMapping>();
|
|
||||||
for (int i = 0; i < formats.length; i++) {
|
|
||||||
map.add(new FormatContentTypeMapping(formats[i], contentTypes[i]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProcessorStub implements ServiceDocumentProcessor, MetadataProcessor,
|
private class ProcessorStub implements ServiceDocumentProcessor, MetadataProcessor,
|
||||||
EntityCollectionProcessor,
|
EntityCollectionProcessor, CustomContentTypeSupportProcessor {
|
||||||
CustomContentTypeSupportProcessor {
|
|
||||||
|
|
||||||
List<FormatContentTypeMapping> customMapping;
|
List<ContentType> customTypes;
|
||||||
|
|
||||||
ProcessorStub(final List<FormatContentTypeMapping> mapping) {
|
ProcessorStub(final List<ContentType> types) {
|
||||||
customMapping = mapping;
|
customTypes = types;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(final OData odata, final Edm edm) {}
|
public void init(final OData odata, final Edm edm) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FormatContentTypeMapping> modifySupportedContentTypes(
|
public List<ContentType> modifySupportedContentTypes(final List<ContentType> supportedContentTypes,
|
||||||
final List<FormatContentTypeMapping> supportedContentTypes,
|
|
||||||
final Class<? extends Processor> processorClass) {
|
final Class<? extends Processor> processorClass) {
|
||||||
if (customMapping != null) {
|
if (customTypes == null) {
|
||||||
supportedContentTypes.addAll(customMapping);
|
return supportedContentTypes;
|
||||||
|
} else {
|
||||||
|
List<ContentType> modifiedTypes = new ArrayList<ContentType>(supportedContentTypes);
|
||||||
|
modifiedTypes.addAll(customTypes);
|
||||||
|
return modifiedTypes;
|
||||||
}
|
}
|
||||||
return supportedContentTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class ODataHandlerTest {
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
|
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(500, response.getStatusCode());
|
assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -92,7 +92,7 @@ public class ODataHandlerTest {
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
|
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(200, response.getStatusCode());
|
assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
|
||||||
|
|
||||||
String ct = response.getHeaders().get(HttpHeader.CONTENT_TYPE);
|
String ct = response.getHeaders().get(HttpHeader.CONTENT_TYPE);
|
||||||
assertTrue(ct.contains("application/json"));
|
assertTrue(ct.contains("application/json"));
|
||||||
|
@ -134,7 +134,7 @@ public class ODataHandlerTest {
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
|
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(500, response.getStatusCode());
|
assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -147,7 +147,7 @@ public class ODataHandlerTest {
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
|
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(200, response.getStatusCode());
|
assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
|
||||||
assertEquals(HttpContentType.APPLICATION_XML, response.getHeaders().get(HttpHeader.CONTENT_TYPE));
|
assertEquals(HttpContentType.APPLICATION_XML, response.getHeaders().get(HttpHeader.CONTENT_TYPE));
|
||||||
|
|
||||||
assertNotNull(response.getContent());
|
assertNotNull(response.getContent());
|
||||||
|
@ -195,7 +195,7 @@ public class ODataHandlerTest {
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
|
|
||||||
assertEquals(ODataServiceVersion.V40.toString(), response.getHeaders().get(HttpHeader.ODATA_VERSION));
|
assertEquals(ODataServiceVersion.V40.toString(), response.getHeaders().get(HttpHeader.ODATA_VERSION));
|
||||||
assertEquals(400, response.getStatusCode());
|
assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -208,7 +208,7 @@ public class ODataHandlerTest {
|
||||||
|
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(200, response.getStatusCode());
|
assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -217,11 +217,11 @@ public class ODataHandlerTest {
|
||||||
|
|
||||||
request.setMethod(HttpMethod.GET);
|
request.setMethod(HttpMethod.GET);
|
||||||
request.setRawODataPath("$metadata");
|
request.setRawODataPath("$metadata");
|
||||||
request.setRawQueryPath("$format=notSupported");
|
request.setRawQueryPath("$format=not/Supported");
|
||||||
|
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(406, response.getStatusCode());
|
assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -233,7 +233,7 @@ public class ODataHandlerTest {
|
||||||
|
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(501, response.getStatusCode());
|
assertEquals(HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -265,7 +265,7 @@ public class ODataHandlerTest {
|
||||||
|
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(404, response.getStatusCode());
|
assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Use this test
|
//TODO: Use this test
|
||||||
|
@ -279,7 +279,7 @@ public class ODataHandlerTest {
|
||||||
|
|
||||||
ODataResponse response = handler.process(request);
|
ODataResponse response = handler.process(request);
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(404, response.getStatusCode());
|
assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -301,7 +301,7 @@ public class ODataHandlerTest {
|
||||||
|
|
||||||
ODataResponse response = localHandler.process(request);
|
ODataResponse response = localHandler.process(request);
|
||||||
assertNotNull(response);
|
assertNotNull(response);
|
||||||
assertEquals(500, response.getStatusCode());
|
assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode());
|
||||||
// TODO: Check for message in case of EdmException
|
// TODO: Check for message in case of EdmException
|
||||||
// System.out.println(IOUtils.toString(response.getContent()));
|
// System.out.println(IOUtils.toString(response.getContent()));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue