[OLINGO-328] refactor content negotiation

This commit is contained in:
Stephan Klevenz 2014-06-27 17:33:20 +02:00
parent 0ffc26d1b2
commit acc12ff742
15 changed files with 576 additions and 228 deletions

View File

@ -46,6 +46,11 @@
<version>2.5</version> <version>2.5</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -18,8 +18,6 @@
*/ */
package org.apache.olingo.server.api; package org.apache.olingo.server.api;
import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;

View File

@ -54,7 +54,7 @@ public class ODataRequest {
String key = name.toUpperCase(); String key = name.toUpperCase();
if (headers.containsKey(key)) { if (headers.containsKey(key)) {
List<String> oldValues = headers.get(key); List<String> oldValues = headers.get(key);
List<String> newValues = new ArrayList<String>(); List<String> newValues = new ArrayList<String>();
newValues.addAll(oldValues); newValues.addAll(oldValues);
newValues.addAll(values); newValues.addAll(values);

View File

@ -20,8 +20,9 @@ package org.apache.olingo.server.api.processor;
import java.util.List; import java.util.List;
public interface SupportCustomContentTypes { public interface CustomContentTypeSupport {
public List<FormatContentTypeMapping> modifySupportedContentTypes(
List<FormatContentTypeMapping> supportedContentTypes, Class<? extends Processor> processorClass);
public List<String> getSupportedContentTypes(Class<? extends Processor> processorClass);
} }

View File

@ -19,8 +19,6 @@
package org.apache.olingo.server.api.processor; package org.apache.olingo.server.api.processor;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.api.format.ODataFormat;
@ -33,7 +31,7 @@ import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfo;
public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProcessor { public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProcessor {
private OData odata; private OData odata;
private Edm edm; private Edm edm;

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.api.processor;
public class FormatContentTypeMapping {
private String formatAlias;
private String contentType;
public FormatContentTypeMapping(String formatAlias, String contentType) {
super();
this.formatAlias = formatAlias;
this.contentType = contentType;
}
public String getFormatAlias() {
return formatAlias;
}
public String getContentType() {
return contentType;
}
public void setFormatAlias(String formatAlias) {
this.formatAlias = formatAlias;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
@Override
public String toString() {
return "('" + formatAlias + "', '" + contentType + "')";
}
}

View File

@ -22,7 +22,7 @@ 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.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfo;
public interface MetadataProcessor extends Processor{ public interface MetadataProcessor extends Processor {
void readMetadata(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format); void readMetadata(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format);
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.apache.olingo.server.api.processor; package org.apache.olingo.server.api.processor;
import java.util.List;
import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.OData;

View File

@ -0,0 +1,132 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.core;
import java.util.ArrayList;
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.server.api.ODataRequest;
import org.apache.olingo.server.api.processor.CustomContentTypeSupport;
import org.apache.olingo.server.api.processor.FormatContentTypeMapping;
import org.apache.olingo.server.api.processor.MetadataProcessor;
import org.apache.olingo.server.api.processor.Processor;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ContentNegotiator {
private final static Logger LOG = LoggerFactory.getLogger(ContentNegotiator.class);
private List<FormatContentTypeMapping> getDefaultSupportedContentTypes(Class<? extends Processor> processorClass) {
List<FormatContentTypeMapping> defaults = new ArrayList<FormatContentTypeMapping>();
if (processorClass == MetadataProcessor.class) {
defaults.add(new FormatContentTypeMapping("xml", ContentType.APPLICATION_XML.toContentTypeString()));
}
else {
defaults.add(new FormatContentTypeMapping("json", ContentType.APPLICATION_JSON.toContentTypeString()));
}
return defaults;
}
public List<FormatContentTypeMapping> getSupportedContentTypes(Processor processor,
Class<? extends Processor> processorClass) {
List<FormatContentTypeMapping> supportedContentTypes = getDefaultSupportedContentTypes(processorClass);
if (processor instanceof CustomContentTypeSupport) {
supportedContentTypes =
((CustomContentTypeSupport) processor).modifySupportedContentTypes(supportedContentTypes, processorClass);
}
return supportedContentTypes;
}
public String doContentNegotiation(FormatOption formatOption, ODataRequest request,
List<FormatContentTypeMapping> supportedContentTypes) {
String requestedContentType = null;
List<String> acceptHeaderValues = request.getHeader(HttpHeader.ACCEPT);
boolean supported = false;
if (formatOption != null) {
if ("json".equalsIgnoreCase(formatOption.getText())) {
requestedContentType = HttpContentType.APPLICATION_JSON;
for (FormatContentTypeMapping entry : supportedContentTypes) {
if (requestedContentType.equalsIgnoreCase(entry.getContentType())){
supported = true;
break;
}
}
} else {
requestedContentType = formatOption.getText();
for (FormatContentTypeMapping entry : supportedContentTypes) {
if (requestedContentType.equalsIgnoreCase(entry.getFormatAlias())){
supported = true;
break;
}
}
}
} else if (acceptHeaderValues != null) {
List<String> acceptedContentTypes = new ArrayList<String>();
// for (String acceptHeaderValue : acceptHeaderValues) {
// acceptedContentTypes.addAll(parseAcceptHeader(acceptHeaderValue));
// }
for (String acceptedContentType : acceptedContentTypes) {
// if (isContentTypeSupported(acceptedContentType, supportedContentTypes)) {
// requestedContentType = acceptedContentType;
// }
}
if (requestedContentType == null) {
throw new RuntimeException("unsupported accept content type: " + acceptedContentTypes + " != "
+ supportedContentTypes);
}
requestedContentType = null;
} else {
requestedContentType = HttpContentType.APPLICATION_JSON;
for (FormatContentTypeMapping entry : supportedContentTypes) {
if (requestedContentType.equalsIgnoreCase(entry.getContentType())){
supported = true;
break;
}
}
}
if (!supported) {
throw new RuntimeException("unsupported accept content type: " + requestedContentType + " != "
+ supportedContentTypes);
}
LOG.debug("requested content type: " + requestedContentType);
return requestedContentType;
}
}

View File

@ -18,6 +18,10 @@
*/ */
package org.apache.olingo.server.core; package org.apache.olingo.server.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -25,6 +29,8 @@ import java.util.Map;
import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.api.format.AcceptType;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpContentType; import org.apache.olingo.commons.api.http.HttpContentType;
import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod; import org.apache.olingo.commons.api.http.HttpMethod;
@ -32,20 +38,27 @@ 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.CollectionProcessor; import org.apache.olingo.server.api.processor.CollectionProcessor;
import org.apache.olingo.server.api.processor.FormatContentTypeMapping;
import org.apache.olingo.server.api.processor.DefaultProcessor; import org.apache.olingo.server.api.processor.DefaultProcessor;
import org.apache.olingo.server.api.processor.EntityProcessor; import org.apache.olingo.server.api.processor.EntityProcessor;
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;
import org.apache.olingo.server.api.processor.CustomContentTypeSupport;
import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriResource; import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.UriResourcePartTyped; import org.apache.olingo.server.api.uri.UriResourcePartTyped;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
import org.apache.olingo.server.core.uri.parser.Parser; import org.apache.olingo.server.core.uri.parser.Parser;
import org.apache.olingo.server.core.uri.validator.UriValidator; import org.apache.olingo.server.core.uri.validator.UriValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ODataHandler { public class ODataHandler {
private final static Logger LOG = LoggerFactory.getLogger(ODataHandler.class);
private final OData odata; private final OData odata;
private final Edm edm; private final Edm edm;
private Map<Class<? extends Processor>, Processor> processors = new HashMap<Class<? extends Processor>, Processor>(); private Map<Class<? extends Processor>, Processor> processors = new HashMap<Class<? extends Processor>, Processor>();
@ -72,12 +85,17 @@ public class ODataHandler {
UriValidator validator = new UriValidator(); UriValidator validator = new UriValidator();
validator.validate(uriInfo, request.getMethod()); validator.validate(uriInfo, request.getMethod());
String requestedContentType = doContentNegotiation(); String requestedContentType = null;
List<FormatContentTypeMapping> supportedContentTypes = null;
switch (uriInfo.getKind()) { switch (uriInfo.getKind()) {
case metadata: case metadata:
MetadataProcessor mp = selectProcessor(MetadataProcessor.class); MetadataProcessor mp = selectProcessor(MetadataProcessor.class);
mp.readMetadata(request, response, uriInfo, HttpContentType.APPLICATION_XML);
/* TODO content negotiation */
requestedContentType = ContentType.APPLICATION_XML.toContentTypeString();
mp.readMetadata(request, response, uriInfo, requestedContentType);
break; break;
case service: case service:
if ("".equals(request.getRawODataPath())) { if ("".equals(request.getRawODataPath())) {
@ -85,11 +103,15 @@ public class ODataHandler {
rdp.redirect(request, response); rdp.redirect(request, response);
} else { } else {
ServiceDocumentProcessor sdp = selectProcessor(ServiceDocumentProcessor.class); ServiceDocumentProcessor sdp = selectProcessor(ServiceDocumentProcessor.class);
/* TODO content negotiation */
requestedContentType = ContentType.APPLICATION_JSON.toContentTypeString();
sdp.readServiceDocument(request, response, uriInfo, requestedContentType); sdp.readServiceDocument(request, response, uriInfo, requestedContentType);
} }
break; break;
case resource: case resource:
handleResourceDispatching(request, response, uriInfo, requestedContentType); handleResourceDispatching(request, response, uriInfo);
break; break;
default: default:
throw new ODataRuntimeException("not implemented"); throw new ODataRuntimeException("not implemented");
@ -102,27 +124,32 @@ public class ODataHandler {
} }
} }
private String doContentNegotiation() { private void handleResourceDispatching(final ODataRequest request, ODataResponse response, UriInfo uriInfo) {
// TODO: Content Negotiation
return HttpContentType.APPLICATION_JSON;
}
private void handleResourceDispatching(final ODataRequest request, ODataResponse response, UriInfo uriInfo,
String requestedContentType) {
int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1; int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1;
UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex); UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex);
String requestedContentType = null;
List<String> supportedContentTypes = null;
switch (lastPathSegment.getKind()) { switch (lastPathSegment.getKind()) {
case entitySet: case entitySet:
if (((UriResourcePartTyped) lastPathSegment).isCollection()) { if (((UriResourcePartTyped) lastPathSegment).isCollection()) {
if (request.getMethod().equals(HttpMethod.GET)) { if (request.getMethod().equals(HttpMethod.GET)) {
CollectionProcessor esp = selectProcessor(CollectionProcessor.class); CollectionProcessor cp = selectProcessor(CollectionProcessor.class);
esp.readCollection(request, response, uriInfo, requestedContentType);
/* TODO content negotiation */
requestedContentType = ContentType.APPLICATION_JSON.toContentTypeString();
cp.readCollection(request, response, uriInfo, requestedContentType);
} else { } else {
throw new ODataRuntimeException("not implemented"); throw new ODataRuntimeException("not implemented");
} }
} else { } else {
if (request.getMethod().equals(HttpMethod.GET)) { if (request.getMethod().equals(HttpMethod.GET)) {
EntityProcessor ep = selectProcessor(EntityProcessor.class); EntityProcessor ep = selectProcessor(EntityProcessor.class);
/* TODO content negotiation */
requestedContentType = ContentType.APPLICATION_JSON.toContentTypeString();
ep.readEntity(request, response, uriInfo, requestedContentType); ep.readEntity(request, response, uriInfo, requestedContentType);
} else { } else {
throw new ODataRuntimeException("not implemented"); throw new ODataRuntimeException("not implemented");
@ -132,14 +159,22 @@ public class ODataHandler {
case navigationProperty: case navigationProperty:
if (((UriResourceNavigation) lastPathSegment).isCollection()) { if (((UriResourceNavigation) lastPathSegment).isCollection()) {
if (request.getMethod().equals(HttpMethod.GET)) { if (request.getMethod().equals(HttpMethod.GET)) {
CollectionProcessor esp = selectProcessor(CollectionProcessor.class); CollectionProcessor cp = selectProcessor(CollectionProcessor.class);
esp.readCollection(request, response, uriInfo, requestedContentType);
/* TODO content negotiation */
requestedContentType = ContentType.APPLICATION_JSON.toContentTypeString();
cp.readCollection(request, response, uriInfo, requestedContentType);
} else { } else {
throw new ODataRuntimeException("not implemented"); throw new ODataRuntimeException("not implemented");
} }
} else { } else {
if (request.getMethod().equals(HttpMethod.GET)) { if (request.getMethod().equals(HttpMethod.GET)) {
EntityProcessor ep = selectProcessor(EntityProcessor.class); EntityProcessor ep = selectProcessor(EntityProcessor.class);
/* TODO content negotiation */
requestedContentType = ContentType.APPLICATION_JSON.toContentTypeString();
ep.readEntity(request, response, uriInfo, requestedContentType); ep.readEntity(request, response, uriInfo, requestedContentType);
} else { } else {
throw new ODataRuntimeException("not implemented"); throw new ODataRuntimeException("not implemented");

View File

@ -182,7 +182,7 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
odRequest.setRawServiceResolutionUri(rawServiceResolutionUri); odRequest.setRawServiceResolutionUri(rawServiceResolutionUri);
} }
private void extractHeaders(ODataRequest odRequest, final HttpServletRequest req) { static void extractHeaders(ODataRequest odRequest, final HttpServletRequest req) {
for (Enumeration<?> headerNames = req.getHeaderNames(); headerNames.hasMoreElements();) { for (Enumeration<?> headerNames = req.getHeaderNames(); headerNames.hasMoreElements();) {
String headerName = (String) headerNames.nextElement(); String headerName = (String) headerNames.nextElement();
List<String> headerValues = new ArrayList<String>(); List<String> headerValues = new ArrayList<String>();

View File

@ -0,0 +1,327 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.core;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.olingo.commons.api.edm.Edm;
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.processor.CollectionProcessor;
import org.apache.olingo.server.api.processor.CustomContentTypeSupport;
import org.apache.olingo.server.api.processor.FormatContentTypeMapping;
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.uri.queryoption.FormatOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ContentNegotiatorTest {
static final private String ACCEPT_CASE_JSON = "application/json;odata=verbose;q=0.2";
static final private String ACCEPT_CASE_XML = "application/xml";
static final private String ACCEPT_CASE_TEXT = "text/plain;q=0.5";
static final private String ACCEPT_CASE_MULTI = "text/plain;q=0.5,application/aaa;q=0.4";
//@formatter:off (Eclipse formatter)
//CHECKSTYLE:OFF (Maven checkstyle)
String[][] casesServiceDocument = {
/* expected $format accept alias ct mapping */
{ "application/json", null, null, null ,null },
{ "application/json", "json", null, null ,null },
{ "application/json", "json", "application/json", null ,null },
{ "a", "a", null, "a" ,"a/a" },
// { "application/json", null, "application/json", null ,null },
// { "application/json", null, ACCEPT_CASE_JSON, null ,null },
// { "application/json", null, "*/*", null ,null },
// { "a/a", "a", null, "a, b" ,"a/a,b/b" },
// { "a", null, "*/*", "a, b" ,null },
// { "a", "a", "*/*", "a, b" ,null },
};
String[][] casesMetadata = {
/* expected $format accept alias ct mapping */
{ "application/xml", null, null, null ,null },
{ "application/xml", "xml", null, null ,null },
{ "application/xml", null, "application/xml", null ,null },
{ "application/xml", "xml", "application/xml", null ,null },
{ "application/xml", null, ACCEPT_CASE_XML, null ,null },
{ "application/xml", null, "*/*", null ,null },
{ "a", "a", null, "a, b" ,null },
{ "a", "a", null, "a, b" ,null },
{ "a", null, "*/*", "a, b" ,null },
{ "a", "a", "*/*", "a, b" ,null },
};
// String[][] casesEntitySet = {
// /* expected $format accept supported $formatmapping */
// { "application/json", null, null, null ,null },
// { "application/json", "json", null, null ,null },
// { "application/json", "json", "application/json", null ,null },
// { "application/json", null, "application/json", null ,null },
// { "application/json", null, ACCEPT_CASE_JSON, null ,null },
// { "application/json", null, "*/*", null ,null },
// { "a", "a", null, "a, b" ,null },
// { "a", null, "*/*", "a, b" ,null },
// { "a", "a", "*/*", "a, b" ,null },
// };
//CHECKSTYLE:ON
//@formatter:on
private final static Logger LOG = LoggerFactory.getLogger(ContentNegotiatorTest.class);
@Test
public void testServiceDocumentSingleCase() {
String[] useCase = { "application/json", null, null, null, null };
testContentNegotiation(useCase, ServiceDocumentProcessor.class);
}
@Test
public void testServiceDocumentDefault() {
for (String[] useCase : casesServiceDocument) {
testContentNegotiation(useCase, ServiceDocumentProcessor.class);
}
}
public void testContentNegotiation(String[] useCase, Class<ServiceDocumentProcessor> processorClass) {
LOG.debug(Arrays.asList(useCase).toString());
ODataRequest request = new ODataRequest();
request.setMethod(HttpMethod.GET);
request.setRawODataPath("/" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
ContentNegotiator cn = new ContentNegotiator();
ProcessorStub p = new ProcessorStub(createCustomContentTypeMapping(useCase[3], useCase[4]));
List<FormatContentTypeMapping> supportedContentTypes =
cn.getSupportedContentTypes(p, processorClass);
FormatOption fo = null;
if (useCase[1] != null) {
fo = mock(FormatOption.class);
when(fo.getText()).thenReturn(useCase[1]);
}
String requestedContentType = cn.doContentNegotiation(fo, request, supportedContentTypes);
assertNotNull(requestedContentType);
assertEquals(useCase[0], requestedContentType);
}
private List<FormatContentTypeMapping> createCustomContentTypeMapping(String formatString, String contentTypeString) {
List<FormatContentTypeMapping> map = null;
assertTrue(!(formatString == null ^ contentTypeString == 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;
}
@Test
@Ignore
public void testMetadataDefault() {
for (String[] useCase : casesMetadata) {
ODataRequest request = new ODataRequest();
request.setMethod(HttpMethod.GET);
request.setRawODataPath("/$metadata" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
// ODataResponse response = callHandler(useCase, request);
//
// assertEquals(useCase[0], response.getHeaders().get(HttpHeader.CONTENT_TYPE));
}
}
// @Test
// public void testEntitySet() {
//
// for (String[] useCase : casesEntitySet) {
// ODataRequest request = new ODataRequest();
// request.setMethod(HttpMethod.GET);
// request.setRawODataPath("/ESAllPrim" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
//
// ODataResponse response = callHandler(useCase, request, new CollectionProcessorStub());
//
// assertEquals(useCase[0], response.getHeaders().get(HttpHeader.CONTENT_TYPE));
// }
// }
// private ODataResponse callHandler(String[] useCase, ODataRequest request,
// Processor defaultProcessor) {
// ODataHandler handler = createHandler();
//
// if (useCase[2] != null) {
// request.addHeader(HttpHeader.ACCEPT, Arrays.asList(useCase[2]));
// }
//
// if (useCase[3] != null) {
// String[] aliase = useCase[3].split(",");
// String[] mappings = useCase[4].split(",");
//
// FormatContentTypeMapping[] formatCTMap = new FormatContentTypeMapping[aliase.length];
//
// for(int i=0; i< formatCTMap.length; i++) {
// formatCTMap[i] = new FormatContentTypeMapping(aliase[i], mappings[i]);
// }
//
//
// ProcessorStub stub = new ProcessorStub(formatCTMap);
// handler.register(stub);
// } else {
// if (defaultProcessor != null) {
// handler.register(defaultProcessor);
// }
// }
//
// ODataResponse response = handler.process(request);
// return response;
// }
// ODataResponse callHandler(String[] useCase, ODataRequest request) {
// return callHandler(useCase, request, null);
// }
private class CollectionProcessorStub implements CollectionProcessor {
@Override
public void init(OData odata, Edm edm) {}
@Override
public void readCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
}
@Test
public void testDefaultSupportedContentTypesServiceDocument() {
ContentNegotiator cn = new ContentNegotiator();
ProcessorStub p = new ProcessorStub(null);
List<FormatContentTypeMapping> supportedContentTypes =
cn.getSupportedContentTypes(p, ServiceDocumentProcessor.class);
assertNotNull(supportedContentTypes);
assertEquals(1, supportedContentTypes.size());
assertEquals("json", supportedContentTypes.get(0).getFormatAlias());
assertEquals("application/json", supportedContentTypes.get(0).getContentType());
}
@Test
public void testDefaultSupportedContentTypesMetadata() {
ContentNegotiator cn = new ContentNegotiator();
ProcessorStub p = new ProcessorStub(null);
List<FormatContentTypeMapping> supportedContentTypes = cn.getSupportedContentTypes(p, MetadataProcessor.class);
assertNotNull(supportedContentTypes);
assertEquals(1, supportedContentTypes.size());
assertEquals("xml", supportedContentTypes.get(0).getFormatAlias());
assertEquals("application/xml", supportedContentTypes.get(0).getContentType());
}
@Test
public void testCustomSupportedContentTypesServiceDocument() {
ContentNegotiator cn = new ContentNegotiator();
ProcessorStub p = new ProcessorStub(Arrays.asList(new FormatContentTypeMapping("a", "a/a")));
List<FormatContentTypeMapping> supportedContentTypes =
cn.getSupportedContentTypes(p, ServiceDocumentProcessor.class);
assertNotNull(supportedContentTypes);
assertEquals(2, supportedContentTypes.size());
assertEquals("json", supportedContentTypes.get(0).getFormatAlias());
assertEquals("application/json", supportedContentTypes.get(0).getContentType());
assertEquals("a", supportedContentTypes.get(1).getFormatAlias());
assertEquals("a/a", supportedContentTypes.get(1).getContentType());
}
private class ProcessorStub implements ServiceDocumentProcessor, MetadataProcessor,
CollectionProcessor,
CustomContentTypeSupport {
List<FormatContentTypeMapping> customMapping;
ProcessorStub(List<FormatContentTypeMapping> mapping) {
this.customMapping = mapping;
}
@Override
public void init(OData odata, Edm edm) {}
@Override
public List<FormatContentTypeMapping> modifySupportedContentTypes(
List<FormatContentTypeMapping> supportedContentTypes,
Class<? extends Processor> processorClass) {
if (customMapping != null) {
supportedContentTypes.addAll(customMapping);
}
return supportedContentTypes;
}
@Override
public void readServiceDocument(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
@Override
public void readCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
@Override
public void readMetadata(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
}
}

View File

@ -23,6 +23,8 @@ 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;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.ODataRuntimeException;

View File

@ -1,201 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.core;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import java.util.Arrays;
import java.util.List;
import org.apache.olingo.commons.api.edm.Edm;
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.processor.CollectionProcessor;
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.processor.SupportCustomContentTypes;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
import org.junit.Ignore;
import org.junit.Test;
public class ContentNegotiationTest {
// static final private String ACCEPT_CASE1 = "text/plain;q=0.5";
// static final private String ACCEPT_CASE2 = "application/json;odata=verbose;q=0.2";
//@formatter:off (Eclipse formatter)
//CHECKSTYLE:OFF (Maven checkstyle)
String[][] casesServiceDocument = {
/* expected $format accept supported */
{ "application/json", null, null, null },
{ "application/json", "json", null, null },
{ "application/json", "json", "application/json", null },
{ "application/json", null, "application/json", null },
{ "application/json", null, "*/*", null },
// { "aaa", "aaa", null, "aaa, bbb" },
// { "aaa", null, "*/*", "aaa, bbb" },
};
String[][] casesMetadata = {
/* expected $format accept supported */
{ "application/xml", null, null, null },
{ "application/xml", "xml", null, null },
{ "application/xml", null, "application/xml", null },
{ "application/xml", "xml", "application/xml", null },
{ "application/xml", null, "*/*", null },
// { "aaa", "aaa", null, "aaa, bbb" },
};
String[][] casesEntitySet = {
/* expected $format accept supported */
{ "application/json", null, null, null },
{ "application/json", "json", null, null },
{ "application/json", "json", "application/json", null },
{ "application/json", null, "*/*", null },
};
//CHECKSTYLE:ON
//@formatter:on
private ODataHandler createHandler() {
OData odata = OData.newInstance();
Edm edm = odata.createEdm(new EdmTechProvider());
return new ODataHandler(odata, edm);
}
@Test
public void testServiceDocumentDefault() {
for (String[] useCase : casesServiceDocument) {
ODataRequest request = new ODataRequest();
request.setMethod(HttpMethod.GET);
request.setRawODataPath("/" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
ODataResponse response = callHandler(useCase, request);
assertEquals(useCase[0], response.getHeaders().get(HttpHeader.CONTENT_TYPE));
}
}
@Test
public void testMetadataDefault() {
for (String[] useCase : casesMetadata) {
ODataRequest request = new ODataRequest();
request.setMethod(HttpMethod.GET);
request.setRawODataPath("/$metadata" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
ODataResponse response = callHandler(useCase, request);
assertEquals(useCase[0], response.getHeaders().get(HttpHeader.CONTENT_TYPE));
}
}
@Test
public void testEntitySet() {
for (String[] useCase : casesEntitySet) {
ODataRequest request = new ODataRequest();
request.setMethod(HttpMethod.GET);
request.setRawODataPath("/ESAllPrim" + (useCase[1] == null ? "" : "?$format=" + useCase[1]));
ODataResponse response = callHandler(useCase, request, new CollectionProcessorStub());
assertEquals(useCase[0], response.getHeaders().get(HttpHeader.CONTENT_TYPE));
}
}
private ODataResponse callHandler(String[] useCase, ODataRequest request,
Processor defaultProcessor) {
ODataHandler handler = createHandler();
if (useCase[3] != null) {
ProcessorStub stub = new ProcessorStub(useCase[3].split(","));
handler.register(stub);
} else {
if (defaultProcessor != null) {
handler.register(defaultProcessor);
}
}
ODataResponse response = handler.process(request);
return response;
}
ODataResponse callHandler(String[] useCase, ODataRequest request) {
return callHandler(useCase, request, null);
}
private class ProcessorStub implements ServiceDocumentProcessor, MetadataProcessor, CollectionProcessor,
SupportCustomContentTypes {
String[] formats;
ProcessorStub(String[] strings) {
this.formats = strings;
}
@Override
public void init(OData odata, Edm edm) {}
@Override
public List<String> getSupportedContentTypes(Class<? extends Processor> processorClass) {
return Arrays.asList(formats);
}
@Override
public void readServiceDocument(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
@Override
public void readCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
@Override
public void readMetadata(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
}
private class CollectionProcessorStub implements CollectionProcessor {
@Override
public void init(OData odata, Edm edm) {}
@Override
public void readCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) {
response.setHeader(HttpHeader.CONTENT_TYPE, format);
}
}
}