Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.trino.client.uri.ConnectionProperties Maven / Gradle / Ivy
/*
* Licensed 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 io.trino.client.uri;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.HostAndPort;
import io.airlift.units.Duration;
import io.trino.client.ClientSelectedRole;
import io.trino.client.DnsResolver;
import io.trino.client.auth.external.ExternalRedirectStrategy;
import io.trino.client.spooling.encoding.QueryDataDecoders;
import org.ietf.jgss.GSSCredential;
import java.io.File;
import java.time.ZoneId;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.Maps.immutableEntry;
import static com.google.common.collect.Streams.stream;
import static io.trino.client.ClientSelectedRole.Type.ALL;
import static io.trino.client.ClientSelectedRole.Type.NONE;
import static io.trino.client.uri.AbstractConnectionProperty.Validator;
import static io.trino.client.uri.AbstractConnectionProperty.validator;
import static io.trino.client.uri.ConnectionProperties.SslVerificationMode.FULL;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
final class ConnectionProperties
{
enum SslVerificationMode
{
FULL, CA, NONE
}
public static final ConnectionProperty USER = new User();
public static final ConnectionProperty PASSWORD = new Password();
public static final ConnectionProperty SESSION_USER = new SessionUser();
public static final ConnectionProperty> ROLES = new Roles();
public static final ConnectionProperty SOCKS_PROXY = new SocksProxy();
public static final ConnectionProperty HTTP_PROXY = new HttpProxy();
public static final ConnectionProperty APPLICATION_NAME_PREFIX = new ApplicationNamePrefix();
public static final ConnectionProperty DISABLE_COMPRESSION = new DisableCompression();
public static final ConnectionProperty ENCODING = new Encoding();
public static final ConnectionProperty ASSUME_LITERAL_NAMES_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS = new AssumeLiteralNamesInMetadataCallsForNonConformingClients();
public static final ConnectionProperty ASSUME_LITERAL_UNDERSCORE_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS = new AssumeLiteralUnderscoreInMetadataCallsForNonConformingClients();
public static final ConnectionProperty SSL = new Ssl();
public static final ConnectionProperty SSL_VERIFICATION = new SslVerification();
public static final ConnectionProperty SSL_KEY_STORE_PATH = new SslKeyStorePath();
public static final ConnectionProperty SSL_KEY_STORE_PASSWORD = new SslKeyStorePassword();
public static final ConnectionProperty SSL_KEY_STORE_TYPE = new SslKeyStoreType();
public static final ConnectionProperty SSL_USE_SYSTEM_KEY_STORE = new SslUseSystemKeyStore();
public static final ConnectionProperty SSL_TRUST_STORE_PATH = new SslTrustStorePath();
public static final ConnectionProperty SSL_TRUST_STORE_PASSWORD = new SslTrustStorePassword();
public static final ConnectionProperty SSL_TRUST_STORE_TYPE = new SslTrustStoreType();
public static final ConnectionProperty SSL_USE_SYSTEM_TRUST_STORE = new SslUseSystemTrustStore();
public static final ConnectionProperty KERBEROS_SERVICE_PRINCIPAL_PATTERN = new KerberosServicePrincipalPattern();
public static final ConnectionProperty KERBEROS_REMOTE_SERVICE_NAME = new KerberosRemoteServiceName();
public static final ConnectionProperty KERBEROS_USE_CANONICAL_HOSTNAME = new KerberosUseCanonicalHostname();
public static final ConnectionProperty KERBEROS_PRINCIPAL = new KerberosPrincipal();
public static final ConnectionProperty KERBEROS_CONFIG_PATH = new KerberosConfigPath();
public static final ConnectionProperty KERBEROS_KEYTAB_PATH = new KerberosKeytabPath();
public static final ConnectionProperty KERBEROS_CREDENTIAL_CACHE_PATH = new KerberosCredentialCachePath();
public static final ConnectionProperty KERBEROS_DELEGATION = new KerberosDelegation();
public static final ConnectionProperty KERBEROS_CONSTRAINED_DELEGATION = new KerberosConstrainedDelegation();
public static final ConnectionProperty ACCESS_TOKEN = new AccessToken();
public static final ConnectionProperty EXTERNAL_AUTHENTICATION = new ExternalAuthentication();
public static final ConnectionProperty EXTERNAL_AUTHENTICATION_TIMEOUT = new ExternalAuthenticationTimeout();
public static final ConnectionProperty> EXTERNAL_AUTHENTICATION_REDIRECT_HANDLERS = new ExternalAuthenticationRedirectHandlers();
public static final ConnectionProperty EXTERNAL_AUTHENTICATION_TOKEN_CACHE = new ExternalAuthenticationTokenCache();
public static final ConnectionProperty> EXTRA_CREDENTIALS = new ExtraCredentials();
public static final ConnectionProperty CLIENT_INFO = new ClientInfo();
public static final ConnectionProperty> CLIENT_TAGS = new ClientTags();
public static final ConnectionProperty TRACE_TOKEN = new TraceToken();
public static final ConnectionProperty> SESSION_PROPERTIES = new SessionProperties();
public static final ConnectionProperty SOURCE = new Source();
public static final ConnectionProperty CATALOG = new Catalog();
public static final ConnectionProperty SCHEMA = new Schema();
public static final ConnectionProperty> DNS_RESOLVER = new Resolver();
public static final ConnectionProperty DNS_RESOLVER_CONTEXT = new ResolverContext();
public static final ConnectionProperty HOSTNAME_IN_CERTIFICATE = new HostnameInCertificate();
public static final ConnectionProperty TIMEZONE = new TimeZone();
public static final ConnectionProperty EXPLICIT_PREPARE = new ExplicitPrepare();
public static final ConnectionProperty ASSUME_NULL_CATALOG_MEANS_CURRENT_CATALOG = new AssumeNullCatalogMeansCurrentCatalog();
public static final ConnectionProperty LOCALE = new UserLocale();
public static final ConnectionProperty TIMEOUT = new Timeout();
public static final ConnectionProperty HTTP_LOGGING_LEVEL = new HttpLoggingLevel();
public static final ConnectionProperty> RESOURCE_ESTIMATES = new ResourceEstimates();
public static final ConnectionProperty> SQL_PATH = new SqlPath();
private static final Set> ALL_PROPERTIES = ImmutableSet.>builder()
// Keep sorted
.add(ACCESS_TOKEN)
.add(APPLICATION_NAME_PREFIX)
.add(ASSUME_LITERAL_NAMES_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS)
.add(ASSUME_LITERAL_UNDERSCORE_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS)
.add(ASSUME_NULL_CATALOG_MEANS_CURRENT_CATALOG)
.add(CATALOG)
.add(CLIENT_INFO)
.add(CLIENT_TAGS)
.add(DISABLE_COMPRESSION)
.add(DNS_RESOLVER)
.add(DNS_RESOLVER_CONTEXT)
.add(ENCODING)
.add(EXPLICIT_PREPARE)
.add(EXTERNAL_AUTHENTICATION)
.add(EXTERNAL_AUTHENTICATION_REDIRECT_HANDLERS)
.add(EXTERNAL_AUTHENTICATION_TIMEOUT)
.add(EXTERNAL_AUTHENTICATION_TOKEN_CACHE)
.add(EXTRA_CREDENTIALS)
.add(HOSTNAME_IN_CERTIFICATE)
.add(HTTP_LOGGING_LEVEL)
.add(HTTP_PROXY)
.add(KERBEROS_CONFIG_PATH)
.add(KERBEROS_CONSTRAINED_DELEGATION)
.add(KERBEROS_CREDENTIAL_CACHE_PATH)
.add(KERBEROS_DELEGATION)
.add(KERBEROS_KEYTAB_PATH)
.add(KERBEROS_PRINCIPAL)
.add(KERBEROS_REMOTE_SERVICE_NAME)
.add(KERBEROS_SERVICE_PRINCIPAL_PATTERN)
.add(KERBEROS_USE_CANONICAL_HOSTNAME)
.add(LOCALE)
.add(PASSWORD)
.add(RESOURCE_ESTIMATES)
.add(ROLES)
.add(SCHEMA)
.add(SESSION_PROPERTIES)
.add(SESSION_USER)
.add(SOCKS_PROXY)
.add(SOURCE)
.add(SQL_PATH)
.add(SSL)
.add(SSL_KEY_STORE_PASSWORD)
.add(SSL_KEY_STORE_PATH)
.add(SSL_KEY_STORE_TYPE)
.add(SSL_USE_SYSTEM_KEY_STORE)
.add(SSL_TRUST_STORE_PASSWORD)
.add(SSL_TRUST_STORE_PATH)
.add(SSL_TRUST_STORE_TYPE)
.add(SSL_USE_SYSTEM_TRUST_STORE)
.add(SSL_VERIFICATION)
.add(TIMEOUT)
.add(TIMEZONE)
.add(TRACE_TOKEN)
.add(USER)
.build();
private static final Map> KEY_LOOKUP = unmodifiableMap(ALL_PROPERTIES.stream()
.collect(toMap(ConnectionProperty::getKey, identity())));
private ConnectionProperties() {}
public static ConnectionProperty, ?> forKey(String propertiesKey)
{
return KEY_LOOKUP.get(propertiesKey);
}
public static Set> allProperties()
{
return ALL_PROPERTIES;
}
private static class User
extends AbstractConnectionProperty
{
public User()
{
super(PropertyName.USER, NOT_REQUIRED, ALLOWED, NON_EMPTY_STRING_CONVERTER);
}
}
private static class Password
extends AbstractConnectionProperty
{
public Password()
{
super(PropertyName.PASSWORD, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class SessionUser
extends AbstractConnectionProperty
{
protected SessionUser()
{
super(PropertyName.SESSION_USER, NOT_REQUIRED, ALLOWED, NON_EMPTY_STRING_CONVERTER);
}
}
private static class Roles
extends AbstractConnectionProperty>
{
public Roles()
{
super(PropertyName.ROLES, NOT_REQUIRED, ALLOWED, converter(Roles::parseRoles, Roles::rolesToString));
}
// Roles consists of a list of catalog role pairs.
// E.g., `jdbc:trino://example.net:8080/?roles=catalog1:none;catalog2:all;catalog3:role` will set following roles:
// - `none` in `catalog1`
// - `all` in `catalog2`
// - `role` in `catalog3`
public static Map parseRoles(String roles)
{
return new MapPropertyParser(PropertyName.ROLES.toString()).parse(roles).entrySet().stream()
.collect(toImmutableMap(Map.Entry::getKey, entry -> mapToClientSelectedRole(entry.getValue())));
}
public static String rolesToString(Map roles)
{
return roles.entrySet().stream()
.map(entry -> entry.getKey() + ":" + roleToString(entry.getValue()))
.collect(Collectors.joining(";"));
}
private static String roleToString(ClientSelectedRole value)
{
switch (value.getType()) {
case ALL:
return "all";
case NONE:
return "none";
case ROLE:
return value.getRole().orElse("role");
default:
throw new IllegalArgumentException("Unrecognized role type " + value.getType());
}
}
private static ClientSelectedRole mapToClientSelectedRole(String role)
{
checkArgument(!role.contains("\""), "Role must not contain double quotes: %s", role);
if (ALL.name().equalsIgnoreCase(role)) {
return new ClientSelectedRole(ALL, Optional.empty());
}
if (NONE.name().equalsIgnoreCase(role)) {
return new ClientSelectedRole(NONE, Optional.empty());
}
return new ClientSelectedRole(ClientSelectedRole.Type.ROLE, Optional.of(role));
}
}
private static class ResourceEstimates
extends AbstractConnectionProperty>
{
private static final CharMatcher PRINTABLE_ASCII = CharMatcher.inRange((char) 0x21, (char) 0x7E);
public ResourceEstimates()
{
super(PropertyName.RESOURCE_ESTIMATES, NOT_REQUIRED, ALLOWED, converter(ResourceEstimates::parseResourceEstimates, ResourceEstimates::toString));
}
public static Map parseResourceEstimates(String resourceEstimateString)
{
Map resourceEstimates = new MapPropertyParser(PropertyName.RESOURCE_ESTIMATES.toString()).parse(resourceEstimateString);
for (String resourceName : resourceEstimates.keySet()) {
checkArgument(PRINTABLE_ASCII.matchesAllOf(resourceName), "Resource contains spaces or is not ASCII: %s", resourceName);
checkArgument(resourceName.indexOf('=') < 0, "Resource must not contain '=': %s", resourceName);
checkArgument(PRINTABLE_ASCII.matchesAllOf(resourceEstimates.get(resourceName)), "Resource estimate contains spaces or is not ASCII: %s", resourceName);
}
return resourceEstimates;
}
public static String toString(Map values)
{
return values.entrySet().stream()
.map(entry -> entry.getKey() + ":" + entry.getValue())
.collect(Collectors.joining(";"));
}
}
private static class SocksProxy
extends AbstractConnectionProperty
{
private static final Validator NO_HTTP_PROXY = validator(
properties -> !HTTP_PROXY.getValue(properties).isPresent(),
format("Connection property %s cannot be used when %s is set", PropertyName.SOCKS_PROXY, PropertyName.HTTP_PROXY));
public SocksProxy()
{
super(PropertyName.SOCKS_PROXY, NOT_REQUIRED, NO_HTTP_PROXY, converter(HostAndPort::fromString, HostAndPort::toString));
}
}
private static class HttpProxy
extends AbstractConnectionProperty
{
private static final Validator NO_SOCKS_PROXY = validator(
properties -> !SOCKS_PROXY.getValue(properties).isPresent(),
format("Connection property %s cannot be used when %s is set", PropertyName.HTTP_PROXY, PropertyName.SOCKS_PROXY));
public HttpProxy()
{
super(PropertyName.HTTP_PROXY, NOT_REQUIRED, NO_SOCKS_PROXY, converter(HostAndPort::fromString, HostAndPort::toString));
}
}
private static class ApplicationNamePrefix
extends AbstractConnectionProperty
{
public ApplicationNamePrefix()
{
super(PropertyName.APPLICATION_NAME_PREFIX, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class ClientInfo
extends AbstractConnectionProperty
{
public ClientInfo()
{
super(PropertyName.CLIENT_INFO, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class ClientTags
extends AbstractConnectionProperty>
{
public ClientTags()
{
super(PropertyName.CLIENT_TAGS, NOT_REQUIRED, ALLOWED, converter(ClientTags::parseClientTags, ClientTags::toString));
}
private static Set parseClientTags(String clientTagsString)
{
Splitter splitter = Splitter.on(',').trimResults().omitEmptyStrings();
return ImmutableSet.copyOf(splitter.split(nullToEmpty(clientTagsString)));
}
private static String toString(Set clientTags)
{
return Joiner.on(",").join(clientTags);
}
}
private static class TraceToken
extends AbstractConnectionProperty
{
public TraceToken()
{
super(PropertyName.TRACE_TOKEN, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class DisableCompression
extends AbstractConnectionProperty
{
public DisableCompression()
{
super(PropertyName.DISABLE_COMPRESSION, NOT_REQUIRED, ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class Encoding
extends AbstractConnectionProperty
{
public Encoding()
{
super(PropertyName.ENCODING, NOT_REQUIRED, Encoding::areEncodingsValid, STRING_CONVERTER);
}
public static Optional areEncodingsValid(Properties properties)
{
List supportedEncodings = Splitter.on(",").trimResults().omitEmptyStrings()
.splitToList(ENCODING.getRequiredValue(properties));
for (String encoding : supportedEncodings) {
if (!QueryDataDecoders.exists(encoding)) {
return Optional.of("Unknown encoding: " + encoding);
}
}
return Optional.empty();
}
}
/**
* @deprecated use {@link AssumeLiteralUnderscoreInMetadataCallsForNonConformingClients}
*/
@Deprecated
private static class AssumeLiteralNamesInMetadataCallsForNonConformingClients
extends AbstractConnectionProperty
{
private static final Predicate IS_NOT_ENABLED = properties -> !ASSUME_LITERAL_NAMES_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS.getValueOrDefault(properties, false);
public AssumeLiteralNamesInMetadataCallsForNonConformingClients()
{
super(
PropertyName.ASSUME_LITERAL_NAMES_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS,
NOT_REQUIRED,
validator(
AssumeLiteralUnderscoreInMetadataCallsForNonConformingClients.IS_NOT_ENABLED.or(IS_NOT_ENABLED),
format(
"Connection property %s cannot be set if %s is enabled",
PropertyName.ASSUME_LITERAL_NAMES_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS,
PropertyName.ASSUME_LITERAL_UNDERSCORE_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS)),
BOOLEAN_CONVERTER);
}
}
private static class AssumeLiteralUnderscoreInMetadataCallsForNonConformingClients
extends AbstractConnectionProperty
{
private static final Predicate IS_NOT_ENABLED = properties -> !ASSUME_LITERAL_UNDERSCORE_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS.getValueOrDefault(properties, false);
public AssumeLiteralUnderscoreInMetadataCallsForNonConformingClients()
{
super(
PropertyName.ASSUME_LITERAL_UNDERSCORE_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS,
NOT_REQUIRED,
validator(
AssumeLiteralNamesInMetadataCallsForNonConformingClients.IS_NOT_ENABLED.or(IS_NOT_ENABLED),
format(
"Connection property %s cannot be set if %s is enabled",
PropertyName.ASSUME_LITERAL_UNDERSCORE_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS,
PropertyName.ASSUME_LITERAL_NAMES_IN_METADATA_CALLS_FOR_NON_CONFORMING_CLIENTS)),
BOOLEAN_CONVERTER);
}
}
private static class Ssl
extends AbstractConnectionProperty
{
public Ssl()
{
super(PropertyName.SSL, Optional.of(false), NOT_REQUIRED, ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class SslVerification
extends AbstractConnectionProperty
{
private static final Predicate IF_SSL_ENABLED = properties -> SSL.getValueOrDefault(properties, false);
static Validator validateEnabled(PropertyName propertyName)
{
return validator(
IF_SSL_ENABLED.and(properties -> !SSL_VERIFICATION.getValueOrDefault(properties, FULL).equals(SslVerificationMode.NONE)),
format("Connection property %s cannot be set if %s is set to %s", propertyName, PropertyName.SSL_VERIFICATION, SslVerificationMode.NONE));
}
static Validator validateFull(PropertyName propertyName)
{
return validator(
IF_SSL_ENABLED.and(properties -> SSL_VERIFICATION.getValueOrDefault(properties, FULL).equals(FULL)),
format("Connection property %s requires %s to be set to %s", propertyName, PropertyName.SSL_VERIFICATION, FULL));
}
public SslVerification()
{
super(
PropertyName.SSL_VERIFICATION,
Optional.of(FULL),
NOT_REQUIRED,
validator(IF_SSL_ENABLED, format("Connection property %s requires TLS/SSL to be enabled", PropertyName.SSL_VERIFICATION)),
converter(SslVerificationMode::valueOf, SslVerificationMode::name));
}
}
private static class SslKeyStorePath
extends AbstractConnectionProperty
{
private static final Validator VALIDATE_SYSTEM_KEY_STORE_NOT_ENABLED = validator(
properties -> !SSL_USE_SYSTEM_KEY_STORE.getValue(properties).orElse(false),
format("Connection property %s cannot be set if %s is enabled", PropertyName.SSL_KEY_STORE_PATH, PropertyName.SSL_USE_SYSTEM_KEY_STORE));
public SslKeyStorePath()
{
super(PropertyName.SSL_KEY_STORE_PATH, NOT_REQUIRED, VALIDATE_SYSTEM_KEY_STORE_NOT_ENABLED.and(SslVerification.validateEnabled(PropertyName.SSL_KEY_STORE_PATH)), STRING_CONVERTER);
}
}
private static class SslKeyStorePassword
extends AbstractConnectionProperty
{
private static final Validator VALID_KEY_STORE = validator(
properties -> SSL_KEY_STORE_PATH.getValue(properties).isPresent(),
format("Connection property %s requires %s to be set", PropertyName.SSL_KEY_STORE_PASSWORD, PropertyName.SSL_KEY_STORE_PATH));
public SslKeyStorePassword()
{
super(PropertyName.SSL_KEY_STORE_PASSWORD, NOT_REQUIRED, VALID_KEY_STORE.and(SslVerification.validateEnabled(PropertyName.SSL_KEY_STORE_PASSWORD)), STRING_CONVERTER);
}
}
private static class SslKeyStoreType
extends AbstractConnectionProperty
{
private static final Validator VALID_KEY_STORE = validator(
properties -> SSL_KEY_STORE_PATH.getValue(properties).isPresent() || SSL_USE_SYSTEM_KEY_STORE.getValue(properties).orElse(false),
format("Connection property %s requires %s to be set or %s to be enabled", PropertyName.SSL_KEY_STORE_TYPE, PropertyName.SSL_KEY_STORE_PATH, PropertyName.SSL_USE_SYSTEM_KEY_STORE));
public SslKeyStoreType()
{
super(PropertyName.SSL_KEY_STORE_TYPE, NOT_REQUIRED, VALID_KEY_STORE.and(SslVerification.validateEnabled(PropertyName.SSL_KEY_STORE_TYPE)), STRING_CONVERTER);
}
}
private static class SslUseSystemKeyStore
extends AbstractConnectionProperty
{
public SslUseSystemKeyStore()
{
super(PropertyName.SSL_USE_SYSTEM_KEY_STORE, NOT_REQUIRED, SslVerification.validateEnabled(PropertyName.SSL_USE_SYSTEM_KEY_STORE), BOOLEAN_CONVERTER);
}
}
private static class SslTrustStorePath
extends AbstractConnectionProperty
{
private static final Validator VALIDATE_SYSTEM_TRUST_STORE_NOT_ENABLED = validator(
properties -> !SSL_USE_SYSTEM_TRUST_STORE.getValueOrDefault(properties, false),
format("Connection property %s cannot be set if %s is enabled", PropertyName.SSL_TRUST_STORE_PATH, PropertyName.SSL_USE_SYSTEM_TRUST_STORE));
public SslTrustStorePath()
{
super(PropertyName.SSL_TRUST_STORE_PATH, NOT_REQUIRED, VALIDATE_SYSTEM_TRUST_STORE_NOT_ENABLED.and(SslVerification.validateEnabled(PropertyName.SSL_TRUST_STORE_PATH)), STRING_CONVERTER);
}
}
private static class SslTrustStorePassword
extends AbstractConnectionProperty
{
private static final Validator VALIDATE_TRUST_STORE = validator(
properties -> SSL_TRUST_STORE_PATH.getValue(properties).isPresent(),
format("Connection property %s requires %s to be set", PropertyName.SSL_TRUST_STORE_PASSWORD, PropertyName.SSL_TRUST_STORE_PATH));
public SslTrustStorePassword()
{
super(PropertyName.SSL_TRUST_STORE_PASSWORD, NOT_REQUIRED, VALIDATE_TRUST_STORE.and(SslVerification.validateEnabled(PropertyName.SSL_TRUST_STORE_PASSWORD)), STRING_CONVERTER);
}
}
private static class SslTrustStoreType
extends AbstractConnectionProperty
{
private static final Validator VALIDATE_TRUST_STORE = validator(
properties -> SSL_TRUST_STORE_PATH.getValue(properties).isPresent() || SSL_USE_SYSTEM_TRUST_STORE.getValueOrDefault(properties, false),
format("Connection property %s requires %s to be set or %s to be enabled", PropertyName.SSL_TRUST_STORE_TYPE, PropertyName.SSL_TRUST_STORE_PATH, PropertyName.SSL_USE_SYSTEM_TRUST_STORE));
public SslTrustStoreType()
{
super(PropertyName.SSL_TRUST_STORE_TYPE, NOT_REQUIRED, VALIDATE_TRUST_STORE.and(SslVerification.validateEnabled(PropertyName.SSL_TRUST_STORE_TYPE)), STRING_CONVERTER);
}
}
private static class SslUseSystemTrustStore
extends AbstractConnectionProperty
{
public SslUseSystemTrustStore()
{
super(PropertyName.SSL_USE_SYSTEM_TRUST_STORE, NOT_REQUIRED, SslVerification.validateEnabled(PropertyName.SSL_USE_SYSTEM_TRUST_STORE), BOOLEAN_CONVERTER);
}
}
private static class KerberosRemoteServiceName
extends AbstractConnectionProperty
{
public KerberosRemoteServiceName()
{
super(PropertyName.KERBEROS_REMOTE_SERVICE_NAME, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static Predicate isKerberosEnabled()
{
return properties -> KERBEROS_REMOTE_SERVICE_NAME.getValue(properties).isPresent();
}
private static Validator validateKerberosWithoutDelegation(PropertyName propertyName)
{
return validator(isKerberosEnabled(), format("Connection property %s requires %s to be set", propertyName, PropertyName.KERBEROS_REMOTE_SERVICE_NAME))
.and(validator(
properties -> !KERBEROS_DELEGATION.getValueOrDefault(properties, false),
format("Connection property %s cannot be set if %s is enabled", propertyName, PropertyName.KERBEROS_DELEGATION)));
}
private static Validator validateKerberosWithDelegation(PropertyName propertyName)
{
return validator(isKerberosEnabled(), format("Connection property %s requires %s to be set", propertyName, PropertyName.KERBEROS_REMOTE_SERVICE_NAME))
.and(validator(
properties -> KERBEROS_DELEGATION.getValueOrDefault(properties, false),
format("Connection property %s requires %s to be enabled", propertyName, PropertyName.KERBEROS_DELEGATION)));
}
private static class KerberosServicePrincipalPattern
extends AbstractConnectionProperty
{
public KerberosServicePrincipalPattern()
{
super(PropertyName.KERBEROS_SERVICE_PRINCIPAL_PATTERN, Optional.of("${SERVICE}@${HOST}"), isKerberosEnabled(), ALLOWED, STRING_CONVERTER);
}
}
private static class KerberosPrincipal
extends AbstractConnectionProperty
{
public KerberosPrincipal()
{
super(PropertyName.KERBEROS_PRINCIPAL, NOT_REQUIRED, validateKerberosWithoutDelegation(PropertyName.KERBEROS_PRINCIPAL), STRING_CONVERTER);
}
}
private static class KerberosUseCanonicalHostname
extends AbstractConnectionProperty
{
public KerberosUseCanonicalHostname()
{
super(PropertyName.KERBEROS_USE_CANONICAL_HOSTNAME, Optional.of(true), isKerberosEnabled(), ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class KerberosConfigPath
extends AbstractConnectionProperty
{
public KerberosConfigPath()
{
super(PropertyName.KERBEROS_CONFIG_PATH, NOT_REQUIRED, validateKerberosWithoutDelegation(PropertyName.KERBEROS_CONFIG_PATH), FILE_CONVERTER);
}
}
private static class KerberosKeytabPath
extends AbstractConnectionProperty
{
public KerberosKeytabPath()
{
super(PropertyName.KERBEROS_KEYTAB_PATH, NOT_REQUIRED, validateKerberosWithoutDelegation(PropertyName.KERBEROS_KEYTAB_PATH), FILE_CONVERTER);
}
}
private static class KerberosCredentialCachePath
extends AbstractConnectionProperty
{
public KerberosCredentialCachePath()
{
super(PropertyName.KERBEROS_CREDENTIAL_CACHE_PATH, NOT_REQUIRED, validateKerberosWithoutDelegation(PropertyName.KERBEROS_CREDENTIAL_CACHE_PATH), FILE_CONVERTER);
}
}
private static class KerberosDelegation
extends AbstractConnectionProperty
{
public KerberosDelegation()
{
super(PropertyName.KERBEROS_DELEGATION, Optional.of(false), isKerberosEnabled(), ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class KerberosConstrainedDelegation
extends AbstractConnectionProperty
{
public KerberosConstrainedDelegation()
{
super(PropertyName.KERBEROS_CONSTRAINED_DELEGATION, Optional.empty(), NOT_REQUIRED, validateKerberosWithDelegation(PropertyName.KERBEROS_CONSTRAINED_DELEGATION), converter(GSSCredential.class::cast, identity()));
}
}
private static class AccessToken
extends AbstractConnectionProperty
{
public AccessToken()
{
super(PropertyName.ACCESS_TOKEN, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class ExternalAuthentication
extends AbstractConnectionProperty
{
public ExternalAuthentication()
{
super(PropertyName.EXTERNAL_AUTHENTICATION, Optional.of(false), NOT_REQUIRED, ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class ExternalAuthenticationRedirectHandlers
extends AbstractConnectionProperty>
{
private static final Splitter ENUM_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
public ExternalAuthenticationRedirectHandlers()
{
super(
PropertyName.EXTERNAL_AUTHENTICATION_REDIRECT_HANDLERS,
Optional.of(singletonList(ExternalRedirectStrategy.OPEN)),
NOT_REQUIRED,
ALLOWED,
converter(ExternalAuthenticationRedirectHandlers::parse, ExternalAuthenticationRedirectHandlers::toString));
}
public static List parse(String value)
{
return stream(ENUM_SPLITTER.split(value))
.map(ExternalRedirectStrategy::valueOf)
.collect(toImmutableList());
}
public static String toString(List values)
{
return values.stream()
.map(ExternalRedirectStrategy::toString)
.collect(Collectors.joining(","));
}
}
private static class ExternalAuthenticationTimeout
extends AbstractConnectionProperty
{
private static final Validator VALIDATE_EXTERNAL_AUTHENTICATION_ENABLED = validator(
properties -> EXTERNAL_AUTHENTICATION.getValueOrDefault(properties, false),
format("Connection property %s requires %s to be enabled", PropertyName.EXTERNAL_AUTHENTICATION_TIMEOUT, PropertyName.EXTERNAL_AUTHENTICATION));
public ExternalAuthenticationTimeout()
{
super(PropertyName.EXTERNAL_AUTHENTICATION_TIMEOUT, NOT_REQUIRED, VALIDATE_EXTERNAL_AUTHENTICATION_ENABLED, converter(Duration::valueOf, Duration::toString));
}
}
private static class ExternalAuthenticationTokenCache
extends AbstractConnectionProperty
{
public ExternalAuthenticationTokenCache()
{
super(PropertyName.EXTERNAL_AUTHENTICATION_TOKEN_CACHE, Optional.of(KnownTokenCache.NONE), NOT_REQUIRED, ALLOWED, converter(KnownTokenCache::valueOf, KnownTokenCache::name));
}
}
private static class ExtraCredentials
extends AbstractConnectionProperty>
{
public ExtraCredentials()
{
super(PropertyName.EXTRA_CREDENTIALS, NOT_REQUIRED, ALLOWED, converter(ExtraCredentials::parseExtraCredentials, ExtraCredentials::toString));
}
// Extra credentials consists of a list of credential name value pairs.
// E.g., `jdbc:trino://example.net:8080/?extraCredentials=abc:xyz;foo:bar` will create credentials `abc=xyz` and `foo=bar`
public static Map parseExtraCredentials(String extraCredentialString)
{
return new MapPropertyParser(PropertyName.EXTRA_CREDENTIALS.toString()).parse(extraCredentialString);
}
public static String toString(Map values)
{
return values.entrySet().stream()
.map(entry -> entry.getKey() + ":" + entry.getValue())
.collect(Collectors.joining(";"));
}
}
private static class SessionProperties
extends AbstractConnectionProperty>
{
private static final Splitter NAME_PARTS_SPLITTER = Splitter.on('.');
public SessionProperties()
{
super(PropertyName.SESSION_PROPERTIES, NOT_REQUIRED, ALLOWED, converter(SessionProperties::parseSessionProperties, SessionProperties::toString));
}
// Session properties consists of a list of session property name value pairs.
// E.g., `jdbc:trino://example.net:8080/?sessionProperties=abc:xyz;catalog.foo:bar` will create session properties `abc=xyz` and `catalog.foo=bar`
public static Map parseSessionProperties(String sessionPropertiesString)
{
Map sessionProperties = new MapPropertyParser(PropertyName.SESSION_PROPERTIES.toString()).parse(sessionPropertiesString);
for (String sessionPropertyName : sessionProperties.keySet()) {
checkArgument(NAME_PARTS_SPLITTER.splitToList(sessionPropertyName).size() <= 2, "Malformed session property name: %s", sessionPropertyName);
}
return sessionProperties;
}
public static String toString(Map values)
{
return values.entrySet().stream()
.map(entry -> entry.getKey() + ":" + entry.getValue())
.collect(Collectors.joining(";"));
}
}
private static class Source
extends AbstractConnectionProperty
{
public Source()
{
super(PropertyName.SOURCE, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class Schema
extends AbstractConnectionProperty
{
public Schema()
{
super(PropertyName.SCHEMA, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class Catalog
extends AbstractConnectionProperty
{
public Catalog()
{
super(PropertyName.CATALOG, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class UserLocale
extends AbstractConnectionProperty
{
public UserLocale()
{
super(PropertyName.LOCALE, NOT_REQUIRED, ALLOWED, converter(Locale::new, Locale::toString));
}
}
private static class Timeout
extends AbstractConnectionProperty
{
protected Timeout()
{
super(PropertyName.TIMEOUT, NOT_REQUIRED, ALLOWED, converter(Duration::valueOf, Duration::toString));
}
}
private static class SqlPath
extends AbstractConnectionProperty>
{
protected SqlPath()
{
super(PropertyName.SQL_PATH, NOT_REQUIRED, SqlPath::isValidSqlPath, converter(Splitter.on(",")::splitToList, Joiner.on(",")::join));
}
private static Optional isValidSqlPath(Properties properties)
{
String paths = properties.getProperty(SQL_PATH.getKey());
if (paths == null) {
return Optional.empty();
}
for (String path : Splitter.on(',').split(paths)) {
if (Splitter.on('.').splitToList(path).size() > 2) {
return Optional.of(format("Connection property '%s' has invalid syntax, should be [catalog].[schema] or [schema]", SQL_PATH.getKey()));
}
}
return Optional.empty();
}
}
private static class HttpLoggingLevel
extends AbstractConnectionProperty
{
protected HttpLoggingLevel()
{
super(PropertyName.HTTP_LOGGING_LEVEL, NOT_REQUIRED, ALLOWED, converter(LoggingLevel::valueOf, LoggingLevel::toString));
}
}
private static class Resolver
extends AbstractConnectionProperty>
{
public Resolver()
{
super(PropertyName.DNS_RESOLVER, NOT_REQUIRED, ALLOWED, converter(Resolver::findByName, Class::getName));
}
public static Class extends DnsResolver> findByName(String name)
{
try {
return Class.forName(name).asSubclass(DnsResolver.class);
}
catch (ClassNotFoundException e) {
throw new RuntimeException("DNS resolver class not found: " + name, e);
}
}
}
private static class ResolverContext
extends AbstractConnectionProperty
{
public ResolverContext()
{
super(PropertyName.DNS_RESOLVER_CONTEXT, NOT_REQUIRED, ALLOWED, STRING_CONVERTER);
}
}
private static class HostnameInCertificate
extends AbstractConnectionProperty
{
public HostnameInCertificate()
{
super(PropertyName.HOSTNAME_IN_CERTIFICATE, NOT_REQUIRED, SslVerification.validateFull(PropertyName.HOSTNAME_IN_CERTIFICATE), STRING_CONVERTER);
}
}
private static class TimeZone
extends AbstractConnectionProperty
{
public TimeZone()
{
super(PropertyName.TIMEZONE, NOT_REQUIRED, ALLOWED, converter(ZoneId::of, ZoneId::getId));
}
}
private static class ExplicitPrepare
extends AbstractConnectionProperty
{
public ExplicitPrepare()
{
super(PropertyName.EXPLICIT_PREPARE, NOT_REQUIRED, ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class AssumeNullCatalogMeansCurrentCatalog
extends AbstractConnectionProperty
{
public AssumeNullCatalogMeansCurrentCatalog()
{
super(PropertyName.ASSUME_NULL_CATALOG_MEANS_CURRENT_CATALOG, NOT_REQUIRED, ALLOWED, BOOLEAN_CONVERTER);
}
}
private static class MapPropertyParser
{
private static final CharMatcher PRINTABLE_ASCII = CharMatcher.inRange((char) 0x21, (char) 0x7E);
private static final Splitter MAP_ENTRIES_SPLITTER = Splitter.on(';');
private static final Splitter MAP_ENTRY_SPLITTER = Splitter.on(':');
private final String mapName;
private MapPropertyParser(String mapName)
{
this.mapName = requireNonNull(mapName, "mapName is null");
}
/**
* Parses map in a form: key1:value1;key2:value2
*/
public Map parse(String map)
{
return MAP_ENTRIES_SPLITTER.splitToList(map).stream()
.map(this::parseEntry)
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
private Map.Entry parseEntry(String credential)
{
List keyValue = MAP_ENTRY_SPLITTER.limit(2).splitToList(credential);
checkArgument(keyValue.size() == 2, "Malformed %s: %s", mapName, credential);
String key = keyValue.get(0);
String value = keyValue.get(1);
checkArgument(!key.isEmpty(), "%s key is empty", mapName);
checkArgument(!value.isEmpty(), "%s key is empty", mapName);
checkArgument(PRINTABLE_ASCII.matchesAllOf(key), "%s key '%s' contains spaces or is not printable ASCII", mapName, key);
// do not log value as it may contain sensitive information
checkArgument(PRINTABLE_ASCII.matchesAllOf(value), "%s value for key '%s' contains spaces or is not printable ASCII", mapName, key);
return immutableEntry(key, value);
}
}
}