You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2013/09/11 08:49:12 UTC
git commit: Refactored content negotiation from RequestHandler into
ContentNegotiator
Updated Branches:
refs/heads/master 060f9c656 -> 31c3d8fca
Refactored content negotiation from RequestHandler into ContentNegotiator
Project: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/commit/31c3d8fc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/tree/31c3d8fc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/diff/31c3d8fc
Branch: refs/heads/master
Commit: 31c3d8fca024eed8c2066c3dddc9218b77033aa0
Parents: 060f9c6
Author: Michael Bolz <mi...@apache.org>
Authored: Wed Sep 11 03:11:20 2013 +0200
Committer: Michael Bolz <mi...@apache.org>
Committed: Wed Sep 11 08:44:57 2013 +0200
----------------------------------------------------------------------
.../olingo/odata2/core/ContentNegotiator.java | 97 +++++++++++++++++---
.../olingo/odata2/core/ODataRequestHandler.java | 55 ++---------
.../odata2/core/ContentNegotiatorTest.java | 43 ++++++++-
.../core/ODataRequestHandlerValidationTest.java | 9 ++
4 files changed, 137 insertions(+), 67 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/31c3d8fc/odata-core/src/main/java/org/apache/olingo/odata2/core/ContentNegotiator.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ContentNegotiator.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ContentNegotiator.java
index 9fe638c..bb63801 100644
--- a/odata-core/src/main/java/org/apache/olingo/odata2/core/ContentNegotiator.java
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ContentNegotiator.java
@@ -20,12 +20,15 @@ package org.apache.olingo.odata2.core;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
import org.apache.olingo.odata2.api.exception.ODataBadRequestException;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.exception.ODataNotAcceptableException;
+import org.apache.olingo.odata2.api.processor.ODataRequest;
import org.apache.olingo.odata2.api.uri.UriInfo;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.core.commons.ContentType.ODataFormat;
@@ -33,7 +36,7 @@ import org.apache.olingo.odata2.core.uri.UriInfoImpl;
import org.apache.olingo.odata2.core.uri.UriType;
/**
- *
+ * Handles content negotiation with handling of OData special cases.
*/
public class ContentNegotiator {
private static final String URI_INFO_FORMAT_JSON = "json";
@@ -41,30 +44,97 @@ public class ContentNegotiator {
private static final String URI_INFO_FORMAT_XML = "xml";
static final String DEFAULT_CHARSET = "utf-8";
+ private UriInfoImpl uriInfo;
+ private ODataRequest odataRequest;
+
/**
- * Do the content negotiation based on requested content type (in <code>acceptHeaderContentTypes</code> list)
- * and supported content types (in <code>supportedContentTypes</code> list).
+ * Creates a {@link ContentNegotiator} for given {@link ODataRequest} and {@link UriInfoImpl}
+ * which then can be used for a <code>accept content type</code> ({@link #doAcceptContentNegotiation(List)})
+ * and <code>response content type</code> ({@link #doResponseContentNegotiation(ContentType)}) negotiation.
+ *
+ * @param request specific request
+ * @param uriInfo specific uri information
+ * @throws IllegalArgumentException if at least one of both parameters is <code>NULL</code>
+ */
+ public ContentNegotiator(ODataRequest request, UriInfoImpl uriInfo) {
+ if(request == null) {
+ throw new IllegalArgumentException("Parameter ODataRequest MUST NOT be null.");
+ }
+ if(uriInfo == null) {
+ throw new IllegalArgumentException("Parameter UriInfoImpl MUST NOT be null.");
+ }
+ this.odataRequest = request;
+ this.uriInfo = uriInfo;
+ }
+
+ /**
+ * Do the content negotiation for <code>accept header value</code> based on
+ * requested content type (in HTTP accept header from {@link ODataRequest}
+ * set on {@link ContentNegotiator} creation) in combination with uri information
+ * (from {@link UriInfo} set on {@link ContentNegotiator} creation)
+ * and from given supported content types (via <code>supportedContentTypes</code>).
*
- * @param uriInfo additional uri informations especially <code>$format</code>
- * @param acceptHeaderContentTypes list of requested content types
* @param supportedContentTypes list of supported content types
* @return best fitting content type or <code>NULL</code> if content type is not set and for given {@link UriInfo} is ignored
* @throws ODataException if no supported content type was found
*/
- public String doContentNegotiation(final UriInfoImpl uriInfo, final List<String> acceptHeaderContentTypes, final List<String> supportedContentTypes) throws ODataException {
- ContentType contentType;
+ public ContentType doAcceptContentNegotiation(List<String> supportedContentTypes) throws ODataException {
+
+ if(uriInfo.isCount() || uriInfo.isValue()) {
+ String rawAcceptHeader = odataRequest.getRequestHeaderValue("Accept");
+ if(rawAcceptHeader == null) {
+ return ContentType.WILDCARD;
+ }
+ return ContentType.createAsCustom(rawAcceptHeader);
+ }
+
+ List<String> usedContentTypes = supportedContentTypes;
+ if(ODataHttpMethod.POST.equals(odataRequest.getMethod()) &&
+ (uriInfo.getUriType() == UriType.URI1 || uriInfo.getUriType() == UriType.URI6B)) {
+
+ usedContentTypes = new LinkedList<String>(supportedContentTypes);
+ usedContentTypes.add(0, ContentType.APPLICATION_ATOM_XML_ENTRY_CS_UTF_8.toContentTypeString());
+ usedContentTypes.add(1, ContentType.APPLICATION_ATOM_XML_ENTRY.toContentTypeString());
+ usedContentTypes.remove(ContentType.APPLICATION_ATOM_XML_FEED.toContentTypeString());
+ usedContentTypes.remove(ContentType.APPLICATION_ATOM_XML_FEED_CS_UTF_8.toContentTypeString());
+ }
+
if (uriInfo.getFormat() == null) {
- contentType = doContentNegotiationForAcceptHeader(acceptHeaderContentTypes, ContentType.create(supportedContentTypes));
+ return doContentNegotiationForAcceptHeader(odataRequest.getAcceptHeaders(), ContentType.create(usedContentTypes));
} else {
- contentType = doContentNegotiationForFormat(uriInfo, ContentType.createAsCustom(supportedContentTypes));
+ return doContentNegotiationForFormat(uriInfo, ContentType.createAsCustom(usedContentTypes));
}
+ }
- if (contentType.getODataFormat() == ODataFormat.CUSTOM) {
- return contentType.getType();
- }
- return contentType.toContentTypeString();
+ /**
+ * Do the content negotiation for <code>response content type header value</code> based on
+ * HTTP request method (from {@link ODataRequest} set on {@link ContentNegotiator} creation)
+ * in combination with uri information (from {@link UriInfo} set on {@link ContentNegotiator} creation)
+ * and from given <code>accepted content type</code> (via <code>acceptContentType</code> parameter).
+ *
+ * @param acceptContentType accepted content type for {@link ODataRequest} and {@link UriInfo} combination (which both
+ * were set on {@link ContentNegotiator} creation).
+ * @return best correct response content type based on accepted content type, {@link ODataRequest} and {@link UriInfo} combination
+ */
+ public ContentType doResponseContentNegotiation(ContentType acceptContentType) {
+ ContentType contentType = acceptContentType;
+ UriType uriType = uriInfo.getUriType();
+ if(contentType != null && contentType.getODataFormat() == ODataFormat.ATOM) {
+ if(uriType == UriType.URI1 || uriType == UriType.URI6B) {
+ if(ODataHttpMethod.GET.equals(odataRequest.getMethod())) {
+ contentType = ContentType.create(contentType, ContentType.PARAMETER_TYPE, "feed");
+ } else {
+ contentType = ContentType.create(contentType, ContentType.PARAMETER_TYPE, "entry");
+ }
+ } else if(uriType == UriType.URI2 || uriType == UriType.URI6A) {
+ contentType = ContentType.create(contentType, ContentType.PARAMETER_TYPE, "entry");
+ }
+ }
+
+ return contentType;
}
+
private ContentType doContentNegotiationForFormat(final UriInfoImpl uriInfo, final List<ContentType> supportedContentTypes) throws ODataException {
validateFormatQuery(uriInfo);
ContentType formatContentType = mapFormat(uriInfo);
@@ -157,5 +227,4 @@ public class ContentNegotiator {
}
return contentType;
}
-
}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/31c3d8fc/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
index cf09bb3..9bbe1b2 100644
--- a/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
@@ -19,7 +19,6 @@
package org.apache.olingo.odata2.core;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -108,10 +107,11 @@ public class ODataRequestHandler {
checkRequestContentType(uriInfo, request.getContentType());
}
- final String acceptContentType = doContentNegotiation(request, uriInfo);
+ ContentNegotiator contentNegotiator = new ContentNegotiator(request, uriInfo);
+ final ContentType acceptContentType = contentNegotiator.doAcceptContentNegotiation(getSupportedContentTypes(uriInfo));
timingHandle2 = context.startRuntimeMeasurement("Dispatcher", "dispatch");
- odataResponse = dispatcher.dispatch(method, uriInfo, request.getBody(), request.getContentType(), acceptContentType);
+ odataResponse = dispatcher.dispatch(method, uriInfo, request.getBody(), request.getContentType(), acceptContentType.toContentTypeString());
context.stopRuntimeMeasurement(timingHandle2);
@@ -119,8 +119,9 @@ public class ODataRequestHandler {
if (!odataResponse.containsHeader(ODataHttpHeaders.DATASERVICEVERSION)) {
extendedResponse = extendedResponse.header(ODataHttpHeaders.DATASERVICEVERSION, serverDataServiceVersion);
}
- if(!odataResponse.containsHeader("Content-Type")) {
- setContentType(extendedResponse, acceptContentType, method, uriInfo.getUriType());
+ if(!odataResponse.containsHeader(HttpHeaders.CONTENT_TYPE)) {
+ ContentType responseContentType = contentNegotiator.doResponseContentNegotiation(acceptContentType);
+ extendedResponse.header(HttpHeaders.CONTENT_TYPE, responseContentType.toContentTypeString());
}
final UriType uriType = uriInfo.getUriType();
@@ -139,50 +140,6 @@ public class ODataRequestHandler {
return debugValue == null ? odataResponse : new ODataDebugResponseWrapper(context, odataResponse, uriInfo, exception, debugValue).wrapResponse();
}
- private void setContentType(ODataResponseBuilder extendedResponse, String acceptContentType, ODataHttpMethod method, UriType uriType) {
- ContentType contentType = ContentType.parse(acceptContentType);
- if(contentType != null && contentType.getODataFormat() == ODataFormat.ATOM) {
- if(uriType == UriType.URI1 || uriType == UriType.URI6B) {
- if(ODataHttpMethod.GET.equals(method)) {
- contentType = ContentType.create(contentType, ContentType.PARAMETER_TYPE, "feed");
- } else {
- contentType = ContentType.create(contentType, ContentType.PARAMETER_TYPE, "entry");
- }
- } else if(uriType == UriType.URI2 || uriType == UriType.URI6A) {
- contentType = ContentType.create(contentType, ContentType.PARAMETER_TYPE, "entry");
- }
- extendedResponse.header(HttpHeaders.CONTENT_TYPE, contentType.toContentTypeString());
- } else {
- extendedResponse.header(HttpHeaders.CONTENT_TYPE, acceptContentType);
- }
- }
-
- /**
- * Do the content negotiation based on requested content type (in HTTP accept header) and from {@link ODataService}
- * supported content types (via {@link ODataService#getSupportedContentTypes(Class)}).
- *
- * @param request with requested content type
- * @param uriInfo additional uri informations
- * @return best fitting content type or <code>NULL</code> if content type is not set and for given {@link UriInfo} is ignored
- * @throws ODataException if no supported content type was found
- */
- private String doContentNegotiation(final ODataRequest request, UriInfoImpl uriInfo) throws ODataException {
- if(uriInfo.isCount() || uriInfo.isValue()) {
- return request.getRequestHeaderValue("Accept");
- } else if(ODataHttpMethod.POST.equals(request.getMethod()) &&
- (uriInfo.getUriType() == UriType.URI1 || uriInfo.getUriType() == UriType.URI6B)) {
-
- List<String> supportedContentTypes = new LinkedList<String>(getSupportedContentTypes(uriInfo));
- supportedContentTypes.add(0, ContentType.APPLICATION_ATOM_XML_ENTRY_CS_UTF_8.toContentTypeString());
- supportedContentTypes.add(1, ContentType.APPLICATION_ATOM_XML_ENTRY.toContentTypeString());
- supportedContentTypes.remove(ContentType.APPLICATION_ATOM_XML_FEED.toContentTypeString());
- supportedContentTypes.remove(ContentType.APPLICATION_ATOM_XML_FEED_CS_UTF_8.toContentTypeString());
-
- return new ContentNegotiator().doContentNegotiation(uriInfo, request.getAcceptHeaders(), supportedContentTypes);
- } else {
- return new ContentNegotiator().doContentNegotiation(uriInfo, request.getAcceptHeaders(), getSupportedContentTypes(uriInfo));
- }
- }
private String getServerDataServiceVersion() throws ODataException {
return service.getVersion() == null ? ODataServiceVersion.V20 : service.getVersion();
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/31c3d8fc/odata-core/src/test/java/org/apache/olingo/odata2/core/ContentNegotiatorTest.java
----------------------------------------------------------------------
diff --git a/odata-core/src/test/java/org/apache/olingo/odata2/core/ContentNegotiatorTest.java b/odata-core/src/test/java/org/apache/olingo/odata2/core/ContentNegotiatorTest.java
index 8ce0df3..a77f4f9 100644
--- a/odata-core/src/test/java/org/apache/olingo/odata2/core/ContentNegotiatorTest.java
+++ b/odata-core/src/test/java/org/apache/olingo/odata2/core/ContentNegotiatorTest.java
@@ -19,13 +19,17 @@
package org.apache.olingo.odata2.core;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.exception.ODataNotAcceptableException;
+import org.apache.olingo.odata2.api.processor.ODataRequest;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.core.uri.UriInfoImpl;
import org.apache.olingo.odata2.core.uri.UriType;
@@ -36,11 +40,34 @@ import org.mockito.Mockito;
*
*/
public class ContentNegotiatorTest {
+
private void negotiateContentType(final List<ContentType> contentTypes, final List<ContentType> supportedTypes, final String expected) throws ODataException {
- final ContentType contentType = new ContentNegotiator().contentNegotiation(contentTypes, supportedTypes);
+ UriInfoImpl uriInfo = Mockito.mock(UriInfoImpl.class);
+ ODataRequest request = Mockito.mock(ODataRequest.class);
+ final ContentType contentType = new ContentNegotiator(request, uriInfo).contentNegotiation(contentTypes, supportedTypes);
assertEquals(expected, contentType.toContentTypeString());
}
+ @Test(expected=IllegalArgumentException.class)
+ public void invalidContentNegotiatorCreation() {
+ final ContentNegotiator contentType = new ContentNegotiator(null, null);
+ assertNull(contentType);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void invalidContentNegotiatorCreationNullRequest() {
+ UriInfoImpl uriInfo = Mockito.mock(UriInfoImpl.class);
+ final ContentNegotiator contentType = new ContentNegotiator(null, uriInfo);
+ assertNull(contentType);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void invalidContentNegotiatorCreationNullUri() {
+ ODataRequest request = Mockito.mock(ODataRequest.class);
+ final ContentNegotiator contentType = new ContentNegotiator(request, null);
+ assertNull(contentType);
+ }
+
@Test
public void contentNegotiationEmptyRequest() throws Exception {
negotiateContentType(
@@ -161,9 +188,17 @@ public class ContentNegotiatorTest {
List<String> acceptedContentTypes = Arrays.asList(requestType);
List<String> supportedContentTypes = Arrays.asList(supportedType);
- ContentNegotiator negotiator = new ContentNegotiator();
- String negotiatedContentType = negotiator.doContentNegotiation(uriInfo, acceptedContentTypes, supportedContentTypes);
-
+ ODataRequest request = Mockito.mock(ODataRequest.class);
+ Mockito.when(request.getMethod()).thenReturn(ODataHttpMethod.GET);
+ Mockito.when(request.getRequestHeaderValue(HttpHeaders.ACCEPT)).thenReturn(requestType);
+ Mockito.when(request.getAcceptHeaders()).thenReturn(acceptedContentTypes);
+
+
+ // perform
+ ContentNegotiator negotiator = new ContentNegotiator(request, uriInfo);
+ String negotiatedContentType = negotiator.doAcceptContentNegotiation(supportedContentTypes).toContentTypeString();
+
+ // verify
assertEquals(supportedType, negotiatedContentType);
}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/31c3d8fc/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
----------------------------------------------------------------------
diff --git a/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java b/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
index f8629cd..505b9fc 100644
--- a/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
+++ b/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
@@ -235,6 +235,15 @@ public class ODataRequestHandlerValidationTest extends BaseTest {
.thenReturn(queryParameters == null ? new HashMap<String, String>() : queryParameters);
when(request.getContentType()).thenReturn(requestContentType);
when(request.getAcceptHeaders()).thenReturn(acceptHeaders);
+ String acceptHeadersAsString = null;
+ for (String string : acceptHeaders) {
+ if(acceptHeadersAsString == null) {
+ acceptHeadersAsString = string;
+ } else {
+ acceptHeadersAsString += ", " + string;
+ }
+ }
+ when(request.getRequestHeaderValue("Accept")).thenReturn(acceptHeadersAsString);
return request;
}