All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.voltdb.client.ClientConfig Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2020 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see .
 */

package org.voltdb.client;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.math.RoundingMode;
import java.security.Principal;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.voltcore.utils.ssl.SSLConfiguration;
import org.voltcore.utils.ssl.SSLConfiguration.SslConfig;
import org.voltdb.common.Constants;
import org.voltdb.types.VoltDecimalHelper;

/**
 * Container for configuration settings for a Client
 */
public class ClientConfig {

    private static final String DEFAULT_SSL_PROPS_FILE = "ssl-config";

    static final long DEFAULT_PROCEDURE_TIMOUT_NANOS = TimeUnit.MINUTES.toNanos(2);// default timeout is 2 minutes;
    static final long DEFAULT_CONNECTION_TIMOUT_MS = 2 * 60 * 1000; // default timeout is 2 minutes;
    static final long DEFAULT_INITIAL_CONNECTION_RETRY_INTERVAL_MS = 1000; // default initial connection retry interval is 1 second
    static final long DEFAULT_MAX_CONNECTION_RETRY_INTERVAL_MS = 8000; // default max connection retry interval is 8 seconds

    final ClientAuthScheme m_hashScheme;
    final String m_username;
    final String m_password;
    final boolean m_cleartext;
    final ClientStatusListenerExt m_listener;
    boolean m_heavyweight = false;
    int m_maxOutstandingTxns = 3000;
    int m_maxTransactionsPerSecond = Integer.MAX_VALUE;
    boolean m_autoTune = false;
    int m_autoTuneTargetInternalLatency = 5;
    long m_procedureCallTimeoutNanos = DEFAULT_PROCEDURE_TIMOUT_NANOS;
    long m_connectionResponseTimeoutMS = DEFAULT_CONNECTION_TIMOUT_MS;
    boolean m_useClientAffinity = true;
    Subject m_subject = null;
    boolean m_reconnectOnConnectionLoss;
    long m_initialConnectionRetryIntervalMS = DEFAULT_INITIAL_CONNECTION_RETRY_INTERVAL_MS;
    long m_maxConnectionRetryIntervalMS = DEFAULT_MAX_CONNECTION_RETRY_INTERVAL_MS;
    boolean m_sendReadsToReplicasBytDefaultIfCAEnabled = false;
    SslConfig m_sslConfig;
    boolean m_topologyChangeAware = false;
    boolean m_enableSSL = false;
    String m_sslPropsFile = null;

    //For unit testing. This should really be in Environment class we should assemble all such there.
    public static final boolean ENABLE_SSL_FOR_TEST = Boolean.valueOf(
            System.getenv("ENABLE_SSL") == null ?
                    Boolean.toString(Boolean.getBoolean("ENABLE_SSL"))
                  : System.getenv("ENABLE_SSL"));

    final static String getUserNameFromSubject(Subject subject) {
        if (subject == null || subject.getPrincipals() == null || subject.getPrincipals().isEmpty()) {
            throw new IllegalArgumentException("Subject is null or does not contain principals");
        }
        Iterator piter = subject.getPrincipals().iterator();
        Principal principal = piter.next();
        String username = principal.getName();
        while (piter.hasNext()) {
            principal = piter.next();
            if (principal instanceof DelegatePrincipal) {
                username = principal.getName();
                break;
            }
        }
        return username;
    }

