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;
   }