diff --git a/itests/hive-unit/pom.xml b/itests/hive-unit/pom.xml index dbc9136..90b5115 100644 --- a/itests/hive-unit/pom.xml +++ b/itests/hive-unit/pom.xml @@ -65,6 +65,57 @@ ${project.version} + + com.github.tomakehurst + wiremock + 1.55 + + + standalone + + + org.mortbay.jetty + jetty + + + com.google.guava + guava + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.httpcomponents + httpclient + + + org.skyscreamer + jsonassert + + + xmlunit + xmlunit + + + com.jayway.jsonpath + json-path + + + net.sf.jopt-simple + jopt-simple + + + + diff --git a/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/MiniHS2WithWireMock.java b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/MiniHS2WithWireMock.java new file mode 100644 index 0000000..c9c4736 --- /dev/null +++ b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/MiniHS2WithWireMock.java @@ -0,0 +1,66 @@ +/** + * 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.hive.jdbc.miniHS2.wiremock; + +import java.util.HashMap; + +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hive.jdbc.miniHS2.MiniHS2; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.Options; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; + +public class MiniHS2WithWireMock { + + MiniHS2 miniHS2; + WireMockServer proxyingService; + WireMock proxyingServiceAdmin; + WireMockTestClient testClient; + + private void setupWireMockServer(WireMockConfiguration options) { + if (options.portNumber() == Options.DEFAULT_PORT) { + options.dynamicPort(); + } + proxyingService = new WireMockServer(options); + proxyingService.start(); + } + + public void init() throws Exception { + HiveConf hiveConf = new HiveConf(); + hiveConf.setVar(HiveConf.ConfVars.HIVE_SERVER2_TRANSPORT_MODE, "http"); + miniHS2 = new MiniHS2(hiveConf); + miniHS2.start(new HashMap()); + setupWireMockServer(wireMockConfig()); + proxyingService.start(); + proxyingServiceAdmin = new WireMock(proxyingService.port()); + testClient = new WireMockTestClient(proxyingService.port()); + + WireMock.configureFor(miniHS2.getHttpPort()); + } + + public void destroy() throws Exception { + miniHS2.stop(); + proxyingService.stop(); + } + +} diff --git a/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockHttpHeader.java b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockHttpHeader.java new file mode 100644 index 0000000..6e3117b --- /dev/null +++ b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockHttpHeader.java @@ -0,0 +1,26 @@ +package org.apache.hive.jdbc.miniHS2.wiremock; + +public class WireMockHttpHeader { + + private String name; + private String value; + + public static WireMockHttpHeader withHeader(String name, String value) { + return new WireMockHttpHeader(name, value); + } + + public WireMockHttpHeader(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + +} diff --git a/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockResponse.java b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockResponse.java new file mode 100644 index 0000000..7a65261 --- /dev/null +++ b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockResponse.java @@ -0,0 +1,53 @@ +package org.apache.hive.jdbc.miniHS2.wiremock; + +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.Multimap; +import org.apache.http.Header; +import org.apache.http.HttpResponse; + +import java.nio.charset.Charset; + +import static com.github.tomakehurst.wiremock.common.HttpClientUtils.getEntityAsByteArrayAndCloseStream; +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.collect.Iterables.getFirst; + +public class WireMockResponse { + + private final HttpResponse httpResponse; + private final byte[] content; + + public WireMockResponse(HttpResponse httpResponse) { + this.httpResponse = httpResponse; + content = getEntityAsByteArrayAndCloseStream((wiremock.org.apache.http.HttpResponse)(httpResponse)); + } + + public int statusCode() { + return httpResponse.getStatusLine().getStatusCode(); + } + + public String content() { + if(content==null) { + return null; + } + return new String(content, Charset.forName(UTF_8.name())); + } + + public byte[] binaryContent() { + return content; + } + + public String firstHeader(String key) { + return getFirst(headers().get(key), null); + } + + public Multimap headers() { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + + for (Header header: httpResponse.getAllHeaders()) { + builder.put(header.getName(), header.getValue()); + } + + return builder.build(); + } + +} diff --git a/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockTestClient.java b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockTestClient.java new file mode 100644 index 0000000..dcd6bae --- /dev/null +++ b/itests/hive-unit/src/main/java/org/apache/hive/jdbc/miniHS2/wiremock/WireMockTestClient.java @@ -0,0 +1,222 @@ +package org.apache.hive.jdbc.miniHS2.wiremock; + + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.*; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URI; + +import static com.github.tomakehurst.wiremock.http.MimeType.JSON; +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.net.HttpURLConnection.HTTP_CREATED; +import static java.net.HttpURLConnection.HTTP_OK; + +public class WireMockTestClient { + public class GenericHttpUriRequest extends HttpRequestBase { + + private final String methodName; + + public GenericHttpUriRequest(String methodName, String url) { + this.methodName = methodName; + setURI(URI.create(url)); + } + + @Override + public String getMethod() { + return methodName; + } + } + private static final String LOCAL_WIREMOCK_ROOT = "http://%s:%d%s"; + private static final String LOCAL_WIREMOCK_NEW_RESPONSE_URL = "http://%s:%d/__admin/mappings/new"; + private static final String LOCAL_WIREMOCK_RESET_URL = "http://%s:%d/__admin/reset"; + private static final String LOCAL_WIREMOCK_RESET_DEFAULT_MAPPINS_URL = "http://%s:%d/__admin/mappings/reset"; + + private int port; + private String address; + + public WireMockTestClient(int port, String address) { + this.port = port; + this.address = address; + } + + public WireMockTestClient(int port) { + this(port, "localhost"); + } + + public WireMockTestClient() { + this(8080); + } + + private String mockServiceUrlFor(String path) { + return String.format(LOCAL_WIREMOCK_ROOT, address, port, path); + } + + private String newMappingUrl() { + return String.format(LOCAL_WIREMOCK_NEW_RESPONSE_URL, address, port); + } + + private String resetUrl() { + return String.format(LOCAL_WIREMOCK_RESET_URL, address, port); + } + + private String resetDefaultMappingsUrl() { + return String.format(LOCAL_WIREMOCK_RESET_DEFAULT_MAPPINS_URL, address, port); + } + + public WireMockResponse get(String url, WireMockHttpHeader... headers) { + String actualUrl = URI.create(url).isAbsolute() ? url : mockServiceUrlFor(url); + HttpUriRequest httpRequest = new HttpGet(actualUrl); + return executeMethodAndCovertExceptions(httpRequest, headers); + } + + public WireMockResponse getViaProxy(String url) { + return getViaProxy(url, port); + } + + public WireMockResponse getViaProxy(String url, int proxyPort) { + URI targetUri = URI.create(url); + HttpHost proxy = new HttpHost(address, proxyPort, targetUri.getScheme()); + HttpClient httpClientUsingProxy = HttpClientBuilder.create() + .disableAuthCaching() + .disableAutomaticRetries() + .disableCookieManagement() + .disableRedirectHandling() + .setProxy(proxy) + .build(); + + try { + HttpHost target = new HttpHost(targetUri.getHost(), targetUri.getPort(), targetUri.getScheme()); + HttpGet req = new HttpGet(targetUri.getPath() + + (isNullOrEmpty(targetUri.getQuery()) ? "" : "?" + targetUri.getQuery())); + req.removeHeaders("Host"); + + System.out.println("executing request to " + targetUri + "(" + target + ") via " + proxy); + HttpResponse httpResponse = httpClientUsingProxy.execute(target, req); + return new WireMockResponse(httpResponse); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + + public WireMockResponse put(String url, WireMockHttpHeader... headers) { + HttpUriRequest httpRequest = new HttpPut(mockServiceUrlFor(url)); + return executeMethodAndCovertExceptions(httpRequest, headers); + } + + public WireMockResponse putWithBody(String url, String body, String contentType, WireMockHttpHeader... headers) { + HttpPut httpPut = new HttpPut(mockServiceUrlFor(url)); + return requestWithBody(httpPut, body, contentType, headers); + } + + public WireMockResponse patchWithBody(String url, String body, String contentType, WireMockHttpHeader... headers) { + HttpPatch httpPatch = new HttpPatch(mockServiceUrlFor(url)); + return requestWithBody(httpPatch, body, contentType, headers); + } + + private WireMockResponse requestWithBody( + HttpEntityEnclosingRequestBase request, String body, String contentType, WireMockHttpHeader... headers) { + request.setEntity(new StringEntity(body, ContentType.create(contentType, "utf-8"))); + return executeMethodAndCovertExceptions(request, headers); + } + + public WireMockResponse postWithBody(String url, String body, String bodyMimeType, String bodyEncoding) { + return post(url, new StringEntity(body, ContentType.create(bodyMimeType, bodyEncoding))); + } + + public WireMockResponse postWithChunkedBody(String url, byte[] body) { + return post(url, new InputStreamEntity(new ByteArrayInputStream(body), -1)); + } + + public WireMockResponse post(String url, HttpEntity entity) { + HttpPost httpPost = new HttpPost(mockServiceUrlFor(url)); + httpPost.setEntity(entity); + return executeMethodAndCovertExceptions(httpPost); + } + + public WireMockResponse patchWithBody(String url, String body, String bodyMimeType, String bodyEncoding) { + return patch(url, new StringEntity(body, ContentType.create(bodyMimeType, bodyEncoding))); + } + + public WireMockResponse patch(String url, HttpEntity entity) { + HttpPatch httpPatch = new HttpPatch(mockServiceUrlFor(url)); + httpPatch.setEntity(entity); + return executeMethodAndCovertExceptions(httpPatch); + } + + public void addResponse(String responseSpecJson) { + int status = postJsonAndReturnStatus(newMappingUrl(), responseSpecJson); + if (status != HTTP_CREATED) { + throw new RuntimeException("Returned status code was " + status); + } + } + + public void resetMappings() { + int status = postEmptyBodyAndReturnStatus(resetUrl()); + if (status != HTTP_OK) { + throw new RuntimeException("Returned status code was " + status); + } + } + + public void resetDefaultMappings() { + int status = postEmptyBodyAndReturnStatus(resetDefaultMappingsUrl()); + if (status != HTTP_OK) { + throw new RuntimeException("Returned status code was " + status); + } + } + + private int postJsonAndReturnStatus(String url, String json) { + HttpPost post = new HttpPost(url); + try { + if (json != null) { + post.setEntity(new StringEntity(json, ContentType.create(JSON.toString(), "utf-8"))); + } + HttpResponse httpResponse = httpClient().execute(post); + return httpResponse.getStatusLine().getStatusCode(); + } catch (RuntimeException re) { + throw re; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private int postEmptyBodyAndReturnStatus(String url) { + return postJsonAndReturnStatus(url, null); + } + + private WireMockResponse executeMethodAndCovertExceptions(HttpUriRequest httpRequest, WireMockHttpHeader... headers) { + try { + for (WireMockHttpHeader header : headers) { + httpRequest.addHeader(header.getName(), header.getValue()); + } + HttpResponse httpResponse = httpClient().execute(httpRequest); + return new WireMockResponse(httpResponse); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + + private static HttpClient httpClient() { + return HttpClientBuilder.create() + .disableAuthCaching() + .disableAutomaticRetries() + .disableCookieManagement() + .disableRedirectHandling() + .disableContentCompression() + .build(); + } + + public WireMockResponse request(final String methodName, String url, WireMockHttpHeader... headers) { + HttpUriRequest httpRequest = new GenericHttpUriRequest(methodName, mockServiceUrlFor(url)); + return executeMethodAndCovertExceptions(httpRequest, headers); + } + +} diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/miniHS2/wiremock/TestMiniHS2WithWireMock.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/miniHS2/wiremock/TestMiniHS2WithWireMock.java new file mode 100644 index 0000000..b6095ab --- /dev/null +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/miniHS2/wiremock/TestMiniHS2WithWireMock.java @@ -0,0 +1,34 @@ +package org.apache.hive.jdbc.miniHS2.wiremock; + +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; + +public class TestMiniHS2WithWireMock { + MiniHS2WithWireMock miniHS2WithWireMock; + + @Before + public void beforeTests() throws Exception { + miniHS2WithWireMock.init(); + } + + @After + public void tearDown() throws Exception { + miniHS2WithWireMock.destroy(); + } + + @Test + public void successfullyGetsResponseFromHS2ViaProxy() { + miniHS2WithWireMock.proxyingServiceAdmin.register(any(urlMatching(".*")).atPriority(10). + willReturn(aResponse() + .proxiedFrom(miniHS2WithWireMock.miniHS2.getJdbcURL("default")))); + + WireMockResponse response = miniHS2WithWireMock.testClient.get("/"); + assertTrue(response.statusCode() == 200); + } + +} diff --git a/itests/pom.xml b/itests/pom.xml index bf909eb..28c3742 100644 --- a/itests/pom.xml +++ b/itests/pom.xml @@ -41,6 +41,59 @@ hive-jmh + + + com.github.tomakehurst + wiremock + 1.55 + + + standalone + + + org.mortbay.jetty + jetty + + + com.google.guava + guava + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.httpcomponents + httpclient + + + org.skyscreamer + jsonassert + + + xmlunit + xmlunit + + + com.jayway.jsonpath + json-path + + + net.sf.jopt-simple + jopt-simple + + + + + hadoop-2