    /**
     * 

Configuration for a client with no authentication credentials that will * work with a server with security disabled. Also specifies no status listener.

*/ public ClientConfig() { this("", "", true, (ClientStatusListenerExt) null, ClientAuthScheme.HASH_SHA256); } /** *

Configuration for a client that specifies authentication credentials. The username and * password can be null or the empty string.

* * @param username Cleartext username. * @param password Cleartext password. */ public ClientConfig(String username, String password) { this(username, password, true, (ClientStatusListenerExt) null, ClientAuthScheme.HASH_SHA256); } /** *

Configuration for a client that specifies authentication credentials. The username and * password can be null or the empty string. Also specifies a status listener.

* * @deprecated {@link ClientStatusListener} deprecated in favor of {@link ClientStatusListenerExt} * in * @param username Cleartext username. * @param password Cleartext password. * @param scheme Client password hash scheme * @param listener {@link ClientStatusListener} implementation to receive callbacks. */ @Deprecated public ClientConfig(String username, String password, ClientStatusListener listener, ClientAuthScheme scheme) { this(username, password, true, new ClientStatusListenerWrapper(listener), scheme); } /** *

Configuration for a client that specifies authentication credentials. The username and * password can be null or the empty string. Also specifies a status listener.

* * @param username Cleartext username. * @param password Cleartext password. * @param listener {@link ClientStatusListenerExt} implementation to receive callbacks. */ public ClientConfig(String username, String password, ClientStatusListenerExt listener) { this(username,password,true,listener, ClientAuthScheme.HASH_SHA256); } /** *

Configuration for a client that specifies authentication credentials. The username and * password can be null or the empty string. Also specifies a status listener.

* * @param username Cleartext username. * @param password Cleartext password. * @param listener {@link ClientStatusListenerExt} implementation to receive callbacks. * @param scheme Client password hash scheme */ public ClientConfig(String username, String password, ClientStatusListenerExt listener, ClientAuthScheme scheme) { this(username,password,true,listener, scheme); } /** *

Configuration for a client that specifies authentication credentials. The username and * password can be null or the empty string. Also specifies a status listener.

* * @param username Cleartext username. * @param password A cleartext or hashed passowrd. * @param listener {@link ClientStatusListenerExt} implementation to receive callbacks. * @param cleartext Whether the password is hashed. */ public ClientConfig(String username, String password, boolean cleartext, ClientStatusListenerExt listener) { this(username, password, cleartext, listener, ClientAuthScheme.HASH_SHA256); } /** *

Configuration for a client that specifies an already authenticated {@link Subject}. * Also specifies a status listener.

* * @param subject an authenticated {@link Subject} * @param listener {@link ClientStatusListenerExt} implementation to receive callbacks. */ public ClientConfig(Subject subject, ClientStatusListenerExt listener) { this(getUserNameFromSubject(subject), "", true, listener, ClientAuthScheme.HASH_SHA256); m_subject = subject; } /** *

Configuration for a client that specifies authentication credentials. The username and * password can be null or the empty string. Also specifies a status listener.

* * @param username Cleartext username. * @param password A cleartext or hashed passowrd. * @param listener {@link ClientStatusListenerExt} implementation to receive callbacks. * @param cleartext Whether the password is hashed. * @param scheme Client password hash scheme */ public ClientConfig(String username, String password, boolean cleartext, ClientStatusListenerExt listener, ClientAuthScheme scheme) { if (ClientConfig.ENABLE_SSL_FOR_TEST) { try (InputStream is = ClientConfig.class.getResourceAsStream(DEFAULT_SSL_PROPS_FILE)) { Properties sslProperties = new Properties(); sslProperties.load(is); String trustStorePath = sslProperties.getProperty(SSLConfiguration.TRUSTSTORE_CONFIG_PROP); String trustStorePassword = sslProperties.getProperty(SSLConfiguration.TRUSTSTORE_PASSWORD_CONFIG_PROP); setTrustStore(trustStorePath, trustStorePassword); enableSSL(); } catch (IOException e) { throw new IllegalArgumentException("Unable to access SSL configuration.", e); } } if (username == null) { m_username = ""; } else { m_username = username; } if (password == null) { m_password = ""; } else { m_password = password; } m_listener = listener; m_cleartext = cleartext; m_hashScheme = scheme; } /** *

Set the timeout for procedure call. If the timeout expires before the call returns, * the procedure callback will be called with status {@link ClientResponse#CONNECTION_TIMEOUT}. * Synchronous procedures will throw an exception. If a response comes back after the * expiration has triggered, then a callback method * {@link ClientStatusListenerExt#lateProcedureResponse(ClientResponse, String, int)} * will be called.

* *

Default value is 2 minutes if not set. Value of 0 means forever.

* *

Note that while specified in MS, this timeout is only accurate to within a second or so.

* * @param ms Timeout value in milliseconds. */ public void setProcedureCallTimeout(long ms) { assert(ms >= 0); if (ms < 0) ms = 0; // 0 implies infinite, but use LONG_MAX to reduce branches to test if (ms == 0) ms = Long.MAX_VALUE; m_procedureCallTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(ms); } /** *

Set the timeout for reading from a connection. If a connection receives no responses, * either from procedure calls or &Pings, for the timeout time in milliseconds, * then the connection will be assumed dead and the closed connection callback will * be called.

* *

Default value is 2 minutes if not set. Value of 0 means forever.

* *

Note that while specified in MS, this timeout is only accurate to within a second or so.

* * @param ms Timeout value in milliseconds. */ public void setConnectionResponseTimeout(long ms) { assert(ms >= 0); if (ms < 0) ms = 0; // 0 implies infinite, but use LONG_MAX to reduce branches to test if (ms == 0) ms = Long.MAX_VALUE; m_connectionResponseTimeoutMS = ms; } /** *

Set the maximum size of memory pool arenas before falling back to using heap byte buffers.

* * @deprecated Deprecated because memory pooling no longer uses arenas. Has no effect. * @param maxArenaSizes Maximum size of each arena. */ @Deprecated public void setMaxArenaSizes(int maxArenaSizes[]) { } /** *

By default a single network thread is created and used to do IO and invoke callbacks. * When set to true, Runtime.getRuntime().availableProcessors() / 2 threads are created. * Multiple server connections are required for more threads to be involved, a connection * is assigned exclusively to a connection.

* * @param heavyweight Whether to create additional threads for high IO or * high processing workloads. */ public void setHeavyweight(boolean heavyweight) { m_heavyweight = heavyweight; } /** *

Provide a hint indicating how large messages will be once serialized. Ensures * efficient message buffer allocation.

* * @deprecated Has no effect. * @param size The expected size of the outgoing message. */ @Deprecated public void setExpectedOutgoingMessageSize(int size) { } /** *

Set the maximum number of outstanding requests that will be submitted before * blocking. Similar to the number of concurrent connections in a traditional synchronous API. * Defaults to 2k.

* * @param maxOutstanding The maximum outstanding transactions before calls to * {@link Client#callProcedure(ProcedureCallback, String, Object...)} will block * or return false (depending on settings). */ public void setMaxOutstandingTxns(int maxOutstanding) { if (maxOutstanding < 1) { throw new IllegalArgumentException( "Max outstanding must be greater than 0, " + maxOutstanding + " was specified"); } m_maxOutstandingTxns = maxOutstanding; } /** *

Set the maximum number of transactions that can be run in 1 second. Note this * specifies a rate, not a ceiling. If the limit is set to 10, you can't send 10 in * the first half of the second and 5 in the later half; the client will let you send * about 1 transaction every 100ms. Default is {link Integer#MAX_VALUE}.

* * @param maxTxnsPerSecond Requested ceiling on rate of call in transaction per second. */ public void setMaxTransactionsPerSecond(int maxTxnsPerSecond) { if (maxTxnsPerSecond < 1) { throw new IllegalArgumentException( "Max TPS must be greater than 0, " + maxTxnsPerSecond + " was specified"); } m_maxTransactionsPerSecond = maxTxnsPerSecond; } /** *

Enable the Auto Tuning feature, which dynamically adjusts the maximum * allowable transaction number with the goal of maintaining a target latency. * The latency value used is the internal latency as reported by the servers. * The internal latency is a good measure of system saturation.

* *

See {@link #setAutoTuneTargetInternalLatency(int)}.

*/ public void enableAutoTune() { m_autoTune = true; } /** *

Attempts to route transactions to the correct master partition improving latency * and throughput

* *

If you are using persistent connections you definitely want this.

* *

Defaults to TRUE.

* * @param on Enable or disable the affinity feature. */ public void setClientAffinity(boolean on) { m_useClientAffinity = on; } /** *

Attempts to connect to all nodes in the cluster

*

Defaults to false.

* @param enabled Enable or disable the topology awareness feature. */ public void setTopologyChangeAware(boolean enabled) { m_topologyChangeAware = enabled; } /** *

By default, reads are sent to the leader replica for each partition. This * is usually optimal for the default read consistency value, SAFE. If you are * using FAST reads, enabling this setting will load balance reads amongst * partition replicas, often increasing throughput and decreasing latency.

* *

Note: consistency modality SAFE/FAST is obsolete and no longer supported. *

* *

Defaults to FALSE. Has no effect if Client Affinity is disabled.

* * @param on Enable or disable sending reads to replicas. */ public void setSendReadsToReplicasByDefault(boolean on) { m_sendReadsToReplicasBytDefaultIfCAEnabled = on; } /** *

Attempts to reconnect to a node with retry after connection loss. See the {@link ReconnectStatusListener}.

* * @param on Enable or disable the reconnection feature. Default is off. */ public void setReconnectOnConnectionLoss(boolean on) { this.m_reconnectOnConnectionLoss = on; } /** *

Set the initial connection retry interval. Only takes effect if {@link #m_reconnectOnConnectionLoss} is turned on.

* * @param ms initial connection retry interval in milliseconds. */ public void setInitialConnectionRetryInterval(long ms) { this.m_initialConnectionRetryIntervalMS = ms; } /** *

Set the max connection retry interval. Only takes effect if {@link #m_reconnectOnConnectionLoss} is turned on.

* * @param ms max connection retry interval in milliseconds. */ public void setMaxConnectionRetryInterval(long ms) { this.m_maxConnectionRetryIntervalMS = ms; } /** *

Set the target latency for the Auto Tune feature. Note this represents internal * latency as reported by the server(s), not round-trip latency measured by the * client. Default value is 5 if this is not called.

* * @param targetLatency New target latency in milliseconds. */ public void setAutoTuneTargetInternalLatency(int targetLatency) { if (targetLatency < 1) { throw new IllegalArgumentException( "Max auto tune latency must be greater than 0, " + targetLatency + " was specified"); } m_autoTuneTargetInternalLatency = targetLatency; } /** *

Enable Kerberos authentication with the provided subject credentials

* @param subject Identity of the authenticated user. */ public void enableKerberosAuthentication(final Subject subject) { m_subject = subject; } /** *

Use the provided JAAS login context entry key to get the authentication * credentials held by the caller

* * @param loginContextEntryKey JAAS login context config entry designation */ public void enableKerberosAuthentication(final String loginContextEntryKey) { try { LoginContext lc = new LoginContext(loginContextEntryKey); lc.login(); m_subject = lc.getSubject(); } catch (SecurityException | LoginException ex) { throw new IllegalArgumentException("Cannot determine client consumer's credentials", ex); } } /** * Enable or disable the rounding mode in the client. This must match the * rounding mode set in the server, which is set using system properties. * * @param isEnabled True iff rounding is enabled. * @param mode The rounding mode, with values taken from java.math.RoundingMode. */ public static void setRoundingConfig(boolean isEnabled, RoundingMode mode) { VoltDecimalHelper.setRoundingConfig(isEnabled, mode); } /** * Configure trust store * * @param pathToTrustStore file specification for the trust store * @param trustStorePassword trust store key file password */ public void setTrustStore(String pathToTrustStore, String trustStorePassword) { File tsFD = new File(pathToTrustStore != null && !pathToTrustStore.trim().isEmpty() ? pathToTrustStore : ""); if (!tsFD.exists() || !tsFD.isFile() || !tsFD.canRead()) { throw new IllegalArgumentException("Trust store " + pathToTrustStore + " is not a read accessible file"); } m_sslConfig = new SSLConfiguration.SslConfig(null, null, pathToTrustStore, trustStorePassword); } /** * Configure trust store * * @param propFN property file name containing trust store properties: *

    *
  • {@code trustStore} trust store file specification *
  • {@code trustStorePassword} trust store password *
*/ public void setTrustStoreConfigFromPropertyFile(String propFN) { File propFD = new File(propFN != null && !propFN.trim().isEmpty() ? propFN : ""); if (!propFD.exists() || !propFD.isFile() || !propFD.canRead()) { throw new IllegalArgumentException("Properties file " + propFN + " is not a read accessible file"); } Properties props = new Properties(); try (FileReader fr = new FileReader(propFD)) { props.load(fr); } catch (IOException e) { throw new IllegalArgumentException("Failed to read properties file " + propFN, e); } String trustStore = props.getProperty(SSLConfiguration.TRUSTSTORE_CONFIG_PROP); String trustStorePassword = props.getProperty(SSLConfiguration.TRUSTSTORE_PASSWORD_CONFIG_PROP); m_sslConfig = new SSLConfiguration.SslConfig(null, null, trustStore, trustStorePassword); } /** * Configure ssl from the provided properties file. if file is not provided we configure without keystore and truststore manager. */ public void enableSSL() { m_enableSSL = true; if (m_sslConfig == null) { m_sslConfig = new SSLConfiguration.SslConfig(); } } public void setTrustStoreConfigFromDefault() { String trustStore = Constants.DEFAULT_TRUSTSTORE_RESOURCE; String trustStorePassword = Constants.DEFAULT_TRUSTSTORE_PASSWD; m_sslConfig = new SSLConfiguration.SslConfig(null, null, trustStore, trustStorePassword); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy