[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 <ramya.vasa...@sap.com> Authored: Tue Nov 14 09:40:23 2017 +0530 Committer: ramya vasanth <ramya.vasa...@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()); + } +}