[OLINGO-856] Created ODataHandler Interface
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/e07abf0b Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/e07abf0b Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/e07abf0b Branch: refs/heads/master Commit: e07abf0b0f463194976e0fdd4c389d5183d7f1a0 Parents: 3c205f9 Author: mibo <[email protected]> Authored: Sat Feb 13 07:08:33 2016 +0100 Committer: mibo <[email protected]> Committed: Sat Feb 13 07:08:33 2016 +0100 ---------------------------------------------------------------------- .../org/apache/olingo/server/api/OData.java | 7 + .../apache/olingo/server/api/ODataHandler.java | 63 ++ .../olingo/server/api/ODataHttpHandler.java | 29 +- .../apache/olingo/server/core/OData4Impl.java | 4 +- .../olingo/server/core/ODataDispatcher.java | 4 +- .../apache/olingo/server/core/ODataHandler.java | 232 ------ .../olingo/server/core/ODataHandlerImpl.java | 233 ++++++ .../server/core/ODataHttpHandlerImpl.java | 11 +- .../apache/olingo/server/core/ODataImpl.java | 11 +- .../core/batchhandler/BatchFacadeImpl.java | 6 +- .../server/core/batchhandler/BatchHandler.java | 6 +- .../core/batchhandler/BatchPartHandler.java | 8 +- .../batchhandler/MockedBatchHandlerTest.java | 6 +- .../server/core/ODataHandlerImplTest.java | 768 +++++++++++++++++++ .../olingo/server/core/ODataHandlerTest.java | 768 ------------------- 15 files changed, 1108 insertions(+), 1048 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java index 26f5f61..4921ac4 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java @@ -98,6 +98,13 @@ public abstract class OData { public abstract ODataHttpHandler createHandler(ServiceMetadata serviceMetadata); /** + * Creates a new ODataHandler for handling OData requests. + * + * @param serviceMetadata - metadata object required to handle an OData request + */ + public abstract ODataHandler createBasicHandler(ServiceMetadata serviceMetadata); + + /** * Creates a metadata object for this service. * * @param edmProvider a custom or default implementation for creating metadata http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHandler.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHandler.java new file mode 100644 index 0000000..d1ba14b --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHandler.java @@ -0,0 +1,63 @@ +/* + * 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; + +import org.apache.olingo.server.api.etag.CustomETagSupport; +import org.apache.olingo.server.api.processor.Processor; +import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; + +/** + * <p>Handles requests as OData requests.</p> + * + * <p>This includes URI parsing, content negotiation, dispatching the request + * to a specific custom processor implementation for handling data and + * creating the serialized content for the response object.</p> + */ +public interface ODataHandler { + + /** + * <p>Processes an OData request.</p> + * <p>This includes URI parsing, content negotiation, dispatching the request + * to a specific custom processor implementation for handling data and + * creating the serialized content for the response object.</p> + * @param request the OData request + * @return OData response + */ + ODataResponse process(final ODataRequest request); + + /** + * <p>Registers additional custom processor implementations for handling OData requests.</p> + * <p>If request processing requires a processor that is not registered then a + * "not implemented" exception will happen.</p> + */ + void register(Processor processor); + + /** + * Registers a service implementation for modifying the standard list of supported + * content types. + * @see CustomContentTypeSupport + */ + void register(CustomContentTypeSupport customContentTypeSupport); + + /** + * Registers support for concurrency control for certain entity sets. + * @param customETagSupport handler to register + */ + void register(CustomETagSupport customETagSupport); +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java index 26d14c3..da26074 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java @@ -22,17 +22,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.olingo.server.api.debug.DebugSupport; -import org.apache.olingo.server.api.etag.CustomETagSupport; -import org.apache.olingo.server.api.processor.Processor; -import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; /** * Handles HTTP requests as OData requests. */ -public interface ODataHttpHandler { +public interface ODataHttpHandler extends ODataHandler { /** - * <p>Processes an OData request.</p> + * <p>Processes a HttpServletRequest as an OData request.</p> * <p>This includes URI parsing, content negotiation, dispatching the request * to a specific custom processor implementation for handling data and * creating the serialized content for the response object.</p> @@ -42,35 +39,15 @@ public interface ODataHttpHandler { void process(HttpServletRequest request, HttpServletResponse response); /** - * <p>Registers additional custom processor implementations for handling OData requests.</p> - * <p>If request processing requires a processor that is not registered then a - * "not implemented" exception will happen.</p> - */ - void register(Processor processor); - - /** - * Registers a service implementation for modifying the standard list of supported - * content types. - * @see CustomContentTypeSupport - */ - void register(CustomContentTypeSupport customContentTypeSupport); - - /** * Sets the split parameter which is used for service resolution. * @param split the number of path segments reserved for service resolution; default is 0 */ void setSplit(int split); - /** - * Registers support for concurrency control for certain entity sets. - * @param customETagSupport - */ - void register(CustomETagSupport customConcurrencyControlSupport); /** * Registers the debug support handler. - * @param debugSupport + * @param debugSupport handler to register */ void register(DebugSupport debugSupport); - } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java ---------------------------------------------------------------------- diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java index 29cb551..c274fb8 100644 --- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java +++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java @@ -39,7 +39,7 @@ public class OData4Impl extends ODataImpl { } @Override - public ODataHttpHandler createHandler(final ServiceMetadata edm) { - return new OData4HttpHandler(this, edm); + public ODataHttpHandler createHandler(final ServiceMetadata serviceMetadata) { + return new OData4HttpHandler(this, serviceMetadata); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/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 4971c8e..1732500 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 @@ -75,9 +75,9 @@ public class ODataDispatcher { private static final String NOT_IMPLEMENTED_MESSAGE = "not implemented"; private final UriInfo uriInfo; - private final ODataHandler handler; + private final ODataHandlerImpl handler; - public ODataDispatcher(final UriInfo uriInfo, final ODataHandler handler) { + public ODataDispatcher(final UriInfo uriInfo, final ODataHandlerImpl handler) { this.uriInfo = uriInfo; this.handler = handler; } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java deleted file mode 100644 index 3baaac7..0000000 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ /dev/null @@ -1,232 +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 java.util.LinkedList; -import java.util.List; - -import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; -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.server.api.OData; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.ODataLibraryException; -import org.apache.olingo.server.api.ODataRequest; -import org.apache.olingo.server.api.ODataResponse; -import org.apache.olingo.server.api.ODataServerError; -import org.apache.olingo.server.api.ServiceMetadata; -import org.apache.olingo.server.api.deserializer.DeserializerException; -import org.apache.olingo.server.api.etag.CustomETagSupport; -import org.apache.olingo.server.api.etag.PreconditionException; -import org.apache.olingo.server.api.processor.DefaultProcessor; -import org.apache.olingo.server.api.processor.ErrorProcessor; -import org.apache.olingo.server.api.processor.Processor; -import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; -import org.apache.olingo.server.api.serializer.RepresentationType; -import org.apache.olingo.server.api.serializer.SerializerException; -import org.apache.olingo.server.api.uri.UriInfo; -import org.apache.olingo.server.core.debug.ServerCoreDebugger; -import org.apache.olingo.server.core.uri.parser.Parser; -import org.apache.olingo.server.core.uri.parser.UriParserException; -import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; -import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException; -import org.apache.olingo.server.core.uri.validator.UriValidationException; -import org.apache.olingo.server.core.uri.validator.UriValidator; - -public class ODataHandler { - - private final OData odata; - private final ServiceMetadata serviceMetadata; - private final List<Processor> processors = new LinkedList<Processor>(); - private final ServerCoreDebugger debugger; - - private CustomContentTypeSupport customContentTypeSupport; - private CustomETagSupport customETagSupport; - - private UriInfo uriInfo; - private Exception lastThrownException; - - public ODataHandler(final OData server, final ServiceMetadata serviceMetadata, final ServerCoreDebugger debugger) { - odata = server; - this.serviceMetadata = serviceMetadata; - this.debugger = debugger; - - register(new DefaultRedirectProcessor()); - register(new DefaultProcessor()); - } - - public ODataResponse process(final ODataRequest request) { - ODataResponse response = new ODataResponse(); - final int responseHandle = debugger.startRuntimeMeasurement("ODataHandler", "process"); - try { - processInternal(request, response); - } catch (final UriValidationException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (final UriParserSemanticException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (final UriParserSyntaxException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (final UriParserException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (ContentNegotiatorException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (SerializerException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (DeserializerException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (PreconditionException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (ODataHandlerException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); - handleException(request, response, serverError, e); - } catch (ODataApplicationException e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e); - handleException(request, response, serverError, e); - } catch (Exception e) { - ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e); - handleException(request, response, serverError, e); - } - debugger.stopRuntimeMeasurement(responseHandle); - return response; - } - - private void processInternal(final ODataRequest request, final ODataResponse response) - throws ODataApplicationException, ODataLibraryException { - final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "processInternal"); - - response.setHeader(HttpHeader.ODATA_VERSION, ODataServiceVersion.V40.toString()); - try { - validateODataVersion(request); - } catch (final ODataHandlerException e) { - debugger.stopRuntimeMeasurement(measurementHandle); - throw e; - } - - final int measurementUriParser = debugger.startRuntimeMeasurement("UriParser", "parseUri"); - try { - uriInfo = new Parser(serviceMetadata.getEdm(), odata) - .parseUri(request.getRawODataPath(), request.getRawQueryPath(), null); - } catch (final ODataLibraryException e) { - debugger.stopRuntimeMeasurement(measurementUriParser); - debugger.stopRuntimeMeasurement(measurementHandle); - throw e; - } - debugger.stopRuntimeMeasurement(measurementUriParser); - - final int measurementUriValidator = debugger.startRuntimeMeasurement("UriValidator", "validate"); - final HttpMethod method = request.getMethod(); - try { - new UriValidator().validate(uriInfo, method); - } catch (final UriValidationException e) { - debugger.stopRuntimeMeasurement(measurementUriValidator); - debugger.stopRuntimeMeasurement(measurementHandle); - throw e; - } - debugger.stopRuntimeMeasurement(measurementUriValidator); - - final int measurementDispatcher = debugger.startRuntimeMeasurement("ODataDispatcher", "dispatch"); - try { - new ODataDispatcher(uriInfo, this).dispatch(request, response); - } finally { - debugger.stopRuntimeMeasurement(measurementDispatcher); - debugger.stopRuntimeMeasurement(measurementHandle); - } - } - - public void handleException(final ODataRequest request, final ODataResponse response, - final ODataServerError serverError, final Exception exception) { - final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "handleException"); - lastThrownException = exception; - ErrorProcessor exceptionProcessor; - try { - exceptionProcessor = selectProcessor(ErrorProcessor.class); - } catch (ODataHandlerException e) { - // This cannot happen since there is always an ExceptionProcessor registered. - exceptionProcessor = new DefaultProcessor(); - } - ContentType requestedContentType; - try { - requestedContentType = ContentNegotiator.doContentNegotiation( - uriInfo == null ? null : uriInfo.getFormatOption(), request, getCustomContentTypeSupport(), - RepresentationType.ERROR); - } catch (final ContentNegotiatorException e) { - requestedContentType = ContentType.JSON; - } - final int measurementError = debugger.startRuntimeMeasurement("ErrorProcessor", "processError"); - exceptionProcessor.processError(request, response, serverError, requestedContentType); - debugger.stopRuntimeMeasurement(measurementError); - debugger.stopRuntimeMeasurement(measurementHandle); - } - - private void validateODataVersion(final ODataRequest request) throws ODataHandlerException { - final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION); - if (maxVersion != null && ODataServiceVersion.isBiggerThan(ODataServiceVersion.V40.toString(), maxVersion)) { - throw new ODataHandlerException("ODataVersion not supported: " + maxVersion, - ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, maxVersion); - } - } - - <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException { - for (final Processor processor : processors) { - if (cls.isAssignableFrom(processor.getClass())) { - processor.init(odata, serviceMetadata); - return cls.cast(processor); - } - } - throw new ODataHandlerException("Processor: " + cls.getSimpleName() + " not registered.", - ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED, cls.getSimpleName()); - } - - public void register(final Processor processor) { - processors.add(0, processor); - } - - public void register(final CustomContentTypeSupport customContentTypeSupport) { - this.customContentTypeSupport = customContentTypeSupport; - } - - public CustomContentTypeSupport getCustomContentTypeSupport() { - return customContentTypeSupport; - } - - public void register(final CustomETagSupport customETagSupport) { - this.customETagSupport = customETagSupport; - } - - public CustomETagSupport getCustomETagSupport() { - return customETagSupport; - } - - public Exception getLastThrownException() { - return lastThrownException; - } - - public UriInfo getUriInfo() { - return uriInfo; - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java new file mode 100644 index 0000000..0b5a8d8 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java @@ -0,0 +1,233 @@ +/* + * 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.LinkedList; +import java.util.List; + +import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; +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.server.api.OData; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataHandler; +import org.apache.olingo.server.api.ODataLibraryException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.ODataServerError; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.deserializer.DeserializerException; +import org.apache.olingo.server.api.etag.CustomETagSupport; +import org.apache.olingo.server.api.etag.PreconditionException; +import org.apache.olingo.server.api.processor.DefaultProcessor; +import org.apache.olingo.server.api.processor.ErrorProcessor; +import org.apache.olingo.server.api.processor.Processor; +import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; +import org.apache.olingo.server.api.serializer.RepresentationType; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.core.debug.ServerCoreDebugger; +import org.apache.olingo.server.core.uri.parser.Parser; +import org.apache.olingo.server.core.uri.parser.UriParserException; +import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; +import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException; +import org.apache.olingo.server.core.uri.validator.UriValidationException; +import org.apache.olingo.server.core.uri.validator.UriValidator; + +public class ODataHandlerImpl implements ODataHandler { + + private final OData odata; + private final ServiceMetadata serviceMetadata; + private final List<Processor> processors = new LinkedList<Processor>(); + private final ServerCoreDebugger debugger; + + private CustomContentTypeSupport customContentTypeSupport; + private CustomETagSupport customETagSupport; + + private UriInfo uriInfo; + private Exception lastThrownException; + + public ODataHandlerImpl(final OData odata, final ServiceMetadata serviceMetadata, final ServerCoreDebugger debugger) { + this.odata = odata; + this.serviceMetadata = serviceMetadata; + this.debugger = debugger; + + register(new DefaultRedirectProcessor()); + register(new DefaultProcessor()); + } + + public ODataResponse process(final ODataRequest request) { + ODataResponse response = new ODataResponse(); + final int responseHandle = debugger.startRuntimeMeasurement("ODataHandler", "process"); + try { + processInternal(request, response); + } catch (final UriValidationException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (final UriParserSemanticException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (final UriParserSyntaxException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (final UriParserException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (ContentNegotiatorException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (SerializerException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (DeserializerException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (PreconditionException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (ODataHandlerException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); + } catch (ODataApplicationException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e); + handleException(request, response, serverError, e); + } catch (Exception e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e); + handleException(request, response, serverError, e); + } + debugger.stopRuntimeMeasurement(responseHandle); + return response; + } + + private void processInternal(final ODataRequest request, final ODataResponse response) + throws ODataApplicationException, ODataLibraryException { + final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "processInternal"); + + response.setHeader(HttpHeader.ODATA_VERSION, ODataServiceVersion.V40.toString()); + try { + validateODataVersion(request); + } catch (final ODataHandlerException e) { + debugger.stopRuntimeMeasurement(measurementHandle); + throw e; + } + + final int measurementUriParser = debugger.startRuntimeMeasurement("UriParser", "parseUri"); + try { + uriInfo = new Parser(serviceMetadata.getEdm(), odata) + .parseUri(request.getRawODataPath(), request.getRawQueryPath(), null); + } catch (final ODataLibraryException e) { + debugger.stopRuntimeMeasurement(measurementUriParser); + debugger.stopRuntimeMeasurement(measurementHandle); + throw e; + } + debugger.stopRuntimeMeasurement(measurementUriParser); + + final int measurementUriValidator = debugger.startRuntimeMeasurement("UriValidator", "validate"); + final HttpMethod method = request.getMethod(); + try { + new UriValidator().validate(uriInfo, method); + } catch (final UriValidationException e) { + debugger.stopRuntimeMeasurement(measurementUriValidator); + debugger.stopRuntimeMeasurement(measurementHandle); + throw e; + } + debugger.stopRuntimeMeasurement(measurementUriValidator); + + final int measurementDispatcher = debugger.startRuntimeMeasurement("ODataDispatcher", "dispatch"); + try { + new ODataDispatcher(uriInfo, this).dispatch(request, response); + } finally { + debugger.stopRuntimeMeasurement(measurementDispatcher); + debugger.stopRuntimeMeasurement(measurementHandle); + } + } + + public void handleException(final ODataRequest request, final ODataResponse response, + final ODataServerError serverError, final Exception exception) { + final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "handleException"); + lastThrownException = exception; + ErrorProcessor exceptionProcessor; + try { + exceptionProcessor = selectProcessor(ErrorProcessor.class); + } catch (ODataHandlerException e) { + // This cannot happen since there is always an ExceptionProcessor registered. + exceptionProcessor = new DefaultProcessor(); + } + ContentType requestedContentType; + try { + requestedContentType = ContentNegotiator.doContentNegotiation( + uriInfo == null ? null : uriInfo.getFormatOption(), request, getCustomContentTypeSupport(), + RepresentationType.ERROR); + } catch (final ContentNegotiatorException e) { + requestedContentType = ContentType.JSON; + } + final int measurementError = debugger.startRuntimeMeasurement("ErrorProcessor", "processError"); + exceptionProcessor.processError(request, response, serverError, requestedContentType); + debugger.stopRuntimeMeasurement(measurementError); + debugger.stopRuntimeMeasurement(measurementHandle); + } + + private void validateODataVersion(final ODataRequest request) throws ODataHandlerException { + final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION); + if (maxVersion != null && ODataServiceVersion.isBiggerThan(ODataServiceVersion.V40.toString(), maxVersion)) { + throw new ODataHandlerException("ODataVersion not supported: " + maxVersion, + ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, maxVersion); + } + } + + <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException { + for (final Processor processor : processors) { + if (cls.isAssignableFrom(processor.getClass())) { + processor.init(odata, serviceMetadata); + return cls.cast(processor); + } + } + throw new ODataHandlerException("Processor: " + cls.getSimpleName() + " not registered.", + ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED, cls.getSimpleName()); + } + + public void register(final Processor processor) { + processors.add(0, processor); + } + + public void register(final CustomContentTypeSupport customContentTypeSupport) { + this.customContentTypeSupport = customContentTypeSupport; + } + + public CustomContentTypeSupport getCustomContentTypeSupport() { + return customContentTypeSupport; + } + + public void register(final CustomETagSupport customETagSupport) { + this.customETagSupport = customETagSupport; + } + + public CustomETagSupport getCustomETagSupport() { + return customETagSupport; + } + + public Exception getLastThrownException() { + return lastThrownException; + } + + public UriInfo getUriInfo() { + return uriInfo; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/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 61581c0..492e4d4 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 @@ -55,14 +55,19 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { public static final int COPY_BUFFER_SIZE = 8192; - private final ODataHandler handler; + private final ODataHandlerImpl handler; private final ServerCoreDebugger debugger; private int split = 0; public ODataHttpHandlerImpl(final OData odata, final ServiceMetadata serviceMetadata) { debugger = new ServerCoreDebugger(odata); - handler = new ODataHandler(odata, serviceMetadata, debugger); + handler = new ODataHandlerImpl(odata, serviceMetadata, debugger); + } + + @Override + public ODataResponse process(ODataRequest request) { + return handler.process(request); } @Override @@ -76,7 +81,7 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { try { fillODataRequest(odRequest, request, split); - odResponse = handler.process(odRequest); + odResponse = process(odRequest); // ALL future methods after process must not throw exceptions! } catch (Exception e) { exception = e; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java index e5c5437..e1e5553 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java @@ -27,6 +27,7 @@ import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ODataHandler; import org.apache.olingo.server.api.ODataHttpHandler; import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.debug.DebugResponseHelper; @@ -42,6 +43,7 @@ import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.core.debug.DebugResponseHelperImpl; +import org.apache.olingo.server.core.debug.ServerCoreDebugger; import org.apache.olingo.server.core.deserializer.FixedFormatDeserializerImpl; import org.apache.olingo.server.core.deserializer.json.ODataJsonDeserializer; import org.apache.olingo.server.core.deserializer.xml.ODataXmlDeserializer; @@ -84,8 +86,13 @@ public class ODataImpl extends OData { } @Override - public ODataHttpHandler createHandler(final ServiceMetadata edm) { - return new ODataHttpHandlerImpl(this, edm); + public ODataHttpHandler createHandler(final ServiceMetadata serviceMetadata) { + return new ODataHttpHandlerImpl(this, serviceMetadata); + } + + @Override + public ODataHandler createBasicHandler(ServiceMetadata serviceMetadata) { + return new ODataHandlerImpl(this, serviceMetadata, new ServerCoreDebugger(this)); } @Override http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchFacadeImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchFacadeImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchFacadeImpl.java index 98bf563..787b7fd 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchFacadeImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchFacadeImpl.java @@ -27,7 +27,7 @@ import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerExceptio import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart; import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart; import org.apache.olingo.server.api.processor.BatchProcessor; -import org.apache.olingo.server.core.ODataHandler; +import org.apache.olingo.server.core.ODataHandlerImpl; import org.apache.olingo.server.core.deserializer.batch.BatchParserCommon; public class BatchFacadeImpl implements BatchFacade { @@ -39,8 +39,8 @@ public class BatchFacadeImpl implements BatchFacade { * @param batchProcessor batch processor * @param isStrict mode switch (currently not used) */ - public BatchFacadeImpl(final ODataHandler oDataHandler, final BatchProcessor batchProcessor, - final boolean isStrict) { + public BatchFacadeImpl(final ODataHandlerImpl oDataHandler, final BatchProcessor batchProcessor, + final boolean isStrict) { partHandler = new BatchPartHandler(oDataHandler, batchProcessor, this); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchHandler.java index 121734e..9b6c643 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchHandler.java @@ -29,14 +29,14 @@ import org.apache.olingo.server.api.batch.BatchFacade; import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerException; import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerException.MessageKeys; import org.apache.olingo.server.api.processor.BatchProcessor; -import org.apache.olingo.server.core.ODataHandler; +import org.apache.olingo.server.core.ODataHandlerImpl; import org.apache.olingo.server.core.deserializer.batch.BatchParserCommon; public class BatchHandler { private final BatchProcessor batchProcessor; - private final ODataHandler oDataHandler; + private final ODataHandlerImpl oDataHandler; - public BatchHandler(final ODataHandler oDataHandler, final BatchProcessor batchProcessor) { + public BatchHandler(final ODataHandlerImpl oDataHandler, final BatchProcessor batchProcessor) { this.batchProcessor = batchProcessor; this.oDataHandler = oDataHandler; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchPartHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchPartHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchPartHandler.java index 712ff3e..9f03362 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchPartHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batchhandler/BatchPartHandler.java @@ -28,17 +28,17 @@ import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerExceptio import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart; import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart; import org.apache.olingo.server.api.processor.BatchProcessor; -import org.apache.olingo.server.core.ODataHandler; +import org.apache.olingo.server.core.ODataHandlerImpl; import org.apache.olingo.server.core.batchhandler.referenceRewriting.BatchReferenceRewriter; public class BatchPartHandler { - private final ODataHandler oDataHandler; + private final ODataHandlerImpl oDataHandler; private final BatchProcessor batchProcessor; private final BatchFacade batchFacade; private final BatchReferenceRewriter rewriter; - public BatchPartHandler(final ODataHandler oDataHandler, final BatchProcessor processor, - final BatchFacade batchFacade) { + public BatchPartHandler(final ODataHandlerImpl oDataHandler, final BatchProcessor processor, + final BatchFacade batchFacade) { this.oDataHandler = oDataHandler; batchProcessor = processor; this.batchFacade = batchFacade; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/lib/server-core/src/test/java/org/apache/olingo/server/core/batchhandler/MockedBatchHandlerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batchhandler/MockedBatchHandlerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batchhandler/MockedBatchHandlerTest.java index 8b5513e..6347048 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/batchhandler/MockedBatchHandlerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batchhandler/MockedBatchHandlerTest.java @@ -52,7 +52,7 @@ import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart; import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart; import org.apache.olingo.server.api.processor.BatchProcessor; import org.apache.olingo.server.api.serializer.BatchSerializerException; -import org.apache.olingo.server.core.ODataHandler; +import org.apache.olingo.server.core.ODataHandlerImpl; import org.apache.olingo.server.core.deserializer.batch.BatchLineReader; import org.apache.olingo.server.core.deserializer.batch.BatchParserCommon; import org.junit.Before; @@ -67,7 +67,7 @@ public class MockedBatchHandlerTest { private static final String BATCH_REQUEST_URI = "http://localhost:8080/odata/$batch"; private static final String BASE_URI = "http://localhost:8080/odata"; private static final String CRLF = "\r\n"; - private ODataHandler oDataHandler; + private ODataHandlerImpl oDataHandler; private BatchHandler batchHandler; private int entityCounter = 1; @@ -77,7 +77,7 @@ public class MockedBatchHandlerTest { batchProcessor.init(OData.newInstance(), null); entityCounter = 1; - oDataHandler = mock(ODataHandler.class); + oDataHandler = mock(ODataHandlerImpl.class); batchHandler = new BatchHandler(oDataHandler, batchProcessor); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e07abf0b/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 new file mode 100644 index 0000000..2b6ed69 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java @@ -0,0 +1,768 @@ +/* + * 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.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import java.util.Collections; +import java.util.Locale; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; +import org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider; +import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet; +import org.apache.olingo.commons.api.ex.ODataException; +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; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.ODataServerError; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.batch.BatchFacade; +import org.apache.olingo.server.api.edmx.EdmxReference; +import org.apache.olingo.server.api.processor.ActionComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionComplexProcessor; +import org.apache.olingo.server.api.processor.ActionEntityCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionEntityProcessor; +import org.apache.olingo.server.api.processor.ActionPrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionPrimitiveProcessor; +import org.apache.olingo.server.api.processor.ActionVoidProcessor; +import org.apache.olingo.server.api.processor.BatchProcessor; +import org.apache.olingo.server.api.processor.ComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.ComplexProcessor; +import org.apache.olingo.server.api.processor.CountComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor; +import org.apache.olingo.server.api.processor.CountPrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityProcessor; +import org.apache.olingo.server.api.processor.ErrorProcessor; +import org.apache.olingo.server.api.processor.MediaEntityProcessor; +import org.apache.olingo.server.api.processor.MetadataProcessor; +import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.PrimitiveProcessor; +import org.apache.olingo.server.api.processor.PrimitiveValueProcessor; +import org.apache.olingo.server.api.processor.Processor; +import org.apache.olingo.server.api.processor.ReferenceCollectionProcessor; +import org.apache.olingo.server.api.processor.ReferenceProcessor; +import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.core.debug.ServerCoreDebugger; +import org.apache.olingo.server.tecsvc.provider.ContainerProvider; +import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; +import org.junit.Test; + +public class ODataHandlerImplTest { + + private static final String BASE_URI = "http://localhost/odata"; + + @Test + public void serviceDocumentNonDefault() throws Exception { + final ServiceDocumentProcessor processor = mock(ServiceDocumentProcessor.class); + final ODataResponse response = dispatch(HttpMethod.GET, "/", processor); + assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + + verify(processor).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); + dispatchMethodNotAllowed(HttpMethod.DELETE, "/", processor); + } + + @Test + public void serviceDocumentDefault() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "/", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + + String ct = response.getHeader(HttpHeader.CONTENT_TYPE); + assertThat(ct, containsString("application/json")); + assertThat(ct, containsString("odata.metadata=minimal")); + + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + + assertThat(doc, containsString("\"@odata.context\":\"$metadata\"")); + assertThat(doc, containsString("\"value\":")); + } + + @Test + public void serviceDocumentRedirect() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "", null); + assertEquals(HttpStatusCode.TEMPORARY_REDIRECT.getStatusCode(), response.getStatusCode()); + assertEquals(BASE_URI + "/", response.getHeader(HttpHeader.LOCATION)); + } + + @Test + public void metadataNonDefault() throws Exception { + final MetadataProcessor processor = mock(MetadataProcessor.class); + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); + assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + + verify(processor).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.PUT, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, "$metadata", processor); + } + + @Test + public void metadataDefault() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + assertEquals(ContentType.APPLICATION_XML.toContentTypeString(), response.getHeader(HttpHeader.CONTENT_TYPE)); + + assertNotNull(response.getContent()); + assertThat(IOUtils.toString(response.getContent()), + containsString("<edmx:Edmx Version=\"4.0\"")); + } + + @Test + public void maxVersionNone() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null); + assertEquals(ODataServiceVersion.V40.toString(), response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void maxVersionSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null, + HttpHeader.ODATA_MAX_VERSION, ODataServiceVersion.V40.toString(), null); + assertEquals(ODataServiceVersion.V40.toString(), response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void maxVersionNotSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null, + HttpHeader.ODATA_MAX_VERSION, ODataServiceVersion.V30.toString(), null); + + assertEquals(ODataServiceVersion.V40.toString(), response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void contentNegotiationSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", "$format=xml", null, null, null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + } + + @Test + public void contentNegotiationNotSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", "$format=not/Supported", null, null, null); + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), response.getStatusCode()); + } + + @Test + public void contentNegotiationNotSupported2() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", "$format=notSupported", null, null, null); + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), response.getStatusCode()); + } + + @Test + public void unregisteredProcessor() { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrim", null); + assertEquals(HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), response.getStatusCode()); + } + + @Test + public void uriParserExceptionResultsInRightResponseNotFound() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "NotFound", null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + } + + @Test + public void uriParserExceptionResultsInRightResponseBadRequest() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrim('122')", null); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void applicationExceptionInProcessor() throws Exception { + MetadataProcessor processor = mock(MetadataProcessor.class); + doThrow(new ODataApplicationException("msg", 425, 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(425, response.getStatusCode()); + } + + @Test + public void uriParserExceptionResultsInRightResponseEdmCause() throws Exception { + final OData odata = OData.newInstance(); + final ServiceMetadata serviceMetadata = odata.createServiceMetadata( + new CsdlAbstractEdmProvider() { + @Override + public CsdlEntitySet getEntitySet(final FullQualifiedName entityContainer, final String entitySetName) + throws ODataException { + throw new ODataException("msg"); + } + }, + Collections.<EdmxReference> emptyList()); + + ODataRequest request = new ODataRequest(); + request.setMethod(HttpMethod.GET); + request.setRawODataPath("EdmException"); + + final ODataResponse response = + new ODataHandlerImpl(odata, serviceMetadata, new ServerCoreDebugger(odata)).process(request); + assertNotNull(response); + assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + } + + @Test + public void dispatchBatch() throws Exception { + final String uri = "$batch"; + final BatchProcessor processor = mock(BatchProcessor.class); + + dispatch(HttpMethod.POST, uri, null, HttpHeader.CONTENT_TYPE, ContentType.MULTIPART_MIXED.toContentTypeString(), + processor); + verify(processor).processBatch(any(BatchFacade.class), any(ODataRequest.class), any(ODataResponse.class)); + + dispatchMethodNotAllowed(HttpMethod.GET, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + } + + @Test + public void dispatchEntitySet() throws Exception { + final String uri = "ESAllPrim"; + final EntityCollectionProcessor processor = mock(EntityCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + } + + @Test + public void dispatchEntitySetCount() throws Exception { + final String uri = "ESAllPrim/$count"; + final CountEntityCollectionProcessor processor = mock(CountEntityCollectionProcessor.class); + + 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); + } + + @Test + public void dispatchCountWithNavigation() throws Exception { + final CountEntityCollectionProcessor processor = mock(CountEntityCollectionProcessor.class); + dispatch(HttpMethod.GET, "ESAllPrim(0)/NavPropertyETTwoPrimMany/$count", processor); + + verify(processor).countEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + } + + @Test + public void dispatchFunction() throws Exception { + EntityProcessor entityProcessor = mock(EntityProcessor.class); + dispatch(HttpMethod.GET, "FICRTETKeyNav()", entityProcessor); + verify(entityProcessor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + EntityCollectionProcessor entityCollectionProcessor = mock(EntityCollectionProcessor.class); + dispatch(HttpMethod.GET, "FICRTCollESTwoKeyNavParam(ParameterInt16=123)", entityCollectionProcessor); + verify(entityCollectionProcessor).readEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + final String entityCountUri = "FICRTCollESTwoKeyNavParam(ParameterInt16=123)/$count"; + final CountEntityCollectionProcessor entityCountProcessor = mock(CountEntityCollectionProcessor.class); + dispatch(HttpMethod.GET, entityCountUri, entityCountProcessor); + verify(entityCountProcessor).countEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, entityCountUri, entityCountProcessor); + + PrimitiveProcessor primitiveProcessor = mock(PrimitiveProcessor.class); + dispatch(HttpMethod.GET, "FICRTString()", primitiveProcessor); + verify(primitiveProcessor).readPrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + // FINRTInt16 is not composable so /$value is not allowed + final String valueUri = "FINRTInt16()/$value"; + final PrimitiveValueProcessor primitiveValueProcessor = mock(PrimitiveValueProcessor.class); + dispatchMethodWithError(HttpMethod.GET, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.POST, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.PATCH, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.PUT, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.DELETE, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + + final String primitiveCollectionUri = "FICRTCollString()"; + PrimitiveCollectionProcessor primitiveCollectionProcessor = mock(PrimitiveCollectionProcessor.class); + dispatch(HttpMethod.GET, primitiveCollectionUri, primitiveCollectionProcessor); + verify(primitiveCollectionProcessor).readPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.POST, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, primitiveCollectionUri, primitiveCollectionProcessor); + + final String primitiveCountUri = "FICRTCollString()/$count"; + final CountPrimitiveCollectionProcessor primitiveCountProcessor = mock(CountPrimitiveCollectionProcessor.class); + dispatch(HttpMethod.GET, primitiveCountUri, primitiveCountProcessor); + verify(primitiveCountProcessor).countPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, primitiveCountUri, primitiveCountProcessor); + + ComplexProcessor complexProcessor = mock(ComplexProcessor.class); + dispatch(HttpMethod.GET, "FICRTCTTwoPrim()", complexProcessor); + verify(complexProcessor).readComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + ComplexCollectionProcessor complexCollectionProcessor = mock(ComplexCollectionProcessor.class); + dispatch(HttpMethod.GET, "FICRTCollCTTwoPrim()", complexCollectionProcessor); + verify(complexCollectionProcessor).readComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + final String complexCountUri = "FICRTCollCTTwoPrim()/$count"; + final CountComplexCollectionProcessor complexCountProcessor = mock(CountComplexCollectionProcessor.class); + dispatch(HttpMethod.GET, complexCountUri, complexCountProcessor); + verify(complexCountProcessor).countComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, complexCountUri, complexCountProcessor); + + final String mediaUri = "FICRTESMedia(ParameterInt16=1)/$value"; + final MediaEntityProcessor mediaProcessor = mock(MediaEntityProcessor.class); + dispatch(HttpMethod.GET, mediaUri, mediaProcessor); + verify(mediaProcessor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.POST, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, mediaUri, mediaProcessor); + } + + @Test + public void dispatchAction() throws Exception { + final ActionPrimitiveProcessor primitiveProcessor = mock(ActionPrimitiveProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_STRING, primitiveProcessor); + verify(primitiveProcessor).processActionPrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.GET, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, ContainerProvider.AIRT_STRING, primitiveProcessor); + + ActionPrimitiveCollectionProcessor primitiveCollectionProcessor = mock(ActionPrimitiveCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_STRING_TWO_PARAM, primitiveCollectionProcessor); + verify(primitiveCollectionProcessor).processActionPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionComplexProcessor complexProcessor = mock(ActionComplexProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRTCT_TWO_PRIM_PARAM, complexProcessor); + verify(complexProcessor).processActionComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionComplexCollectionProcessor complexCollectionProcessor = mock(ActionComplexCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_CT_TWO_PRIM_PARAM, complexCollectionProcessor); + verify(complexCollectionProcessor).processActionComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityProcessor entityProcessor = mock(ActionEntityProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRTET_TWO_KEY_TWO_PRIM_PARAM, entityProcessor); + verify(entityProcessor).processActionEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityCollectionProcessor entityCollectionProcessor = mock(ActionEntityCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_ET_KEY_NAV_PARAM, entityCollectionProcessor); + verify(entityCollectionProcessor).processActionEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityProcessor entityProcessorEs = mock(ActionEntityProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRTES_ALL_PRIM_PARAM, entityProcessorEs); + verify(entityProcessorEs).processActionEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityCollectionProcessor entityCollectionProcessorEs = mock(ActionEntityCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_ES_ALL_PRIM_PARAM, entityCollectionProcessorEs); + verify(entityCollectionProcessorEs).processActionEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + final ActionVoidProcessor voidProcessor = mock(ActionVoidProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT, voidProcessor); + verify(voidProcessor).processActionVoid( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.GET, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, ContainerProvider.AIRT, voidProcessor); + } + + @Test + public void dispatchEntity() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatch(HttpMethod.POST, "ESAllPrim", processor); + verify(processor).createEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + } + + @Test + public void dispatchMedia() throws Exception { + final String uri = "ESMedia(1)/$value"; + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.POST, "ESMedia", processor); + verify(processor).createMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + } + + @Test + public void dispatchMediaDeleteIndirect() throws Exception { + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + dispatch(HttpMethod.DELETE, "ESMedia(1)", processor); + + verify(processor).deleteEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + } + + @Test + public void dispatchPrimitiveProperty() throws Exception { + final String uri = "ESAllPrim(0)/PropertyString"; + final PrimitiveProcessor processor = mock(PrimitiveProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readPrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updatePrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updatePrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deletePrimitive(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + } + + @Test + public void dispatchPrimitivePropertyValue() throws Exception { + final String uri = "ESAllPrim(0)/PropertyString/$value"; + final PrimitiveValueProcessor processor = mock(PrimitiveValueProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readPrimitiveValue(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, null, HttpHeader.CONTENT_TYPE, ContentType.TEXT_PLAIN.toContentTypeString(), + processor); + verify(processor).updatePrimitiveValue( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deletePrimitiveValue(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + } + + @Test + public void dispatchPrimitiveCollectionProperty() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyString"; + final PrimitiveCollectionProcessor processor = mock(PrimitiveCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updatePrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deletePrimitiveCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + } + + @Test + public void dispatchPrimitiveCollectionPropertyCount() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyString/$count"; + final CountPrimitiveCollectionProcessor processor = mock(CountPrimitiveCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).countPrimitiveCollection(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); + } + + @Test + public void dispatchComplexProperty() throws Exception { + final String uri = "ESMixPrimCollComp(7)/PropertyComp"; + final ComplexProcessor processor = mock(ComplexProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteComplex(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + } + + @Test + public void dispatchComplexCollectionProperty() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyComp"; + final ComplexCollectionProcessor processor = mock(ComplexCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteComplexCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + } + + @Test + public void dispatchComplexCollectionPropertyCount() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyComp/$count"; + final CountComplexCollectionProcessor processor = mock(CountComplexCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).countComplexCollection(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); + } + + @Test + public void dispatchReference() throws Exception { + final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimOne/$ref"; + final String uriMany = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref"; + final ReferenceProcessor processor = mock(ReferenceProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + + dispatch(HttpMethod.POST, uriMany, processor); + verify(processor).createReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uriMany, "$id=ESTwoPrim(1)", null, null, processor); + verify(processor).deleteReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + } + + @Test + public void dispatchReferenceCollection() throws Exception { + final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref"; + final ReferenceCollectionProcessor processor = mock(ReferenceCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readReferenceCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + } + + @Test + public void noRequestContentType() throws Exception { + EntityProcessor processor = mock(EntityProcessor.class); + final ODataResponse response = dispatch(HttpMethod.POST, "ESAllPrim", null, + HttpHeader.CONTENT_TYPE, null, processor); + verifyZeroInteractions(processor); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void illegalRequestContentType() throws Exception { + EntityProcessor processor = mock(EntityProcessor.class); + final ODataResponse response = dispatch(HttpMethod.POST, "ESAllPrim", null, + HttpHeader.CONTENT_TYPE, "*/*", processor); + verifyZeroInteractions(processor); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void unsupportedRequestContentType() throws Exception { + EntityProcessor processor = mock(EntityProcessor.class); + ErrorProcessor errorProcessor = mock(ErrorProcessor.class); + dispatch(HttpMethod.POST, "ESAllPrim", null, HttpHeader.CONTENT_TYPE, "some/unsupported", errorProcessor); + verifyZeroInteractions(processor); + verify(errorProcessor).processError(any(ODataRequest.class), any(ODataResponse.class), + any(ODataServerError.class), + any(ContentType.class)); + } + + private ODataResponse dispatch(final HttpMethod method, final String path, final String query, + final String headerName, final String headerValue, final Processor processor) { + ODataRequest request = new ODataRequest(); + request.setMethod(method); + request.setRawBaseUri(BASE_URI); + if (path.isEmpty()) { + request.setRawRequestUri(BASE_URI); + } + request.setRawODataPath(path); + request.setRawQueryPath(query); + + if (headerName != null) { + request.addHeader(headerName, Collections.singletonList(headerValue)); + } + + if (headerName != HttpHeader.CONTENT_TYPE) { + request.addHeader(HttpHeader.CONTENT_TYPE, Collections.singletonList( + ContentType.JSON.toContentTypeString())); + } + + final OData odata = OData.newInstance(); + final ServiceMetadata metadata = odata.createServiceMetadata( + new EdmTechProvider(), Collections.<EdmxReference> emptyList()); + + ODataHandlerImpl handler = new ODataHandlerImpl(odata, metadata, new ServerCoreDebugger(odata)); + + if (processor != null) { + handler.register(processor); + } + + final ODataResponse response = handler.process(request); + assertNotNull(response); + return response; + } + + private ODataResponse dispatch(final HttpMethod method, final String path, final Processor processor) { + return dispatch(method, path, null, null, null, processor); + } + + private void dispatchMethodNotAllowed(final HttpMethod method, final String path, final Processor processor) { + final ODataResponse response = dispatch(method, path, processor); + assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); + assertNotNull(response.getContent()); + } + + private void dispatchMethodWithError(final HttpMethod method, final String path, final Processor processor, + final HttpStatusCode statusCode) { + final ODataResponse response = dispatch(method, path, processor); + assertEquals(statusCode.getStatusCode(), response.getStatusCode()); + assertNotNull(response.getContent()); + } +}
