You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@calcite.apache.org by Julian Hyde <jh...@apache.org> on 2018/06/15 22:01:35 UTC
Re: calcite-avatica git commit: CALCITE-2285 Support client cert
keystore for Avatica Client
Josh,
Please remember to use brackets about the JIRA case number in the
commit. Not "CALCITE-2285" but "[CALCITE-2285]".
You write great code and great commit comments, but I'd just like to
keep the commit format consistent across all committers.
Julian
On Thu, Jun 14, 2018 at 9:41 AM, <el...@apache.org> wrote:
> Repository: calcite-avatica
> Updated Branches:
> refs/heads/master 9a3784529 -> 97c8a1612
>
>
> CALCITE-2285 Support client cert keystore for Avatica Client
>
> Closes #57
>
> Signed-off-by: Josh Elser <el...@apache.org>
>
>
> Project: http://git-wip-us.apache.org/repos/asf/calcite-avatica/repo
> Commit: http://git-wip-us.apache.org/repos/asf/calcite-avatica/commit/97c8a161
> Tree: http://git-wip-us.apache.org/repos/asf/calcite-avatica/tree/97c8a161
> Diff: http://git-wip-us.apache.org/repos/asf/calcite-avatica/diff/97c8a161
>
> Branch: refs/heads/master
> Commit: 97c8a1612220e46fe70fed3a92082e55248f868e
> Parents: 9a37845
> Author: Karan Mehta <k....@salesforce.com>
> Authored: Thu Jun 14 12:24:38 2018 -0400
> Committer: Josh Elser <el...@apache.org>
> Committed: Thu Jun 14 12:39:40 2018 -0400
>
> ----------------------------------------------------------------------
> .../avatica/BuiltInConnectionProperty.java | 9 ++
> .../calcite/avatica/ConnectionConfig.java | 6 +
> .../calcite/avatica/ConnectionConfigImpl.java | 17 +++
> .../remote/AvaticaCommonsHttpClientImpl.java | 108 ++++++++++++-----
> .../remote/AvaticaHttpClientFactoryImpl.java | 15 ++-
> .../avatica/remote/KeyStoreConfigurable.java | 39 ++++++
> ...aCommonsHttpClientImplSocketFactoryTest.java | 120 +++++++++++++++++++
> core/src/test/resources/log4j.properties | 24 ++++
> 8 files changed, 310 insertions(+), 28 deletions(-)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
> ----------------------------------------------------------------------
> diff --git a/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java b/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
> index 1da7025..a1babb3 100644
> --- a/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
> +++ b/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
> @@ -76,6 +76,15 @@ public enum BuiltInConnectionProperty implements ConnectionProperty {
> /** Password for the truststore */
> TRUSTSTORE_PASSWORD("truststore_password", Type.STRING, null, false),
>
> + /** Keystore for MTLS authentication */
> + KEYSTORE("keystore", Type.STRING, null, false),
> +
> + /** Password for the keystore */
> + KEYSTORE_PASSWORD("keystore_password", Type.STRING, null, false),
> +
> + /** Password for the key inside keystore */
> + KEY_PASSWORD("key_password", Type.STRING, null, false),
> +
> HOSTNAME_VERIFICATION("hostname_verification", Type.ENUM, HostnameVerification.STRICT,
> HostnameVerification.class, false);
>
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
> ----------------------------------------------------------------------
> diff --git a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
> index cd18ec5..bbbfa87 100644
> --- a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
> +++ b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
> @@ -54,6 +54,12 @@ public interface ConnectionConfig {
> File truststore();
> /** @see BuiltInConnectionProperty#TRUSTSTORE_PASSWORD */
> String truststorePassword();
> + /** @see BuiltInConnectionProperty#KEYSTORE */
> + File keystore();
> + /** @see BuiltInConnectionProperty#KEYSTORE_PASSWORD */
> + String keystorePassword();
> + /** @see BuiltInConnectionProperty#KEY_PASSWORD */
> + String keyPassword();
> /** @see BuiltInConnectionProperty#HOSTNAME_VERIFICATION */
> HostnameVerification hostnameVerification();
> }
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
> ----------------------------------------------------------------------
> diff --git a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
> index 94cdc51..36cdf61 100644
> --- a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
> +++ b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
> @@ -106,6 +106,23 @@ public class ConnectionConfigImpl implements ConnectionConfig {
> return BuiltInConnectionProperty.TRUSTSTORE_PASSWORD.wrap(properties).getString();
> }
>
> + public File keystore() {
> + String filename = BuiltInConnectionProperty.KEYSTORE.wrap(properties).getString();
> + if (null == filename) {
> + return null;
> + }
> + return new File(filename);
> + }
> +
> + public String keystorePassword() {
> + return BuiltInConnectionProperty.KEYSTORE_PASSWORD.wrap(properties).getString();
> + }
> +
> + public String keyPassword() {
> + return BuiltInConnectionProperty.KEY_PASSWORD.wrap(properties).getString();
> +
> + }
> +
> public HostnameVerification hostnameVerification() {
> return BuiltInConnectionProperty.HOSTNAME_VERIFICATION.wrap(properties)
> .getEnum(HostnameVerification.class);
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
> ----------------------------------------------------------------------
> diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
> index 5576847..e7e6aa0 100644
> --- a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
> +++ b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
> @@ -28,6 +28,7 @@ import org.apache.http.client.methods.CloseableHttpResponse;
> import org.apache.http.client.methods.HttpPost;
> import org.apache.http.client.protocol.HttpClientContext;
> import org.apache.http.config.Lookup;
> +import org.apache.http.config.Registry;
> import org.apache.http.config.RegistryBuilder;
> import org.apache.http.conn.socket.ConnectionSocketFactory;
> import org.apache.http.conn.socket.PlainConnectionSocketFactory;
> @@ -42,6 +43,7 @@ import org.apache.http.impl.client.BasicCredentialsProvider;
> import org.apache.http.impl.client.CloseableHttpClient;
> import org.apache.http.impl.client.HttpClients;
> import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
> +import org.apache.http.ssl.SSLContextBuilder;
> import org.apache.http.ssl.SSLContexts;
> import org.apache.http.util.EntityUtils;
>
> @@ -64,7 +66,8 @@ import javax.net.ssl.SSLContext;
> * sent and received across the wire.
> */
> public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
> - UsernamePasswordAuthenticateable, TrustStoreConfigurable, HostnameVerificationConfigurable {
> + UsernamePasswordAuthenticateable, TrustStoreConfigurable,
> + KeyStoreConfigurable, HostnameVerificationConfigurable {
> private static final Logger LOG = LoggerFactory.getLogger(AvaticaCommonsHttpClientImpl.class);
>
> // Some basic exposed configurations
> @@ -78,14 +81,19 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
> protected final URI uri;
> protected BasicAuthCache authCache;
> protected CloseableHttpClient client;
> - PoolingHttpClientConnectionManager pool;
> + protected Registry<ConnectionSocketFactory> socketFactoryRegistry;
> + protected PoolingHttpClientConnectionManager pool;
>
> protected UsernamePasswordCredentials credentials = null;
> protected CredentialsProvider credentialsProvider = null;
> protected Lookup<AuthSchemeProvider> authRegistry = null;
>
> + protected boolean configureHttpsSocket = false;
> protected File truststore = null;
> + protected File keystore = null;
> protected String truststorePassword = null;
> + protected String keystorePassword = null;
> + protected String keyPassword = null;
> protected HostnameVerification hostnameVerification = null;
>
> public AvaticaCommonsHttpClientImpl(URL url) {
> @@ -95,29 +103,15 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
> }
>
> private void initializeClient() {
> - SSLConnectionSocketFactory sslFactory = null;
> - if (null != truststore && null != truststorePassword) {
> - try {
> - SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(
> - truststore, truststorePassword.toCharArray()).build();
> -
> - final HostnameVerifier verifier = getHostnameVerifier(hostnameVerification);
> -
> - sslFactory = new SSLConnectionSocketFactory(sslcontext, verifier);
> - } catch (Exception e) {
> - throw new RuntimeException(e);
> - }
> - } else {
> - LOG.debug("Not configuring HTTPS because of missing truststore/password");
> - }
> + socketFactoryRegistry = this.configureSocketFactories();
> + configureConnectionPool(socketFactoryRegistry);
> + this.authCache = new BasicAuthCache();
> + // A single thread-safe HttpClient, pooling connections via the ConnectionManager
> + this.client = HttpClients.custom().setConnectionManager(pool).build();
> + }
>
> - RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
> - registryBuilder.register("http", PlainConnectionSocketFactory.getSocketFactory());
> - // Only register the SSL factory when provided
> - if (null != sslFactory) {
> - registryBuilder.register("https", sslFactory);
> - }
> - pool = new PoolingHttpClientConnectionManager(registryBuilder.build());
> + protected void configureConnectionPool(Registry<ConnectionSocketFactory> registry) {
> + pool = new PoolingHttpClientConnectionManager(registry);
> // Increase max total connection to 100
> final String maxCnxns =
> System.getProperty(MAX_POOLED_CONNECTIONS_KEY,
> @@ -127,11 +121,57 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
> final String maxCnxnsPerRoute = System.getProperty(MAX_POOLED_CONNECTION_PER_ROUTE_KEY,
> MAX_POOLED_CONNECTION_PER_ROUTE_DEFAULT);
> pool.setDefaultMaxPerRoute(Integer.parseInt(maxCnxnsPerRoute));
> + }
>
> - this.authCache = new BasicAuthCache();
> + protected Registry<ConnectionSocketFactory> configureSocketFactories() {
> + RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
> + if (host.getSchemeName().equalsIgnoreCase("https")) {
> + configureHttpsRegistry(registryBuilder);
> + } else {
> + configureHttpRegistry(registryBuilder);
> + }
> + return registryBuilder.build();
> + }
>
> - // A single thread-safe HttpClient, pooling connections via the ConnectionManager
> - this.client = HttpClients.custom().setConnectionManager(pool).build();
> + protected void configureHttpsRegistry(RegistryBuilder<ConnectionSocketFactory> registryBuilder) {
> + if (!configureHttpsSocket) {
> + LOG.debug("HTTPS Socket not being configured because no truststore/keystore provided");
> + return;
> + }
> +
> + try {
> + SSLContext sslContext = getSSLContext();
> + final HostnameVerifier verifier = getHostnameVerifier(hostnameVerification);
> + SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext, verifier);
> + registryBuilder.register("https", sslFactory);
> + } catch (Exception e) {
> + LOG.error("HTTPS registry configuration failed");
> + throw new RuntimeException(e);
> + }
> + }
> +
> + private SSLContext getSSLContext() throws Exception {
> + SSLContextBuilder sslContextBuilder = SSLContexts.custom();
> + if (null != truststore && null != truststorePassword) {
> + loadTrustStore(sslContextBuilder);
> + }
> + if (null != keystore && null != keystorePassword && null != keyPassword) {
> + loadKeyStore(sslContextBuilder);
> + }
> + return sslContextBuilder.build();
> + }
> +
> + protected void loadKeyStore(SSLContextBuilder sslContextBuilder) throws Exception {
> + sslContextBuilder.loadKeyMaterial(keystore,
> + keystorePassword.toCharArray(), keyPassword.toCharArray());
> + }
> +
> + protected void loadTrustStore(SSLContextBuilder sslContextBuilder) throws Exception {
> + sslContextBuilder.loadTrustMaterial(truststore, truststorePassword.toCharArray());
> + }
> +
> + protected void configureHttpRegistry(RegistryBuilder<ConnectionSocketFactory> registryBuilder) {
> + registryBuilder.register("http", PlainConnectionSocketFactory.getSocketFactory());
> }
>
> /**
> @@ -244,11 +284,25 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
> "Truststore is must be an existing, regular file: " + truststore);
> }
> this.truststorePassword = Objects.requireNonNull(password);
> + configureHttpsSocket = true;
> + initializeClient();
> + }
> +
> + @Override public void setKeyStore(File keystore, String keystorepassword, String keypassword) {
> + this.keystore = Objects.requireNonNull(keystore);
> + if (!keystore.exists() || !keystore.isFile()) {
> + throw new IllegalArgumentException(
> + "Keystore is must be an existing, regular file: " + keystore);
> + }
> + this.keystorePassword = Objects.requireNonNull(keystorepassword);
> + this.keyPassword = Objects.requireNonNull(keypassword);
> + configureHttpsSocket = true;
> initializeClient();
> }
>
> @Override public void setHostnameVerification(HostnameVerification verification) {
> this.hostnameVerification = Objects.requireNonNull(verification);
> + configureHttpsSocket = true;
> initializeClient();
> }
> }
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
> ----------------------------------------------------------------------
> diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
> index f352ac1..8f305dc 100644
> --- a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
> +++ b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
> @@ -74,12 +74,25 @@ public class AvaticaHttpClientFactoryImpl implements AvaticaHttpClientFactory {
> File truststore = config.truststore();
> String truststorePassword = config.truststorePassword();
> if (null != truststore && null != truststorePassword) {
> - ((TrustStoreConfigurable) client).setTrustStore(truststore, truststorePassword);
> + ((TrustStoreConfigurable) client)
> + .setTrustStore(truststore, truststorePassword);
> }
> } else {
> LOG.debug("{} is not capable of SSL/TLS communication", client.getClass().getName());
> }
>
> + if (client instanceof KeyStoreConfigurable) {
> + File keystore = config.keystore();
> + String keystorePassword = config.keystorePassword();
> + String keyPassword = config.keyPassword();
> + if (null != keystore && null != keystorePassword && null != keyPassword) {
> + ((KeyStoreConfigurable) client)
> + .setKeyStore(keystore, keystorePassword, keyPassword);
> + }
> + } else {
> + LOG.debug("{} is not capable of Mutual authentication", client.getClass().getName());
> + }
> +
> // Set the SSL hostname verification if the client supports it
> if (client instanceof HostnameVerificationConfigurable) {
> ((HostnameVerificationConfigurable) client)
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/main/java/org/apache/calcite/avatica/remote/KeyStoreConfigurable.java
> ----------------------------------------------------------------------
> diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/KeyStoreConfigurable.java b/core/src/main/java/org/apache/calcite/avatica/remote/KeyStoreConfigurable.java
> new file mode 100644
> index 0000000..eaffd2a
> --- /dev/null
> +++ b/core/src/main/java/org/apache/calcite/avatica/remote/KeyStoreConfigurable.java
> @@ -0,0 +1,39 @@
> +/*
> + * 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.calcite.avatica.remote;
> +
> +import java.io.File;
> +
> +/**
> + * Allows a keystore (and keystorepassword, keypassword) to be
> + * provided to enable MTLS authentication
> + */
> +public interface KeyStoreConfigurable {
> +
> + /**
> + * Sets a keystore containing the collection of client side certificates
> + * to use for HTTPS mutual authentication along with
> + * password for keystore and password for key
> + *
> + * @param keystore The keystore on the local filesystem
> + * @param keystorepassword The keystore's password
> + * @param keypassword The key's password
> + */
> + void setKeyStore(File keystore, String keystorepassword, String keypassword);
> +}
> +
> +// End KeyStoreConfigurable.java
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplSocketFactoryTest.java
> ----------------------------------------------------------------------
> diff --git a/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplSocketFactoryTest.java b/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplSocketFactoryTest.java
> new file mode 100644
> index 0000000..a75222a
> --- /dev/null
> +++ b/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplSocketFactoryTest.java
> @@ -0,0 +1,120 @@
> +/*
> + * 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.calcite.avatica.remote;
> +
> +import org.apache.http.conn.socket.ConnectionSocketFactory;
> +import org.apache.http.conn.socket.PlainConnectionSocketFactory;
> +import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
> +import org.apache.http.ssl.SSLContextBuilder;
> +
> +import org.junit.Test;
> +
> +import java.io.File;
> +import java.net.URL;
> +
> +import static org.junit.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +import static org.mockito.ArgumentMatchers.any;
> +import static org.mockito.Mockito.doNothing;
> +import static org.mockito.Mockito.mock;
> +import static org.mockito.Mockito.spy;
> +import static org.mockito.Mockito.times;
> +import static org.mockito.Mockito.verify;
> +import static org.mockito.Mockito.when;
> +
> +/**
> + * Tests to verify loading of truststore/keystore in AvaticaCommonsHttpClientImpl
> + */
> +public class AvaticaCommonsHttpClientImplSocketFactoryTest {
> +
> + private static final String HTTP_REGISTRY = "http";
> + private static final String HTTPS_REGISTRY = "https";
> +
> + private URL url;
> + private AvaticaCommonsHttpClientImpl client;
> + private File storeFile;
> + private String password;
> +
> + @Test public void testPlainSocketFactory() throws Exception {
> + configureHttpClient();
> + assertFalse("Https socket should not be configured"
> + + " without truststore/keystore", client.configureHttpsSocket);
> + verifyFactoryInstance(client, HTTP_REGISTRY, PlainConnectionSocketFactory.class);
> + verifyFactoryInstance(client, HTTPS_REGISTRY, null);
> + verify(client, times(0)).loadTrustStore(any(SSLContextBuilder.class));
> + verify(client, times(0)).loadKeyStore(any(SSLContextBuilder.class));
> + }
> +
> + @Test public void testTrustStoreLoadedInFactory() throws Exception {
> + configureHttpsClient();
> + client.setTrustStore(storeFile, password);
> + assertTrue("Https socket should be configured"
> + + " with truststore", client.configureHttpsSocket);
> + verifyFactoryInstance(client, HTTP_REGISTRY, null);
> + verifyFactoryInstance(client, HTTPS_REGISTRY, SSLConnectionSocketFactory.class);
> + verify(client, times(1)).configureSocketFactories();
> + verify(client, times(1)).loadTrustStore(any(SSLContextBuilder.class));
> + verify(client, times(0)).loadKeyStore(any(SSLContextBuilder.class));
> + }
> +
> + @Test public void testKeyStoreLoadedInFactory() throws Exception {
> + configureHttpsClient();
> + client.setKeyStore(storeFile, password, password);
> + assertTrue("Https socket should be configured"
> + + " with keystore", client.configureHttpsSocket);
> + verifyFactoryInstance(client, HTTP_REGISTRY, null);
> + verifyFactoryInstance(client, HTTPS_REGISTRY, SSLConnectionSocketFactory.class);
> + verify(client, times(1)).configureSocketFactories();
> + verify(client, times(0)).loadTrustStore(any(SSLContextBuilder.class));
> + verify(client, times(1)).loadKeyStore(any(SSLContextBuilder.class));
> + }
> +
> + private void configureHttpClient() throws Exception {
> + url = new URL("http://fake_url.com");
> + configureClient();
> + }
> +
> + private void configureHttpsClient() throws Exception {
> + url = new URL("https://fake_url.com");
> + configureClient();
> + }
> +
> + private void configureClient() throws Exception {
> + client = spy(new AvaticaCommonsHttpClientImpl(url));
> + // storeFile can be used as either Keystore/Truststore
> + storeFile = mock(File.class);
> + when(storeFile.exists()).thenReturn(true);
> + when(storeFile.isFile()).thenReturn(true);
> + password = "";
> +
> + doNothing().when(client).loadTrustStore(any(SSLContextBuilder.class));
> + doNothing().when(client).loadKeyStore(any(SSLContextBuilder.class));
> + }
> +
> + <T> void verifyFactoryInstance(AvaticaCommonsHttpClientImpl client,
> + String registry, Class<T> expected) {
> + ConnectionSocketFactory factory = client.socketFactoryRegistry.lookup(registry);
> + if (expected == null) {
> + assertTrue("Factory for registry " + registry + " expected as null", factory == null);
> + } else {
> + assertTrue("Factory for registry " + registry + " expected of type " + expected.getName(),
> + expected.equals(factory.getClass()));
> + }
> + }
> +}
> +
> +// End AvaticaCommonsHttpClientImplSocketFactoryTest.java
>
> http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/97c8a161/core/src/test/resources/log4j.properties
> ----------------------------------------------------------------------
> diff --git a/core/src/test/resources/log4j.properties b/core/src/test/resources/log4j.properties
> new file mode 100644
> index 0000000..834e2db
> --- /dev/null
> +++ b/core/src/test/resources/log4j.properties
> @@ -0,0 +1,24 @@
> +# 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.
> +
> +# Root logger is configured at INFO and is sent to A1
> +log4j.rootLogger=INFO, A1
> +
> +# A1 goes to the console
> +log4j.appender.A1=org.apache.log4j.ConsoleAppender
> +
> +# Set the pattern for each log message
> +log4j.appender.A1.layout=org.apache.log4j.PatternLayout
> +log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p - %m%n
>