You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ra...@apache.org on 2017/11/14 04:10:38 UTC
[2/2] olingo-odata4 git commit: [OLINGO-1201] Enhancements to run
better with Netty
[OLINGO-1201] Enhancements to run better with Netty
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/03ab69c4
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/03ab69c4
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/03ab69c4
Branch: refs/heads/master
Commit: 03ab69c498d08b09fda466ed46ee2e1c20e96253
Parents: 5e21bb2
Author: ramya vasanth <ra...@sap.com>
Authored: Tue Nov 14 09:40:23 2017 +0530
Committer: ramya vasanth <ra...@sap.com>
Committed: Tue Nov 14 09:40:23 2017 +0530
----------------------------------------------------------------------
.../olingo/netty/server/api/ODataNetty.java | 56 +
.../netty/server/api/ODataNettyHandler.java | 47 +
.../server/core/ODataNettyHandlerImpl.java | 334 +++++
.../netty/server/core/ODataNettyImpl.java | 164 +++
.../server/core/ODataNettyHandlerImplTest.java | 160 +++
.../netty/server/core/ODataNettyImplTest.java | 59 +
.../server/core/ODataNettyHandlerImplTest.java | 1259 ++++++++++++++++++
7 files changed, 2079 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNetty.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNetty.java b/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNetty.java
new file mode 100644
index 0000000..a61b160
--- /dev/null
+++ b/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNetty.java
@@ -0,0 +1,56 @@
+/*
+ * 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.netty.server.api;
+
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+
+public abstract class ODataNetty extends OData {
+
+ private static final String IMPLEMENTATION = "org.apache.olingo.netty.server.core.ODataNettyImpl";
+
+ /**
+ * Use this method to create a new OData instance. Each thread/request should keep its own instance.
+ * @return a new OData instance
+ */
+ public static ODataNetty newInstance() {
+ try {
+ final Class<?> clazz = Class.forName(ODataNetty.IMPLEMENTATION);
+
+
+ /* We explicitly do not use the singleton pattern to keep the server state free
+ * and avoid class loading issues also during hot deployment.*/
+
+ final Object object = clazz.newInstance();
+
+ return (ODataNetty) object;
+
+ } catch (final Exception e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+ /**
+ * Creates a new ODataNettyHandler for handling OData requests in an HTTP context.
+ *
+ * @param serviceMetadata - metadata object required to handle an OData request
+ */
+ public abstract ODataNettyHandler createNettyHandler(ServiceMetadata serviceMetadata);
+
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNettyHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNettyHandler.java b/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNettyHandler.java
new file mode 100644
index 0000000..414ba92
--- /dev/null
+++ b/lib/server-api/src/main/java/org/apache/olingo/netty/server/api/ODataNettyHandler.java
@@ -0,0 +1,47 @@
+/*
+ * 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.netty.server.api;
+
+import java.util.Map;
+
+import org.apache.olingo.server.api.processor.Processor;
+
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponse;
+
+public interface ODataNettyHandler {
+
+ /**
+ * <p>Processes a NettyRequest 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>
+ * @param request - must be a HTTP OData request
+ * @param response - HTTP OData response
+ */
+ void processNettyRequest(HttpRequest request, HttpResponse response, Map<String, String> requestParameters);
+
+ /**
+ * <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);
+
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImpl.java
new file mode 100644
index 0000000..add6409
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImpl.java
@@ -0,0 +1,334 @@
+/*
+ * 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.netty.server.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channel;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.netty.server.api.ODataNettyHandler;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataContent;
+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.processor.Processor;
+import org.apache.olingo.server.core.ODataExceptionHelper;
+import org.apache.olingo.server.core.ODataHandlerException;
+import org.apache.olingo.server.core.ODataHandlerImpl;
+import org.apache.olingo.server.core.debug.ServerCoreDebugger;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.handler.codec.http.HttpContent;
+import io.netty.handler.codec.http.HttpMessage;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+public class ODataNettyHandlerImpl implements ODataNettyHandler {
+
+ public static final int COPY_BUFFER_SIZE = 8192;
+
+ private final ODataHandlerImpl handler;
+ private final ServerCoreDebugger debugger;
+
+ private static final String CONTEXT_PATH = "contextPath";
+ private static final String SPLIT = "split";
+
+ private int split = 0;
+
+ public ODataNettyHandlerImpl(final OData odata, final ServiceMetadata serviceMetadata) {
+ debugger = new ServerCoreDebugger(odata);
+ handler = new ODataHandlerImpl(odata, serviceMetadata, debugger);
+ }
+
+ private ODataResponse handleException(final ODataRequest odRequest, final Exception e) {
+ ODataResponse resp = new ODataResponse();
+ ODataServerError serverError;
+ if (e instanceof ODataHandlerException) {
+ serverError = ODataExceptionHelper.createServerErrorObject((ODataHandlerException) e, null);
+ } else if (e instanceof ODataLibraryException) {
+ serverError = ODataExceptionHelper.createServerErrorObject((ODataLibraryException) e, null);
+ } else {
+ serverError = ODataExceptionHelper.createServerErrorObject(e);
+ }
+ handler.handleException(odRequest, resp, serverError, e);
+ return resp;
+ }
+
+ /**
+ * Convert the OData Response to Netty Response
+ * @param response
+ * @param odResponse
+ */
+ static void convertToHttp(final HttpResponse response, final ODataResponse odResponse) {
+ response.setStatus(HttpResponseStatus.valueOf(odResponse.getStatusCode()));
+
+ for (Entry<String, List<String>> entry : odResponse.getAllHeaders().entrySet()) {
+ for (String headerValue : entry.getValue()) {
+ ((HttpMessage)response).headers().add(entry.getKey(), headerValue);
+ }
+ }
+
+ if (odResponse.getContent() != null) {
+ copyContent(odResponse.getContent(), response);
+ } else if (odResponse.getODataContent() != null) {
+ writeContent(odResponse, response);
+ }
+ }
+
+ /**
+ * Write the odata content to netty response content
+ * @param odataResponse
+ * @param response
+ */
+ static void writeContent(final ODataResponse odataResponse, final HttpResponse response) {
+ ODataContent res = odataResponse.getODataContent();
+ res.write(Channels.newChannel(new ByteBufOutputStream(((HttpContent)response).content())));
+ }
+
+ static void copyContent(final InputStream inputStream, final HttpResponse response) {
+ copyContent(Channels.newChannel(inputStream), response);
+ }
+
+ /**
+ * Copy OData content to netty content
+ * @param input
+ * @param response
+ */
+ static void copyContent(final ReadableByteChannel input, final HttpResponse response) {
+ WritableByteChannel output = null;
+ try {
+ ByteBuffer inBuffer = ByteBuffer.allocate(COPY_BUFFER_SIZE);
+ output = Channels.newChannel(new ByteBufOutputStream(((HttpContent)response).content()));
+ while (input.read(inBuffer) > 0) {
+ inBuffer.flip();
+ output.write(inBuffer);
+ inBuffer.clear();
+ }
+ } catch (IOException e) {
+ throw new ODataRuntimeException("Error on reading request content", e);
+ } finally {
+ closeStream(input);
+ closeStream(output);
+ }
+ }
+
+ private static void closeStream(final Channel closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Extract the information part of Netty Request and fill OData Request
+ * @param odRequest
+ * @param httpRequest
+ * @param split
+ * @param contextPath
+ * @return
+ * @throws ODataLibraryException
+ */
+ private ODataRequest fillODataRequest(final ODataRequest odRequest, final HttpRequest httpRequest,
+ final int split, final String contextPath) throws ODataLibraryException {
+ final int requestHandle = debugger.startRuntimeMeasurement("ODataHttpHandlerImpl", "fillODataRequest");
+ try {
+ ByteBuf byteBuf = ((HttpContent)httpRequest).content();
+ ByteBufInputStream inputStream = new ByteBufInputStream(byteBuf);
+ odRequest.setBody(inputStream);
+
+ odRequest.setProtocol(httpRequest.protocolVersion().text());
+ odRequest.setMethod(extractMethod(httpRequest));
+ int innerHandle = debugger.startRuntimeMeasurement("ODataNettyHandlerImpl", "copyHeaders");
+ copyHeaders(odRequest, httpRequest);
+ debugger.stopRuntimeMeasurement(innerHandle);
+ innerHandle = debugger.startRuntimeMeasurement("ODataNettyHandlerImpl", "fillUriInformation");
+ fillUriInformationFromHttpRequest(odRequest, httpRequest, split, contextPath);
+ debugger.stopRuntimeMeasurement(innerHandle);
+
+ return odRequest;
+ } finally {
+ debugger.stopRuntimeMeasurement(requestHandle);
+ }
+ }
+
+ static HttpMethod extractMethod(final HttpRequest httpRequest) throws ODataLibraryException {
+ final HttpMethod httpRequestMethod;
+ try {
+ httpRequestMethod = HttpMethod.valueOf(httpRequest.method().name());
+ } catch (IllegalArgumentException e) {
+ throw new ODataHandlerException("HTTP method not allowed" +
+ httpRequest.method().name(), e,
+ ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED,
+ httpRequest.method().name());
+ }
+ try {
+ if (httpRequestMethod == HttpMethod.POST) {
+ String xHttpMethod = httpRequest.headers().
+ get(HttpHeader.X_HTTP_METHOD);
+ String xHttpMethodOverride = httpRequest.headers().
+ get(HttpHeader.X_HTTP_METHOD_OVERRIDE);
+
+ if (xHttpMethod == null && xHttpMethodOverride == null) {
+ return httpRequestMethod;
+ } else if (xHttpMethod == null) {
+ return HttpMethod.valueOf(xHttpMethodOverride);
+ } else if (xHttpMethodOverride == null) {
+ return HttpMethod.valueOf(xHttpMethod);
+ } else {
+ if (!xHttpMethod.equalsIgnoreCase(xHttpMethodOverride)) {
+ throw new ODataHandlerException("Ambiguous X-HTTP-Methods",
+ ODataHandlerException.MessageKeys.AMBIGUOUS_XHTTP_METHOD, xHttpMethod, xHttpMethodOverride);
+ }
+ return HttpMethod.valueOf(xHttpMethod);
+ }
+ } else {
+ return httpRequestMethod;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new ODataHandlerException("Invalid HTTP method" +
+ httpRequest.method().name(), e,
+ ODataHandlerException.MessageKeys.INVALID_HTTP_METHOD,
+ httpRequest.method().name());
+ }
+ }
+
+ /**
+ * Fetch the uri information parsing netty request url
+ * @param odRequest
+ * @param httpRequest
+ * @param split
+ * @param contextPath
+ */
+ static void fillUriInformationFromHttpRequest(final ODataRequest odRequest, final HttpRequest httpRequest,
+ final int split, final String contextPath) {
+ String rawRequestUri = httpRequest.uri();
+ if (rawRequestUri.indexOf("?") != -1) {
+ rawRequestUri = rawRequestUri.substring(0, rawRequestUri.indexOf("?"));
+ }
+
+ String rawODataPath;
+ if (!"".equals(contextPath)) {
+ int beginIndex = rawRequestUri.indexOf(contextPath) + contextPath.length();
+ rawODataPath = rawRequestUri.substring(beginIndex);
+ } else {
+ rawODataPath = rawRequestUri;
+ }
+
+ String rawServiceResolutionUri = null;
+ if (split > 0) {
+ rawServiceResolutionUri = rawODataPath;
+ for (int i = 0; i < split; i++) {
+ int index = rawODataPath.indexOf('/', 1);
+ if (-1 == index) {
+ rawODataPath = "";
+ break;
+ } else {
+ rawODataPath = rawODataPath.substring(index);
+ }
+ }
+ int end = rawServiceResolutionUri.length() - rawODataPath.length();
+ rawServiceResolutionUri = rawServiceResolutionUri.substring(0, end);
+ }
+
+ String rawBaseUri = rawRequestUri.substring(0, rawRequestUri.length() - rawODataPath.length());
+
+ int index = httpRequest.uri().indexOf('?');
+ String queryString = null;
+ if (index != -1) {
+ queryString = httpRequest.uri().substring(index + 1);
+ }
+ odRequest.setRawQueryPath(queryString);
+ odRequest.setRawRequestUri(rawRequestUri
+ + (queryString == null ? "" : "?" + queryString));
+ odRequest.setRawODataPath(rawODataPath);
+ odRequest.setRawBaseUri(rawBaseUri);
+ odRequest.setRawServiceResolutionUri(rawServiceResolutionUri);
+ }
+
+ /**
+ * Copy the headers part of Netty Request to OData Request
+ * @param odRequest
+ * @param req
+ */
+ static void copyHeaders(ODataRequest odRequest, final HttpRequest req) {
+ final Set<String> headers = req.headers().names();
+ Iterator<String> headerNames = headers.iterator();
+ while (headerNames.hasNext()) {
+ final String headerName = headerNames.next();
+ final List<String> headerValues = req.headers().getAll(headerName);
+ odRequest.addHeader(headerName, headerValues);
+ }
+ }
+
+@SuppressWarnings("unused")
+@Override
+public void processNettyRequest(HttpRequest request, HttpResponse response,
+ Map<String, String> requestParameters) {
+ ODataRequest odRequest = new ODataRequest();
+ Exception exception = null;
+ ODataResponse odResponse;
+
+ final int processMethodHandle =
+ debugger.startRuntimeMeasurement("ODataNettyHandlerImpl", "process");
+ try {
+ fillODataRequest(odRequest, request,
+ requestParameters.get(SPLIT) != null? Integer.parseInt(requestParameters.get(SPLIT)) : split,
+ requestParameters.get(CONTEXT_PATH));
+
+ odResponse = process(odRequest);
+ // ALL future methods after process must not throw exceptions!
+ } catch (Exception e) {
+ exception = e;
+ odResponse = handleException(odRequest, e);
+ }
+ debugger.stopRuntimeMeasurement(processMethodHandle);
+
+ convertToHttp(response, odResponse);
+ }
+
+ public ODataResponse process(ODataRequest request) {
+ return handler.process(request);
+ }
+
+ @Override
+ public void register(Processor processor) {
+ handler.register(processor);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java
new file mode 100644
index 0000000..113eb03
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/netty/server/core/ODataNettyImpl.java
@@ -0,0 +1,164 @@
+/*
+ * 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.netty.server.core;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider;
+import org.apache.olingo.commons.api.edmx.EdmxReference;
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.netty.server.api.ODataNetty;
+import org.apache.olingo.netty.server.api.ODataNettyHandler;
+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;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer;
+import org.apache.olingo.server.api.deserializer.ODataDeserializer;
+import org.apache.olingo.server.api.etag.ETagHelper;
+import org.apache.olingo.server.api.etag.ServiceMetadataETagSupport;
+import org.apache.olingo.server.api.prefer.Preferences;
+import org.apache.olingo.server.api.serializer.EdmAssistedSerializer;
+import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
+import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.uri.UriHelper;
+
+public class ODataNettyImpl extends ODataNetty {
+
+ private static OData odata;
+ private static final String IMPLEMENTATION = "org.apache.olingo.server.core.ODataImpl";
+
+ static {
+ try {
+ final Class<?> clazz = Class.forName(IMPLEMENTATION);
+
+ /*
+ * We explicitly do not use the singleton pattern to keep the server state free
+ * and avoid class loading issues also during hot deployment.
+ */
+ final Object object = clazz.newInstance();
+ odata = (OData) object;
+ } catch (Exception e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ @Override
+ public ODataNettyHandler createNettyHandler(ServiceMetadata serviceMetadata) {
+ return new ODataNettyHandlerImpl(this, serviceMetadata);
+ }
+
+ @Override
+ public ODataSerializer createSerializer(ContentType contentType) throws SerializerException {
+ return odata.createSerializer(contentType);
+ }
+
+ @Override
+ public ODataSerializer createSerializer(final ContentType contentType,
+ final List<String> versions) throws SerializerException {
+ return odata.createSerializer(contentType, versions);
+ }
+
+ @Override
+ public FixedFormatSerializer createFixedFormatSerializer() {
+ return odata.createFixedFormatSerializer();
+ }
+
+ @Override
+ public FixedFormatDeserializer createFixedFormatDeserializer() {
+ return odata.createFixedFormatDeserializer();
+ }
+
+ @Override
+ public ODataHttpHandler createHandler(ServiceMetadata serviceMetadata) {
+ return odata.createHandler(serviceMetadata);
+ }
+
+ @Override
+ public ODataHandler createRawHandler(ServiceMetadata serviceMetadata) {
+ return odata.createRawHandler(serviceMetadata);
+ }
+
+ @Override
+ public ServiceMetadata createServiceMetadata(CsdlEdmProvider edmProvider, List<EdmxReference> references) {
+ return odata.createServiceMetadata(edmProvider, references);
+ }
+
+ @Override
+ public ServiceMetadata createServiceMetadata(CsdlEdmProvider edmProvider, List<EdmxReference> references,
+ ServiceMetadataETagSupport serviceMetadataETagSupport) {
+ return odata.createServiceMetadata(edmProvider, references, serviceMetadataETagSupport);
+ }
+
+ @Override
+ public UriHelper createUriHelper() {
+ return odata.createUriHelper();
+ }
+
+ @Override
+ public ODataDeserializer createDeserializer(ContentType contentType) throws DeserializerException {
+ return odata.createDeserializer(contentType);
+ }
+
+ @Override
+ public ODataDeserializer createDeserializer(ContentType contentType, ServiceMetadata metadata)
+ throws DeserializerException {
+ return odata.createDeserializer(contentType);
+ }
+
+ @Override
+ public EdmPrimitiveType createPrimitiveTypeInstance(EdmPrimitiveTypeKind kind) {
+ return odata.createPrimitiveTypeInstance(kind);
+ }
+
+ @Override
+ public ETagHelper createETagHelper() {
+ return odata.createETagHelper();
+ }
+
+ @Override
+ public Preferences createPreferences(Collection<String> preferHeaders) {
+ return odata.createPreferences(preferHeaders);
+ }
+
+ @Override
+ public DebugResponseHelper createDebugResponseHelper(String debugFormat) {
+ return odata.createDebugResponseHelper(debugFormat);
+ }
+
+ @Override
+ public EdmAssistedSerializer createEdmAssistedSerializer(ContentType contentType) throws SerializerException {
+ return odata.createEdmAssistedSerializer(contentType);
+ }
+
+ @Override
+ public EdmDeltaSerializer createEdmDeltaSerializer(ContentType contentType, List<String> versions)
+ throws SerializerException {
+ return odata.createEdmDeltaSerializer(contentType, versions);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java b/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java
new file mode 100644
index 0000000..7a1a01a
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.netty.server.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.server.api.ODataLibraryException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.junit.Test;
+
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpRequest;
+
+public class ODataNettyHandlerImplTest {
+
+ @Test
+ public void extractMethodForNettyRequest() throws Exception {
+ String[][] mm = {
+ { "GET", null, null, "GET" },
+ { "GET", "xxx", "yyy", "GET" },
+ { "PUT", "xxx", "yyy", "PUT" },
+ { "DELETE", "xxx", "yyy", "DELETE" },
+ { "PATCH", "xxx", "yyy", "PATCH" },
+
+ { "POST", null, null, "POST" },
+ { "POST", null, "GET", "GET" },
+ { "POST", null, "PATCH", "PATCH" },
+
+ { "POST", "GET", null, "GET" },
+ { "POST", "PATCH", null, "PATCH" },
+
+ { "POST", "GET", "GET", "GET" },
+ { "HEAD", null, null, "HEAD" }
+ };
+
+ for (String[] m : mm) {
+
+ HttpRequest hr = mock(HttpRequest.class);
+ io.netty.handler.codec.http.HttpMethod hm = mock(io.netty.handler.codec.http.HttpMethod.class);
+ when(hr.method()).thenReturn(hm);
+ when(hm.name()).thenReturn(m[0]);
+ HttpHeaders hh = mock(HttpHeaders.class);
+ when(hr.headers()).thenReturn(hh);
+ when(hh.get("X-HTTP-Method")).thenReturn(m[1]);
+ when(hh.get("X-HTTP-Method-Override")).thenReturn(m[2]);
+
+ assertEquals(HttpMethod.valueOf(m[3]), ODataNettyHandlerImpl.extractMethod(hr));
+ }
+ }
+
+ @Test
+ public void extractMethodFailForNettyRequest() throws Exception {
+ String[][] mm = {
+ { "POST", "bla", null },
+ { "POST", "PUT", "PATCH" },
+ { "OPTIONS", null, null }
+ };
+
+ for (String[] m : mm) {
+
+ HttpRequest hr = mock(HttpRequest.class);
+ io.netty.handler.codec.http.HttpMethod hm = mock(io.netty.handler.codec.http.HttpMethod.class);
+ when(hr.method()).thenReturn(hm);
+
+ when(hm.name()).thenReturn(m[0]);
+ HttpHeaders hh = mock(HttpHeaders.class);
+ when(hr.headers()).thenReturn(hh);
+ when(hh.get("X-HTTP-Method")).thenReturn(m[1]);
+ when(hh.get("X-HTTP-Method-Override")).thenReturn(m[2]);
+
+ try {
+ ODataNettyHandlerImpl.extractMethod(hr);
+ fail();
+ } catch (ODataLibraryException e) {
+ // expected
+ }
+ }
+ }
+
+ @Test
+ public void extractUriForNettyRequests() {
+
+ //@formatter:off (Eclipse formatter)
+ //CHECKSTYLE:OFF (Maven checkstyle)
+ String [][] uris = {
+ /* 0: cp 1: sr 2: od 3: qp 4: spl */
+ { "", "", "", "", "0"},
+ { "", "", "/", "", "0"},
+ { "", "", "/od", "", "0"},
+ { "", "", "/od/", "", "0"},
+
+ { "/cp", "", "", "", "0"},
+ { "/cp", "", "/", "", "0"},
+ { "/cp", "", "/od", "", "0"},
+ { "", "/sr", "", "", "1"},
+ { "", "/sr", "/", "", "1"},
+ { "", "/sr", "/od", "", "1"},
+ { "", "/sr/sr", "", "", "2"},
+ { "", "/sr/sr", "/", "", "2"},
+ { "", "/sr/sr", "/od", "", "2"},
+
+ { "/cp", "/sr", "/", "", "1"},
+ { "/cp", "/sr", "/od", "", "1"},
+
+ { "", "", "", "qp", "0"},
+ { "", "", "/", "qp", "0"},
+ { "/cp", "/sr", "/od", "qp", "1"},
+
+ { "/c%20p", "/s%20r", "/o%20d", "p+q", "1"},
+ };
+ //@formatter:on
+ // CHECKSTYLE:on
+
+ for (String[] p : uris) {
+ HttpRequest hr = mock(HttpRequest.class);
+
+ String requestUrl = p[0] + p[1] + p[2];
+ if (!p[3].equals("") || p[3].length() > 0) {
+ requestUrl += "?$" + p[3];
+ }
+
+ when(hr.uri()).thenReturn(requestUrl);
+
+ ODataRequest odr = new ODataRequest();
+ ODataNettyHandlerImpl.fillUriInformationFromHttpRequest(odr, hr, Integer.parseInt(p[4]), p[0]);
+
+ String rawBaseUri = p[0] + p[1];
+ String rawODataPath = p[2];
+ String rawQueryPath = "".equals(p[3]) ? null : "$" + p[3];
+ String rawRequestUri = p[0] + p[1] + p[2] + ("".equals(p[3]) ? "" : "?$" + p[3]);
+ String rawServiceResolutionUri = "".equals(p[1]) ? null : p[1];
+
+ assertEquals(rawBaseUri, odr.getRawBaseUri());
+ assertEquals(rawODataPath, odr.getRawODataPath());
+ assertEquals(rawQueryPath, odr.getRawQueryPath());
+ assertEquals(rawRequestUri, odr.getRawRequestUri());
+ assertEquals(rawServiceResolutionUri, odr.getRawServiceResolutionUri());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyImplTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyImplTest.java b/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyImplTest.java
new file mode 100644
index 0000000..e71ffbd
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/netty/server/core/ODataNettyImplTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.netty.server.core;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.netty.server.api.ODataNetty;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.junit.Test;
+
+public class ODataNettyImplTest {
+
+ private final ODataNetty odata = ODataNetty.newInstance();
+
+ @Test
+ public void serializerSupportedFormats() throws SerializerException {
+ assertNotNull(odata.createSerializer(ContentType.JSON_NO_METADATA));
+ assertNotNull(odata.createSerializer(ContentType.JSON));
+ assertNotNull(odata.createSerializer(ContentType.APPLICATION_JSON));
+ assertNotNull(odata.createSerializer(ContentType.JSON_FULL_METADATA));
+
+ }
+
+ @Test
+ public void deserializerSupportedFormats() throws DeserializerException {
+ assertNotNull(odata.createDeserializer(ContentType.JSON_NO_METADATA));
+ assertNotNull(odata.createDeserializer(ContentType.JSON));
+ assertNotNull(odata.createDeserializer(ContentType.JSON_FULL_METADATA));
+ assertNotNull(odata.createDeserializer(ContentType.APPLICATION_JSON));
+ }
+
+ @Test
+ public void serializerFixedFormat() throws DeserializerException {
+ assertNotNull(odata.createFixedFormatSerializer());
+ }
+
+ @Test
+ public void deserializerFixedFormat() throws DeserializerException {
+ assertNotNull(odata.createFixedFormatDeserializer());
+ }
+}