Repository: olingo-odata4 Updated Branches: refs/heads/master c0f1b997e -> 38c9a76eb
[OLINGO-995] Support HEAD for metadata and service document Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/38c9a76e Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/38c9a76e Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/38c9a76e Branch: refs/heads/master Commit: 38c9a76eb60cfcdea7ec26deb92910e9efd438f7 Parents: c0f1b99 Author: Christian Amend <[email protected]> Authored: Thu Aug 11 15:58:17 2016 +0200 Committer: Christian Amend <[email protected]> Committed: Thu Aug 11 15:58:17 2016 +0200 ---------------------------------------------------------------------- .../olingo/fit/tecsvc/http/BasicHttpITCase.java | 31 +++++ .../olingo/commons/api/http/HttpMethod.java | 3 +- .../olingo/commons/api/http/HttpStatusCode.java | 4 + .../server/api/processor/DefaultProcessor.java | 27 +++-- .../olingo/server/core/ODataDispatcher.java | 94 ++++++++------- .../server/core/ODataExceptionHelper.java | 3 +- .../server/core/ODataHttpHandlerImpl.java | 15 ++- .../olingo/server/core/ExceptionHelperTest.java | 19 ++- .../server/core/ODataHttpHandlerImplTest.java | 4 +- .../server/core/ODataHandlerImplTest.java | 117 +++++++++++++++---- 10 files changed, 237 insertions(+), 80 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java index bb13989..55a04f5 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/BasicHttpITCase.java @@ -19,6 +19,7 @@ package org.apache.olingo.fit.tecsvc.http; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.net.HttpURLConnection; @@ -39,6 +40,36 @@ public class BasicHttpITCase extends AbstractBaseTestITCase { private static final String SERVICE_URI = TecSvcConst.BASE_URI + "/"; @Test + public void testHeadMethodOnServiceDocument() throws Exception { + URL url = new URL(SERVICE_URI); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.HEAD.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("", IOUtils.toString(connection.getInputStream())); + connection.disconnect(); + } + + @Test + public void testHeadMethodOnMetadataDocument() throws Exception { + URL url = new URL(SERVICE_URI + "$metadata"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.HEAD.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/xml"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("", IOUtils.toString(connection.getInputStream())); + connection.disconnect(); + } + + @Test public void testFormat() throws Exception { URL url = new URL(SERVICE_URI + "?$format=json"); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpMethod.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpMethod.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpMethod.java index bccefd2..c77977a 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpMethod.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpMethod.java @@ -28,6 +28,7 @@ public enum HttpMethod { PUT, PATCH, MERGE, - DELETE + DELETE, + HEAD } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpStatusCode.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpStatusCode.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpStatusCode.java index 23bd85b..e1e4e32 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpStatusCode.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/http/HttpStatusCode.java @@ -23,6 +23,10 @@ package org.apache.olingo.commons.api.http; * and additional status codes as defined in RFC 6585 */ public enum HttpStatusCode { + + CONTINUE(100, "Continue"), + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java index bde6aa4..83691b5 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java @@ -23,6 +23,7 @@ import java.nio.charset.Charset; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpMethod; import org.apache.olingo.commons.api.http.HttpStatusCode; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataApplicationException; @@ -72,10 +73,15 @@ public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProce if (isNotModified) { response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode()); } else { - ODataSerializer serializer = odata.createSerializer(requestedContentType); - response.setContent(serializer.serviceDocument(serviceMetadata, null).getContent()); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + // HTTP HEAD requires no payload but a 200 OK response + if (HttpMethod.HEAD == request.getMethod()) { + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + } else { + ODataSerializer serializer = odata.createSerializer(requestedContentType); + response.setContent(serializer.serviceDocument(serviceMetadata, null).getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + } } } @@ -97,10 +103,15 @@ public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProce if (isNotModified) { response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode()); } else { - ODataSerializer serializer = odata.createSerializer(requestedContentType); - response.setContent(serializer.metadataDocument(serviceMetadata).getContent()); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + // HTTP HEAD requires no payload but a 200 OK response + if (HttpMethod.HEAD == request.getMethod()) { + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + } else { + ODataSerializer serializer = odata.createSerializer(requestedContentType); + response.setContent(serializer.metadataDocument(serviceMetadata).getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + } } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java index 3d4bdc7..138f45a 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java @@ -86,7 +86,7 @@ public class ODataDispatcher { ODataLibraryException { switch (uriInfo.getKind()) { case metadata: - checkMethod(request.getMethod(), HttpMethod.GET); + checkMethods(request.getMethod(), HttpMethod.GET, HttpMethod.HEAD); final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), RepresentationType.METADATA); handler.selectProcessor(MetadataProcessor.class) @@ -94,7 +94,7 @@ public class ODataDispatcher { break; case service: - checkMethod(request.getMethod(), HttpMethod.GET); + checkMethods(request.getMethod(), HttpMethod.GET, HttpMethod.HEAD); if ("".equals(request.getRawODataPath())) { handler.selectProcessor(RedirectProcessor.class) .redirect(request, response); @@ -267,7 +267,7 @@ public class ODataDispatcher { final HttpMethod httpMethod = request.getMethod(); final boolean isCollection = ((UriResourcePartTyped) uriInfo.getUriResourceParts() .get(lastPathSegmentIndex - 1)) - .isCollection(); + .isCollection(); if (isCollection && httpMethod == HttpMethod.GET) { final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), @@ -303,57 +303,57 @@ public class ODataDispatcher { private void handleValueDispatching(final ODataRequest request, final ODataResponse response, final int lastPathSegmentIndex) throws ODataApplicationException, ODataLibraryException { - //The URI Parser already checked if $value is allowed here so we only have to dispatch to the correct processor + // The URI Parser already checked if $value is allowed here so we only have to dispatch to the correct processor final HttpMethod method = request.getMethod(); final UriResource resource = uriInfo.getUriResourceParts().get(lastPathSegmentIndex - 1); if (resource instanceof UriResourceProperty || resource instanceof UriResourceFunction - && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { - final EdmType type = resource instanceof UriResourceProperty ? - ((UriResourceProperty) resource).getType() : ((UriResourceFunction) resource).getType(); - final RepresentationType valueRepresentationType = - type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? - RepresentationType.BINARY : RepresentationType.VALUE; - if (method == HttpMethod.GET) { - final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), valueRepresentationType); - - handler.selectProcessor(PrimitiveValueProcessor.class) + && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { + final EdmType type = resource instanceof UriResourceProperty ? ((UriResourceProperty) resource).getType() + : ((UriResourceFunction) resource).getType(); + final RepresentationType valueRepresentationType = + type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Binary) ? RepresentationType.BINARY + : RepresentationType.VALUE; + if (method == HttpMethod.GET) { + final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), valueRepresentationType); + + handler.selectProcessor(PrimitiveValueProcessor.class) .readPrimitiveValue(request, response, uriInfo, requestedContentType); - } else if (method == HttpMethod.PUT && resource instanceof UriResourceProperty) { - validatePreconditions(request, false); - final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), - valueRepresentationType, true); - final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), - request, handler.getCustomContentTypeSupport(), valueRepresentationType); - handler.selectProcessor(PrimitiveValueProcessor.class) + } else if (method == HttpMethod.PUT && resource instanceof UriResourceProperty) { + validatePreconditions(request, false); + final ContentType requestFormat = getSupportedContentType(request.getHeader(HttpHeader.CONTENT_TYPE), + valueRepresentationType, true); + final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), + request, handler.getCustomContentTypeSupport(), valueRepresentationType); + handler.selectProcessor(PrimitiveValueProcessor.class) .updatePrimitiveValue(request, response, uriInfo, requestFormat, responseFormat); - } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { - validatePreconditions(request, false); - handler.selectProcessor(PrimitiveValueProcessor.class) + } else if (method == HttpMethod.DELETE && resource instanceof UriResourceProperty) { + validatePreconditions(request, false); + handler.selectProcessor(PrimitiveValueProcessor.class) .deletePrimitiveValue(request, response, uriInfo); - } else { - throwMethodNotAllowed(method); - } + } else { + throwMethodNotAllowed(method); + } } else { if (method == HttpMethod.GET) { - //This can be a GET on an EntitySet, Navigation or Function + // This can be a GET on an EntitySet, Navigation or Function final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), RepresentationType.MEDIA); handler.selectProcessor(MediaEntityProcessor.class) - .readMediaEntity(request, response, uriInfo, requestedContentType); - //PUT and DELETE can only be called on EntitySets or Navigation properties which are media resources + .readMediaEntity(request, response, uriInfo, requestedContentType); + // PUT and DELETE can only be called on EntitySets or Navigation properties which are media resources } else if (method == HttpMethod.PUT && isEntityOrNavigationMedia(resource)) { validatePreconditions(request, true); final ContentType requestFormat = ContentType.parse(request.getHeader(HttpHeader.CONTENT_TYPE)); final ContentType responseFormat = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), RepresentationType.ENTITY); handler.selectProcessor(MediaEntityProcessor.class) - .updateMediaEntity(request, response, uriInfo, requestFormat, responseFormat); + .updateMediaEntity(request, response, uriInfo, requestFormat, responseFormat); } else if (method == HttpMethod.DELETE && isEntityOrNavigationMedia(resource)) { validatePreconditions(request, true); handler.selectProcessor(MediaEntityProcessor.class) - .deleteMediaEntity(request, response, uriInfo); + .deleteMediaEntity(request, response, uriInfo); } else { throwMethodNotAllowed(method); } @@ -363,8 +363,8 @@ public class ODataDispatcher { private void handleComplexDispatching(final ODataRequest request, final ODataResponse response, final boolean isCollection) throws ODataApplicationException, ODataLibraryException { final HttpMethod method = request.getMethod(); - final RepresentationType complexRepresentationType = isCollection ? - RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX; + final RepresentationType complexRepresentationType = isCollection ? RepresentationType.COLLECTION_COMPLEX + : RepresentationType.COMPLEX; if (method == HttpMethod.GET) { final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), complexRepresentationType); @@ -405,8 +405,8 @@ public class ODataDispatcher { private void handlePrimitiveDispatching(final ODataRequest request, final ODataResponse response, final boolean isCollection) throws ODataApplicationException, ODataLibraryException { final HttpMethod method = request.getMethod(); - final RepresentationType representationType = isCollection ? - RepresentationType.COLLECTION_PRIMITIVE : RepresentationType.PRIMITIVE; + final RepresentationType representationType = isCollection ? RepresentationType.COLLECTION_PRIMITIVE + : RepresentationType.PRIMITIVE; if (method == HttpMethod.GET) { final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request, handler.getCustomContentTypeSupport(), representationType); @@ -450,12 +450,12 @@ public class ODataDispatcher { if (resource instanceof UriResourceEntitySet || resource instanceof UriResourceNavigation || resource instanceof UriResourceFunction - && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.ENTITY) { + && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.ENTITY) { handler.selectProcessor(CountEntityCollectionProcessor.class) .countEntityCollection(request, response, uriInfo); } else if (resource instanceof UriResourcePrimitiveProperty || resource instanceof UriResourceFunction - && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { + && ((UriResourceFunction) resource).getType().getKind() == EdmTypeKind.PRIMITIVE) { handler.selectProcessor(CountPrimitiveCollectionProcessor.class) .countPrimitiveCollection(request, response, uriInfo); } else { @@ -533,6 +533,18 @@ public class ODataDispatcher { } } + private void checkMethods(final HttpMethod requestMethod, final HttpMethod... allowedMethods) + throws ODataHandlerException { + //Check if the request method is one of the allowed ones + for (int i = 0; i < allowedMethods.length; i++) { + if (requestMethod == allowedMethods[i]) { + return; + } + } + //request method does not match any allowed method + throwMethodNotAllowed(requestMethod); + } + private void throwMethodNotAllowed(final HttpMethod httpMethod) throws ODataHandlerException { throw new ODataHandlerException("HTTP method " + httpMethod + " is not allowed.", ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, httpMethod.toString()); @@ -560,11 +572,11 @@ public class ODataDispatcher { } private boolean isEntityOrNavigationMedia(final UriResource pathSegment) { - //This method MUST NOT check if the resource is of type function since these are handled differently + // This method MUST NOT check if the resource is of type function since these are handled differently return pathSegment instanceof UriResourceEntitySet && ((UriResourceEntitySet) pathSegment).getEntityType().hasStream() || pathSegment instanceof UriResourceNavigation - && ((EdmEntityType) ((UriResourceNavigation) pathSegment).getType()).hasStream(); + && ((EdmEntityType) ((UriResourceNavigation) pathSegment).getType()).hasStream(); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java index f409903..20c0634 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java @@ -92,7 +92,8 @@ public class ODataExceptionHelper { || ODataHandlerException.MessageKeys.INVALID_HTTP_METHOD.equals(e.getMessageKey()) || ODataHandlerException.MessageKeys.AMBIGUOUS_XHTTP_METHOD.equals(e.getMessageKey()) || ODataHandlerException.MessageKeys.MISSING_CONTENT_TYPE.equals(e.getMessageKey()) - || ODataHandlerException.MessageKeys.INVALID_CONTENT_TYPE.equals(e.getMessageKey())) { + || ODataHandlerException.MessageKeys.INVALID_CONTENT_TYPE.equals(e.getMessageKey()) + || ODataHandlerException.MessageKeys.UNSUPPORTED_CONTENT_TYPE.equals(e.getMessageKey())) { serverError.setStatusCode(HttpStatusCode.BAD_REQUEST.getStatusCode()); } else if (ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED.equals(e.getMessageKey())) { serverError.setStatusCode(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode()); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java index 8fd533b..b6ebc6b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java @@ -157,9 +157,9 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { } } - if (odResponse.getContent() != null ) { + if (odResponse.getContent() != null) { copyContent(odResponse.getContent(), response); - } else if(odResponse.getODataContent() != null) { + } else if (odResponse.getODataContent() != null) { writeContent(odResponse, response); } } @@ -229,9 +229,14 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { } static HttpMethod extractMethod(final HttpServletRequest httpRequest) throws ODataLibraryException { + final HttpMethod httpRequestMethod; + try { + httpRequestMethod = HttpMethod.valueOf(httpRequest.getMethod()); + } catch (IllegalArgumentException e) { + throw new ODataHandlerException("HTTP method not allowed" + httpRequest.getMethod(), e, + ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, httpRequest.getMethod()); + } try { - HttpMethod httpRequestMethod = HttpMethod.valueOf(httpRequest.getMethod()); - if (httpRequestMethod == HttpMethod.POST) { String xHttpMethod = httpRequest.getHeader(HttpHeader.X_HTTP_METHOD); String xHttpMethodOverride = httpRequest.getHeader(HttpHeader.X_HTTP_METHOD_OVERRIDE); @@ -301,7 +306,7 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { static void copyHeaders(ODataRequest odRequest, final HttpServletRequest req) { for (final Enumeration<?> headerNames = req.getHeaderNames(); headerNames.hasMoreElements();) { final String headerName = (String) headerNames.nextElement(); - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // getHeaders() says it returns an Enumeration of String. final List<String> headerValues = Collections.list(req.getHeaders(headerName)); odRequest.addHeader(headerName, headerValues); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-core/src/test/java/org/apache/olingo/server/core/ExceptionHelperTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ExceptionHelperTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ExceptionHelperTest.java index c4eb24c..dffbf82 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ExceptionHelperTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ExceptionHelperTest.java @@ -106,9 +106,26 @@ public class ExceptionHelperTest { } } + @Test + public void httpHandlerExceptions() { + for (MessageKey key : ODataHandlerException.MessageKeys.values()) { + final ODataHandlerException e = new ODataHandlerException(DEV_MSG, key); + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + + if (key.equals(ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED) + || key.equals(ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED)) { + checkStatusCode(serverError, HttpStatusCode.NOT_IMPLEMENTED, e); + } else if (key.equals(ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED)) { + checkStatusCode(serverError, HttpStatusCode.METHOD_NOT_ALLOWED, e); + } else { + checkStatusCode(serverError, HttpStatusCode.BAD_REQUEST, e); + } + } + } + private void checkStatusCode(final ODataServerError serverError, final HttpStatusCode statusCode, final ODataLibraryException exception) { assertEquals("FailedKey: " + exception.getMessageKey().getKey(), - serverError.getStatusCode(), statusCode.getStatusCode()); + statusCode.getStatusCode(), serverError.getStatusCode()); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataHttpHandlerImplTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataHttpHandlerImplTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataHttpHandlerImplTest.java index 1a66609..2a58f57 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataHttpHandlerImplTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ODataHttpHandlerImplTest.java @@ -49,6 +49,7 @@ public class ODataHttpHandlerImplTest { { "POST", "PATCH", null, "PATCH" }, { "POST", "GET", "GET", "GET" }, + { "HEAD", null, null, "HEAD" } }; for (String[] m : mm) { @@ -68,8 +69,7 @@ public class ODataHttpHandlerImplTest { String[][] mm = { { "POST", "bla", null }, { "POST", "PUT", "PATCH" }, - { "OPTIONS", null, null }, - { "HEAD", null, null }, + { "OPTIONS", null, null } }; for (String[] m : mm) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/38c9a76e/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java index 03a513e..591e8e2 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java @@ -18,11 +18,10 @@ */ package org.apache.olingo.server.core; -import java.io.InputStream; -import java.nio.charset.Charset; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; @@ -31,6 +30,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import java.io.InputStream; +import java.nio.charset.Charset; import java.util.Collections; import java.util.Locale; @@ -90,12 +91,26 @@ public class ODataHandlerImplTest { @Test public void serviceDocumentNonDefault() throws Exception { final ServiceDocumentProcessor processor = mock(ServiceDocumentProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor) + .readServiceDocument(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); final ODataResponse response = dispatch(HttpMethod.GET, "/", processor); - assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response.getStatusCode()); verify(processor).readServiceDocument( any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + // We support HEAD now too + final ServiceDocumentProcessor processor2 = mock(ServiceDocumentProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor2) + .readServiceDocument(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "/", processor2); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response2.getStatusCode()); + + verify(processor2).readServiceDocument( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.POST, "/", processor); dispatchMethodNotAllowed(HttpMethod.PATCH, "/", processor); dispatchMethodNotAllowed(HttpMethod.PUT, "/", processor); @@ -116,6 +131,11 @@ public class ODataHandlerImplTest { assertThat(doc, containsString("\"@odata.context\":\"$metadata\"")); assertThat(doc, containsString("\"value\":")); + + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "/", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response2.getStatusCode()); + assertNull(response2.getHeader(HttpHeader.CONTENT_TYPE)); + assertNull(response2.getContent()); } @Test @@ -123,16 +143,34 @@ public class ODataHandlerImplTest { final ODataResponse response = dispatch(HttpMethod.GET, "", null); assertEquals(HttpStatusCode.TEMPORARY_REDIRECT.getStatusCode(), response.getStatusCode()); assertEquals(BASE_URI + "/", response.getHeader(HttpHeader.LOCATION)); + + final ODataResponse responseHead = dispatch(HttpMethod.HEAD, "", null); + assertEquals(HttpStatusCode.TEMPORARY_REDIRECT.getStatusCode(), responseHead.getStatusCode()); + assertEquals(BASE_URI + "/", responseHead.getHeader(HttpHeader.LOCATION)); } @Test public void metadataNonDefault() throws Exception { final MetadataProcessor processor = mock(MetadataProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor) + .readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); - assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response.getStatusCode()); verify(processor).readMetadata( any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + // We support HEAD now too + final MetadataProcessor processor2 = mock(MetadataProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor2) + .readMetadata(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "$metadata", processor2); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response2.getStatusCode()); + + verify(processor2).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); dispatchMethodNotAllowed(HttpMethod.POST, "$metadata", processor); dispatchMethodNotAllowed(HttpMethod.PATCH, "$metadata", processor); @@ -149,6 +187,11 @@ public class ODataHandlerImplTest { assertNotNull(response.getContent()); assertThat(IOUtils.toString(response.getContent()), containsString("<edmx:Edmx Version=\"4.0\"")); + + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "$metadata", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response2.getStatusCode()); + assertNull(response2.getHeader(HttpHeader.CONTENT_TYPE)); + assertNull(response2.getContent()); } @Test @@ -209,7 +252,6 @@ public class ODataHandlerImplTest { assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); } - @Test public void uriParserExceptionWithFormatQueryJson() throws Exception { final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=json", "", "", null); @@ -226,7 +268,6 @@ public class ODataHandlerImplTest { response.getHeader(HttpHeader.CONTENT_TYPE)); } - @Test public void uriParserExceptionWithFormatJsonAcceptAtom() throws Exception { final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=json", @@ -268,27 +309,27 @@ public class ODataHandlerImplTest { assertEquals("application/json;odata.metadata=minimal", response.getHeader(HttpHeader.CONTENT_TYPE)); } - + @Test public void applicationExceptionInProcessorMessage() throws Exception { final String ODATA_ERRORCODE = "425"; final String ORIGINAL_MESSAGE = "original message"; final String LOCALIZED_MESSAGE = "localized message"; MetadataProcessor processor = mock(MetadataProcessor.class); - - ODataApplicationException oDataApplicationException = + + ODataApplicationException oDataApplicationException = new ODataApplicationException(ORIGINAL_MESSAGE, 425, Locale.ENGLISH, ODATA_ERRORCODE) { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; + + @Override + public String getLocalizedMessage() { + return LOCALIZED_MESSAGE; + } + }; - @Override - public String getLocalizedMessage() { - return LOCALIZED_MESSAGE; - } - }; - doThrow(oDataApplicationException).when(processor).readMetadata( any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); - + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); InputStream contentStream = response.getContent(); String responseContent = IOUtils.toString(contentStream, Charset.forName("UTF-8")); @@ -299,7 +340,7 @@ public class ODataHandlerImplTest { // test if the original is hold assertEquals(ORIGINAL_MESSAGE, oDataApplicationException.getMessage()); } - + @Test public void applicationExceptionInProcessor() throws Exception { MetadataProcessor processor = mock(MetadataProcessor.class); @@ -345,6 +386,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -359,6 +401,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -374,15 +417,22 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test public void dispatchCountWithNavigation() throws Exception { final CountEntityCollectionProcessor processor = mock(CountEntityCollectionProcessor.class); - dispatch(HttpMethod.GET, "ESAllPrim(0)/NavPropertyETTwoPrimMany/$count", processor); + String uri = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$count"; + dispatch(HttpMethod.GET, uri, processor); verify(processor).countEntityCollection( any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -406,6 +456,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, entityCountUri, entityCountProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, entityCountUri, entityCountProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, entityCountUri, entityCountProcessor); PrimitiveProcessor primitiveProcessor = mock(PrimitiveProcessor.class); dispatch(HttpMethod.GET, "FICRTString()", primitiveProcessor); @@ -420,6 +471,7 @@ public class ODataHandlerImplTest { dispatchMethodWithError(HttpMethod.PATCH, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); dispatchMethodWithError(HttpMethod.PUT, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); dispatchMethodWithError(HttpMethod.DELETE, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.HEAD, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); final String primitiveCollectionUri = "FICRTCollString()"; PrimitiveCollectionProcessor primitiveCollectionProcessor = mock(PrimitiveCollectionProcessor.class); @@ -430,6 +482,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, primitiveCollectionUri, primitiveCollectionProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, primitiveCollectionUri, primitiveCollectionProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, primitiveCollectionUri, primitiveCollectionProcessor); final String primitiveCountUri = "FICRTCollString()/$count"; final CountPrimitiveCollectionProcessor primitiveCountProcessor = mock(CountPrimitiveCollectionProcessor.class); @@ -440,6 +493,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, primitiveCountUri, primitiveCountProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, primitiveCountUri, primitiveCountProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, primitiveCountUri, primitiveCountProcessor); ComplexProcessor complexProcessor = mock(ComplexProcessor.class); dispatch(HttpMethod.GET, "FICRTCTTwoPrim()", complexProcessor); @@ -460,6 +514,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, complexCountUri, complexCountProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, complexCountUri, complexCountProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, complexCountUri, complexCountProcessor); final String mediaUri = "FICRTESMedia(ParameterInt16=1)/$value"; final MediaEntityProcessor mediaProcessor = mock(MediaEntityProcessor.class); @@ -470,6 +525,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, mediaUri, mediaProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, mediaUri, mediaProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, mediaUri, mediaProcessor); } @Test @@ -483,6 +539,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, ContainerProvider.AIRT_STRING, primitiveProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, ContainerProvider.AIRT_STRING, primitiveProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, ContainerProvider.AIRT_STRING, primitiveProcessor); ActionPrimitiveCollectionProcessor primitiveCollectionProcessor = mock(ActionPrimitiveCollectionProcessor.class); dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_STRING_TWO_PARAM, primitiveCollectionProcessor); @@ -534,6 +591,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, ContainerProvider.AIRT, voidProcessor); dispatchMethodNotAllowed(HttpMethod.PUT, ContainerProvider.AIRT, voidProcessor); dispatchMethodNotAllowed(HttpMethod.DELETE, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, ContainerProvider.AIRT, voidProcessor); } @Test @@ -563,6 +621,7 @@ public class ODataHandlerImplTest { any(ContentType.class), any(ContentType.class)); dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -587,8 +646,9 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } - + @Test public void dispatchValueOnNoMedia() throws Exception { final String uri = "ESAllPrim(1)/$value"; @@ -605,6 +665,9 @@ public class ODataHandlerImplTest { dispatch(HttpMethod.DELETE, uri, processor); verifyZeroInteractions(processor); + + dispatch(HttpMethod.HEAD, uri, processor); + verifyZeroInteractions(processor); } @Test @@ -621,7 +684,7 @@ public class ODataHandlerImplTest { any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne", processor); - + dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne/$value", processor); dispatch(HttpMethod.PUT, uri, processor); @@ -633,6 +696,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -641,6 +705,7 @@ public class ODataHandlerImplTest { dispatch(HttpMethod.DELETE, "ESMedia(1)", processor); verify(processor).deleteEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.HEAD, "ESMedia(1)", processor); } @Test @@ -666,6 +731,7 @@ public class ODataHandlerImplTest { verify(processor).deletePrimitive(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -688,6 +754,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -708,6 +775,7 @@ public class ODataHandlerImplTest { verify(processor).deletePrimitiveCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -722,6 +790,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -747,6 +816,7 @@ public class ODataHandlerImplTest { verify(processor).deleteComplex(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -767,6 +837,7 @@ public class ODataHandlerImplTest { verify(processor).deleteComplexCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -781,6 +852,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -809,6 +881,8 @@ public class ODataHandlerImplTest { dispatch(HttpMethod.DELETE, uriMany, "$id=ESTwoPrim(1)", null, null, processor); verify(processor).deleteReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test @@ -822,6 +896,7 @@ public class ODataHandlerImplTest { dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); } @Test
