Configuration.java
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2025 MariaDB Corporation Ab
package org.mariadb.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;
import org.mariadb.jdbc.export.HaMode;
import org.mariadb.jdbc.export.SslMode;
import org.mariadb.jdbc.plugin.Codec;
import org.mariadb.jdbc.plugin.CredentialPlugin;
import org.mariadb.jdbc.plugin.credential.CredentialPluginLoader;
import org.mariadb.jdbc.util.constants.CatalogTerm;
import org.mariadb.jdbc.util.constants.MetaExportedKeys;
import org.mariadb.jdbc.util.log.Logger;
import org.mariadb.jdbc.util.log.Loggers;
import org.mariadb.jdbc.util.options.OptionAliases;
/**
* parse and verification of URL.
*
* <p>basic syntax :<br>
* {@code
* jdbc:mariadb:[replication:|failover|loadbalance:|aurora:]//<hostDescription>[,<hostDescription>]/[database>]
* [?<key1>=<value1>[&<key2>=<value2>]] }
*
* <p>hostDescription:<br>
* - simple :<br>
* {@code <host>:<portnumber>}<br>
* (for example localhost:3306)<br>
* <br>
* - complex :<br>
* {@code address=[(type=(master|slave))][(port=<portnumber>)](host=<host>)}<br>
* <br>
* <br>
* type is by default master<br>
* port is by default 3306<br>
*
* <p>host can be dns name, ipv4 or ipv6.<br>
* in case of ipv6 and simple host description, the ip must be written inside bracket.<br>
* exemple : {@code jdbc:mariadb://[2001:0660:7401:0200:0000:0000:0edf:bdd7]:3306}<br>
*
* <p>Some examples :<br>
* {@code jdbc:mariadb://localhost:3306/database?user=greg&password=pass}<br>
* {@code
* jdbc:mariadb://address=(type=master)(host=master1),address=(port=3307)(type=slave)(host=slave1)/database?user=greg&password=pass}
* <br>
*/
public class Configuration {
private static final Logger logger = Loggers.getLogger(Configuration.class);
private static final Set<String> EXCLUDED_FIELDS;
private static final Set<String> SECURE_FIELDS;
private static final Set<String> PROPERTIES_TO_SKIP;
private static final Set<String> SENSITIVE_FIELDS;
private static final String CATALOG_TERM = "CATALOG";
private static final String SCHEMA_TERM = "SCHEMA";
private static Codec<?>[] cachedCodecs = null;
static {
EXCLUDED_FIELDS = new HashSet<>();
EXCLUDED_FIELDS.add("database");
EXCLUDED_FIELDS.add("haMode");
EXCLUDED_FIELDS.add("$jacocoData");
EXCLUDED_FIELDS.add("addresses");
SECURE_FIELDS = new HashSet<>();
SECURE_FIELDS.add("password");
SECURE_FIELDS.add("keyStorePassword");
SECURE_FIELDS.add("trustStorePassword");
PROPERTIES_TO_SKIP = new HashSet<>();
PROPERTIES_TO_SKIP.add("initialUrl");
PROPERTIES_TO_SKIP.add("logger");
PROPERTIES_TO_SKIP.add("codecs");
PROPERTIES_TO_SKIP.add("$jacocoData");
PROPERTIES_TO_SKIP.add("CATALOG_TERM");
PROPERTIES_TO_SKIP.add("SCHEMA_TERM");
PROPERTIES_TO_SKIP.add("cachedCodecs");
SENSITIVE_FIELDS = new HashSet<>();
SENSITIVE_FIELDS.add("password");
SENSITIVE_FIELDS.add("keyStorePassword");
SENSITIVE_FIELDS.add("trustStorePassword");
}
// standard options
private String user;
private String password;
private String database;
private List<HostAddress> addresses;
private HaMode haMode;
private String initialUrl;
private Properties nonMappedOptions;
// various
private String timezone;
private String connectionCollation;
private String connectionTimeZone;
private Boolean forceConnectionTimeZoneToSession;
private boolean preserveInstants;
private Boolean autocommit;
private boolean useMysqlMetadata;
private boolean nullDatabaseMeansCurrent;
private CatalogTerm useCatalogTerm;
private boolean createDatabaseIfNotExist;
private boolean useLocalSessionState;
private boolean returnMultiValuesGeneratedIds;
private boolean jdbcCompliantTruncation;
private boolean oldModeNoPrecisionTimestamp;
private Boolean permitRedirect;
private TransactionIsolation transactionIsolation;
private int defaultFetchSize;
private int maxQuerySizeToLog;
private Integer maxAllowedPacket;
private String geometryDefaultType;
private String restrictedAuth;
private String initSql;
private boolean pinGlobalTxToPhysicalConnection;
private boolean permitNoResults;
private boolean cacheCodecs;
private MetaExportedKeys metaExportedKeys;
// socket
private String socketFactory;
private int connectTimeout;
private String pipe;
private String localSocket;
private boolean uuidAsString;
private boolean tcpKeepAlive;
private int tcpKeepIdle;
private int tcpKeepCount;
private int tcpKeepInterval;
private boolean tcpAbortiveClose;
private String localSocketAddress;
private int socketTimeout;
private boolean useReadAheadInput;
private String tlsSocketType;
// SSL
private SslMode sslMode;
private String serverSslCert;
private String keyStore;
private String trustStore;
private String keyStorePassword;
private String trustStorePassword;
private String keyPassword;
private String keyStoreType;
private String trustStoreType;
private String enabledSslCipherSuites;
private String enabledSslProtocolSuites;
private boolean fallbackToSystemKeyStore;
private boolean fallbackToSystemTrustStore;
// protocol
private boolean allowMultiQueries;
private boolean allowLocalInfile;
private boolean useCompression;
private boolean useAffectedRows;
private boolean useBulkStmts;
private boolean useBulkStmtsForInserts;
private boolean disablePipeline;
// prepare
private boolean cachePrepStmts;
private int prepStmtCacheSize;
private boolean useServerPrepStmts;
// authentication
private CredentialPlugin credentialType;
private String sessionVariables;
private String connectionAttributes;
private String servicePrincipalName;
private boolean disconnectOnExpiredPasswords;
// meta
private boolean blankTableNameMeta;
private boolean tinyInt1isBit;
private boolean transformedBitIsBoolean;
private boolean yearIsDateType;
private boolean dumpQueriesOnException;
private boolean includeInnodbStatusInDeadlockExceptions;
private boolean includeThreadDumpInDeadlockExceptions;
// HA options
private int retriesAllDown;
private String galeraAllowedState;
private boolean transactionReplay;
private int transactionReplaySize;
// Pool options
private boolean pool;
private String poolName;
private int maxPoolSize;
private int minPoolSize;
private int maxIdleTime;
private boolean registerJmxPool;
private int poolValidMinDelay;
private boolean useResetConnection;
// MySQL sha authentication
private String serverRsaPublicKeyFile;
private boolean allowPublicKeyRetrieval;
private Codec<?>[] codecs;
private Configuration(Builder builder) {
// Set basic configuration
initializeBasicConfig(builder);
// Set SSL/TLS configuration
initializeSslConfig(builder);
// Set socket configuration
initializeSocketConfig(builder);
// Set transaction and protocol settings
initializeTransactionConfig(builder);
// Set data type handling
initializeDataTypeConfig(builder);
// Set timezone settings
initializeTimezoneConfig(builder);
// Set query and statement handling
initializeQueryConfig(builder);
// Set bulk operations
initializeBulkConfig(builder);
// Set pipeline and transaction settings
initializePipelineConfig(builder);
// Set database and schema settings
initializeDatabaseConfig(builder);
// Set exception handling
initializeExceptionConfig(builder);
// Set pool configuration
initializePoolConfig(builder);
// Set security settings
initializeSecurityConfig(builder);
// Set additional properties
initializeAdditionalConfig(builder);
// Configure hosts
configureHosts();
// Validate configuration
validateConfiguration();
}
private void initializeBasicConfig(Builder builder) {
this.database = builder.database;
this.addresses = builder._addresses;
this.nonMappedOptions = builder._nonMappedOptions;
this.haMode = builder._haMode != null ? builder._haMode : HaMode.NONE;
this.credentialType = CredentialPluginLoader.get(builder.credentialType);
this.user = builder.user;
this.password = builder.password;
this.metaExportedKeys =
builder.metaExportedKeys != null
? MetaExportedKeys.from(builder.metaExportedKeys)
: MetaExportedKeys.Auto;
}
private void initializeSslConfig(Builder builder) {
this.enabledSslProtocolSuites = builder.enabledSslProtocolSuites;
this.fallbackToSystemKeyStore =
builder.fallbackToSystemKeyStore == null || builder.fallbackToSystemKeyStore;
this.fallbackToSystemTrustStore =
builder.fallbackToSystemTrustStore == null || builder.fallbackToSystemTrustStore;
this.serverSslCert = builder.serverSslCert;
this.keyStore = builder.keyStore;
this.trustStore = builder.trustStore;
this.keyStorePassword = builder.keyStorePassword;
this.trustStorePassword = builder.trustStorePassword;
this.keyPassword = builder.keyPassword;
this.keyStoreType = builder.keyStoreType;
this.trustStoreType = builder.trustStoreType;
// SSL Mode configuration
if (this.credentialType != null
&& this.credentialType.mustUseSsl()
&& (builder.sslMode == null || SslMode.from(builder.sslMode) == SslMode.DISABLE)) {
this.sslMode = SslMode.VERIFY_FULL;
} else {
this.sslMode = builder.sslMode != null ? SslMode.from(builder.sslMode) : SslMode.DISABLE;
}
}
private void initializeSocketConfig(Builder builder) {
this.socketFactory = builder.socketFactory;
this.connectTimeout =
builder.connectTimeout != null
? builder.connectTimeout
: (DriverManager.getLoginTimeout() > 0
? DriverManager.getLoginTimeout() * 1000
: 30_000);
this.pipe = builder.pipe;
this.localSocket = builder.localSocket;
this.tcpKeepAlive = builder.tcpKeepAlive == null || builder.tcpKeepAlive;
this.uuidAsString = builder.uuidAsString != null && builder.uuidAsString;
this.tcpKeepIdle = builder.tcpKeepIdle != null ? builder.tcpKeepIdle : 0;
this.tcpKeepCount = builder.tcpKeepCount != null ? builder.tcpKeepCount : 0;
this.tcpKeepInterval = builder.tcpKeepInterval != null ? builder.tcpKeepInterval : 0;
this.tcpAbortiveClose = builder.tcpAbortiveClose != null && builder.tcpAbortiveClose;
this.localSocketAddress = builder.localSocketAddress;
this.socketTimeout = builder.socketTimeout != null ? builder.socketTimeout : 0;
this.useReadAheadInput = builder.useReadAheadInput != null && builder.useReadAheadInput;
this.tlsSocketType = builder.tlsSocketType;
this.useCompression = builder.useCompression != null && builder.useCompression;
}
private void initializeTransactionConfig(Builder builder) {
this.transactionIsolation =
builder.transactionIsolation != null
? TransactionIsolation.from(builder.transactionIsolation)
: null;
this.enabledSslCipherSuites = builder.enabledSslCipherSuites;
this.sessionVariables = builder.sessionVariables;
}
private void initializeDataTypeConfig(Builder builder) {
this.tinyInt1isBit = builder.tinyInt1isBit == null || builder.tinyInt1isBit;
this.transformedBitIsBoolean =
builder.transformedBitIsBoolean == null || builder.transformedBitIsBoolean;
this.yearIsDateType = builder.yearIsDateType == null || builder.yearIsDateType;
}
private void initializeTimezoneConfig(Builder builder) {
this.timezone = builder.timezone;
this.connectionTimeZone = builder.connectionTimeZone;
this.connectionCollation = builder.connectionCollation;
this.forceConnectionTimeZoneToSession = builder.forceConnectionTimeZoneToSession;
this.preserveInstants = builder.preserveInstants != null && builder.preserveInstants;
}
private void initializeQueryConfig(Builder builder) {
this.dumpQueriesOnException =
builder.dumpQueriesOnException != null && builder.dumpQueriesOnException;
this.prepStmtCacheSize = builder.prepStmtCacheSize != null ? builder.prepStmtCacheSize : 250;
this.useAffectedRows = builder.useAffectedRows != null && builder.useAffectedRows;
this.useServerPrepStmts = builder.useServerPrepStmts != null && builder.useServerPrepStmts;
this.connectionAttributes = builder.connectionAttributes;
this.allowLocalInfile = builder.allowLocalInfile == null || builder.allowLocalInfile;
this.allowMultiQueries = builder.allowMultiQueries != null && builder.allowMultiQueries;
}
private void initializeBulkConfig(Builder builder) {
this.useBulkStmts = builder.useBulkStmts != null && builder.useBulkStmts;
this.useBulkStmtsForInserts =
builder.useBulkStmtsForInserts != null
? builder.useBulkStmtsForInserts
: (builder.useBulkStmts == null || builder.useBulkStmts);
}
private void initializePipelineConfig(Builder builder) {
this.disablePipeline = builder.disablePipeline != null && builder.disablePipeline;
this.autocommit = builder.autocommit;
this.useMysqlMetadata = builder.useMysqlMetadata != null && builder.useMysqlMetadata;
this.nullDatabaseMeansCurrent =
builder.nullDatabaseMeansCurrent != null && builder.nullDatabaseMeansCurrent;
}
private void initializeDatabaseConfig(Builder builder) {
if (builder.useCatalogTerm != null) {
if (!CATALOG_TERM.equalsIgnoreCase(builder.useCatalogTerm)
&& !SCHEMA_TERM.equalsIgnoreCase(builder.useCatalogTerm)) {
throw new IllegalArgumentException(
"useCatalogTerm can only have CATALOG/SCHEMA value, current set value is "
+ builder.useCatalogTerm);
}
this.useCatalogTerm =
CATALOG_TERM.equalsIgnoreCase(builder.useCatalogTerm)
? CatalogTerm.UseCatalog
: CatalogTerm.UseSchema;
} else {
this.useCatalogTerm = CatalogTerm.UseCatalog;
}
this.createDatabaseIfNotExist =
builder.createDatabaseIfNotExist != null && builder.createDatabaseIfNotExist;
this.useLocalSessionState =
builder.useLocalSessionState != null && builder.useLocalSessionState;
this.returnMultiValuesGeneratedIds =
builder.returnMultiValuesGeneratedIds != null && builder.returnMultiValuesGeneratedIds;
this.jdbcCompliantTruncation =
builder.jdbcCompliantTruncation == null || builder.jdbcCompliantTruncation;
this.oldModeNoPrecisionTimestamp =
builder.oldModeNoPrecisionTimestamp != null && builder.oldModeNoPrecisionTimestamp;
this.permitRedirect = builder.permitRedirect;
this.pinGlobalTxToPhysicalConnection =
builder.pinGlobalTxToPhysicalConnection != null && builder.pinGlobalTxToPhysicalConnection;
this.permitNoResults = builder.permitNoResults == null || builder.permitNoResults;
this.cacheCodecs = builder.cacheCodecs != null && builder.cacheCodecs;
this.blankTableNameMeta = builder.blankTableNameMeta != null && builder.blankTableNameMeta;
this.disconnectOnExpiredPasswords =
builder.disconnectOnExpiredPasswords == null || builder.disconnectOnExpiredPasswords;
}
private void initializeExceptionConfig(Builder builder) {
this.includeInnodbStatusInDeadlockExceptions =
builder.includeInnodbStatusInDeadlockExceptions != null
&& builder.includeInnodbStatusInDeadlockExceptions;
this.includeThreadDumpInDeadlockExceptions =
builder.includeThreadDumpInDeadlockExceptions != null
&& builder.includeThreadDumpInDeadlockExceptions;
}
private void initializePoolConfig(Builder builder) {
this.pool = builder.pool != null && builder.pool;
this.poolName = builder.poolName;
this.maxPoolSize = builder.maxPoolSize != null ? builder.maxPoolSize : 8;
this.minPoolSize = builder.minPoolSize != null ? builder.minPoolSize : this.maxPoolSize;
this.maxIdleTime = builder.maxIdleTime != null ? builder.maxIdleTime : 600_000;
this.registerJmxPool = builder.registerJmxPool == null || builder.registerJmxPool;
this.poolValidMinDelay = builder.poolValidMinDelay != null ? builder.poolValidMinDelay : 1000;
this.useResetConnection = builder.useResetConnection != null && builder.useResetConnection;
}
private void initializeSecurityConfig(Builder builder) {
this.serverRsaPublicKeyFile =
builder.serverRsaPublicKeyFile != null && !builder.serverRsaPublicKeyFile.isEmpty()
? builder.serverRsaPublicKeyFile
: null;
this.allowPublicKeyRetrieval =
builder.allowPublicKeyRetrieval != null && builder.allowPublicKeyRetrieval;
}
private void initializeAdditionalConfig(Builder builder) {
this.servicePrincipalName = builder.servicePrincipalName;
this.defaultFetchSize = builder.defaultFetchSize != null ? builder.defaultFetchSize : 0;
this.tlsSocketType = builder.tlsSocketType;
this.maxQuerySizeToLog = builder.maxQuerySizeToLog != null ? builder.maxQuerySizeToLog : 1024;
this.maxAllowedPacket = builder.maxAllowedPacket;
this.retriesAllDown = builder.retriesAllDown != null ? builder.retriesAllDown : 120;
this.galeraAllowedState = builder.galeraAllowedState;
this.cachePrepStmts = builder.cachePrepStmts == null || builder.cachePrepStmts;
this.transactionReplay = builder.transactionReplay != null && builder.transactionReplay;
this.transactionReplaySize =
builder.transactionReplaySize != null ? builder.transactionReplaySize : 64;
this.geometryDefaultType = builder.geometryDefaultType;
this.restrictedAuth = builder.restrictedAuth;
this.initSql = builder.initSql;
this.codecs = null;
}
private void configureHosts() {
if (addresses.isEmpty()) {
if (this.localSocket != null) {
addresses.add(HostAddress.localSocket(this.localSocket));
} else if (this.pipe != null) {
addresses.add(HostAddress.pipe(this.pipe));
}
} else {
if (this.localSocket != null) {
List<HostAddress> newAddresses = new ArrayList<>();
for (HostAddress host : addresses) {
newAddresses.add(host.withLocalSocket(this.localSocket));
}
this.addresses = newAddresses;
}
if (this.pipe != null) {
List<HostAddress> newAddresses = new ArrayList<>();
for (HostAddress host : addresses) {
newAddresses.add(host.withPipe(this.pipe));
}
this.addresses = newAddresses;
}
}
// Configure host primary settings
boolean first = true;
for (HostAddress host : addresses) {
boolean primary = haMode != HaMode.REPLICATION || first;
if (host.primary == null) {
host.primary = primary;
}
first = false;
}
}
private void validateConfiguration() {
// Validate timezone settings
if (this.timezone != null && this.connectionTimeZone == null) {
if ("disable".equalsIgnoreCase(this.timezone)) {
this.forceConnectionTimeZoneToSession = false;
} else {
this.forceConnectionTimeZoneToSession = true;
if (!"auto".equalsIgnoreCase(this.timezone)) {
this.connectionTimeZone = this.timezone;
}
}
}
// Validate connection collation
if (connectionCollation != null) {
if (connectionCollation.trim().isEmpty()) {
this.connectionCollation = null;
} else {
if (!connectionCollation.toLowerCase(Locale.ROOT).startsWith("utf8mb4_")) {
throw new IllegalArgumentException(
String.format(
"wrong connection collation '%s' only utf8mb4 collation are accepted",
connectionCollation));
} else if (!connectionCollation.matches("\\w+$")) {
throw new IllegalArgumentException(
String.format("wrong connection collation '%s' name", connectionCollation));
}
}
}
// Validate integer fields
validateIntegerFields();
}
private void validateIntegerFields() {
Field[] fields = Configuration.class.getDeclaredFields();
try {
for (Field field : fields) {
if (field.getType().equals(int.class)) {
int val = field.getInt(this);
if (val < 0) {
throw new IllegalArgumentException(
String.format("Value for %s must be >= 1 (value is %s)", field.getName(), val));
}
}
}
} catch (IllegalAccessException ie) {
// Ignore reflection errors
}
}
/**
* Create a Builder from current configuration. Since configuration data are final, this permit to
* change configuration, creating another object.
*
* @return builder
*/
public Builder toBuilder() {
Builder builder =
new Builder()
.user(this.user)
.password(this.password)
.database(this.database)
.addresses(this.addresses == null ? null : this.addresses.toArray(new HostAddress[0]))
.haMode(this.haMode)
.timezone(this.timezone)
.connectionTimeZone(this.connectionTimeZone)
.connectionCollation(this.connectionCollation)
.forceConnectionTimeZoneToSession(this.forceConnectionTimeZoneToSession)
.preserveInstants(this.preserveInstants)
.autocommit(this.autocommit)
.useMysqlMetadata(this.useMysqlMetadata)
.nullDatabaseMeansCurrent(this.nullDatabaseMeansCurrent)
.useCatalogTerm(
this.useCatalogTerm == CatalogTerm.UseCatalog ? CATALOG_TERM : SCHEMA_TERM)
.createDatabaseIfNotExist(this.createDatabaseIfNotExist)
.useLocalSessionState(this.useLocalSessionState)
.returnMultiValuesGeneratedIds(this.returnMultiValuesGeneratedIds)
.jdbcCompliantTruncation(this.jdbcCompliantTruncation)
.oldModeNoPrecisionTimestamp(this.oldModeNoPrecisionTimestamp)
.permitRedirect(this.permitRedirect)
.pinGlobalTxToPhysicalConnection(this.pinGlobalTxToPhysicalConnection)
.permitNoResults(this.permitNoResults)
.cacheCodecs(this.cacheCodecs)
.transactionIsolation(
transactionIsolation == null ? null : this.transactionIsolation.getValue())
.metaExportedKeys(metaExportedKeys == null ? null : this.metaExportedKeys.name())
.defaultFetchSize(this.defaultFetchSize)
.maxQuerySizeToLog(this.maxQuerySizeToLog)
.maxAllowedPacket(this.maxAllowedPacket)
.geometryDefaultType(this.geometryDefaultType)
.geometryDefaultType(this.geometryDefaultType)
.restrictedAuth(this.restrictedAuth)
.initSql(this.initSql)
.socketFactory(this.socketFactory)
.connectTimeout(this.connectTimeout)
.pipe(this.pipe)
.localSocket(this.localSocket)
.uuidAsString(this.uuidAsString)
.tcpKeepAlive(this.tcpKeepAlive)
.tcpKeepIdle(this.tcpKeepIdle)
.tcpKeepCount(this.tcpKeepCount)
.tcpKeepInterval(this.tcpKeepInterval)
.tcpAbortiveClose(this.tcpAbortiveClose)
.localSocketAddress(this.localSocketAddress)
.socketTimeout(this.socketTimeout)
.useReadAheadInput(this.useReadAheadInput)
.tlsSocketType(this.tlsSocketType)
.sslMode(this.sslMode.name())
.serverSslCert(this.serverSslCert)
.keyStore(this.keyStore)
.trustStore(this.trustStore)
.keyStoreType(this.keyStoreType)
.keyStorePassword(this.keyStorePassword)
.trustStorePassword(this.trustStorePassword)
.keyPassword(this.keyPassword)
.trustStoreType(this.trustStoreType)
.enabledSslCipherSuites(this.enabledSslCipherSuites)
.enabledSslProtocolSuites(this.enabledSslProtocolSuites)
.fallbackToSystemKeyStore(this.fallbackToSystemKeyStore)
.fallbackToSystemTrustStore(this.fallbackToSystemTrustStore)
.allowMultiQueries(this.allowMultiQueries)
.allowLocalInfile(this.allowLocalInfile)
.useCompression(this.useCompression)
.useAffectedRows(this.useAffectedRows)
.useBulkStmts(this.useBulkStmts)
.useBulkStmtsForInserts(this.useBulkStmtsForInserts)
.disablePipeline(this.disablePipeline)
.cachePrepStmts(this.cachePrepStmts)
.prepStmtCacheSize(this.prepStmtCacheSize)
.useServerPrepStmts(this.useServerPrepStmts)
.credentialType(this.credentialType == null ? null : this.credentialType.type())
.sessionVariables(this.sessionVariables)
.connectionAttributes(this.connectionAttributes)
.servicePrincipalName(this.servicePrincipalName)
.blankTableNameMeta(this.blankTableNameMeta)
.disconnectOnExpiredPasswords(this.disconnectOnExpiredPasswords)
.tinyInt1isBit(this.tinyInt1isBit)
.transformedBitIsBoolean(this.transformedBitIsBoolean)
.yearIsDateType(this.yearIsDateType)
.dumpQueriesOnException(this.dumpQueriesOnException)
.includeInnodbStatusInDeadlockExceptions(this.includeInnodbStatusInDeadlockExceptions)
.includeThreadDumpInDeadlockExceptions(this.includeThreadDumpInDeadlockExceptions)
.retriesAllDown(this.retriesAllDown)
.galeraAllowedState(this.galeraAllowedState)
.transactionReplay(this.transactionReplay)
.transactionReplaySize(this.transactionReplaySize)
.pool(this.pool)
.poolName(this.poolName)
.maxPoolSize(this.maxPoolSize)
.minPoolSize(this.minPoolSize)
.maxIdleTime(this.maxIdleTime)
.registerJmxPool(this.registerJmxPool)
.poolValidMinDelay(this.poolValidMinDelay)
.useResetConnection(this.useResetConnection)
.serverRsaPublicKeyFile(this.serverRsaPublicKeyFile)
.allowPublicKeyRetrieval(this.allowPublicKeyRetrieval);
builder._nonMappedOptions = this.nonMappedOptions;
return builder;
}
/**
* Tell if mariadb driver accept url string. (Correspond to interface
* java.jdbc.Driver.acceptsURL() method)
*
* @param url url String
* @return true if url string correspond.
*/
public static boolean acceptsUrl(String url) {
return url != null
&& (url.startsWith("jdbc:mariadb:")
|| (url.startsWith("jdbc:mysql:") && url.contains("permitMysqlScheme")));
}
/**
* parse connection string
*
* @param url connection string
* @return configuration resulting object
* @throws SQLException if not supported driver or wrong connection string format.
*/
public static Configuration parse(final String url) throws SQLException {
return parse(url, new Properties());
}
/**
* Parse url connection string with additional properties.
*
* @param url connection string
* @param prop properties
* @return UrlParser instance
* @throws SQLException if parsing exception occur
*/
public static Configuration parse(final String url, Properties prop) throws SQLException {
if (acceptsUrl(url)) {
return parseInternal(url, (prop == null) ? new Properties() : prop);
}
return null;
}
/**
* Parses the connection URL in order to set the UrlParser instance with all the information
* provided through the URL.
*
* @param url connection URL
* @param properties properties
* @throws SQLException if format is incorrect
*/
private static Configuration parseInternal(String url, Properties properties)
throws SQLException {
try {
Builder builder = new Builder();
// Validate and parse basic URL structure
validateUrlFormat(url);
int separator = url.indexOf("//");
builder.haMode(parseHaMode(url, separator));
// Extract host and parameters sections
String urlSecondPart = url.substring(separator + 2);
// Skip complex address definitions
int posToSkip = skipComplexAddresses(urlSecondPart);
int dbIndex = urlSecondPart.indexOf("/", posToSkip);
int paramIndex = urlSecondPart.indexOf("?");
// parse address and additional parameter parts
String hostAddressesString;
String additionalParameters;
if ((dbIndex < paramIndex && dbIndex < 0) || (dbIndex > paramIndex && paramIndex > -1)) {
hostAddressesString = urlSecondPart.substring(0, paramIndex);
additionalParameters = urlSecondPart.substring(paramIndex);
} else if (dbIndex < paramIndex || dbIndex > paramIndex) {
hostAddressesString = urlSecondPart.substring(0, dbIndex);
additionalParameters = urlSecondPart.substring(dbIndex);
} else {
hostAddressesString = urlSecondPart;
additionalParameters = null;
}
// Process database and parameters if present
if (additionalParameters != null) {
processDatabaseAndParameters(additionalParameters, builder, properties);
} else {
builder.database(null);
}
// Map properties to configuration options
mapPropertiesToOption(builder, properties);
// Parse host addresses
builder._addresses = HostAddress.parse(hostAddressesString, builder._haMode);
return builder.build();
} catch (IllegalArgumentException i) {
throw new SQLException("error parsing url: " + i.getMessage(), i);
}
}
private static void validateUrlFormat(String url) {
int separator = url.indexOf("//");
if (separator == -1) {
throw new IllegalArgumentException(
"url parsing error : '//' is not present in the url " + url);
}
}
private static int skipComplexAddresses(String urlSecondPart) {
int posToSkip = 0;
int skipPos;
while ((skipPos = urlSecondPart.indexOf("address=(", posToSkip)) > -1) {
posToSkip = urlSecondPart.indexOf(")", skipPos) + 1;
while (urlSecondPart.startsWith("(", posToSkip)) {
int endingBraceIndex = urlSecondPart.indexOf(")", posToSkip);
if (endingBraceIndex == -1) break;
posToSkip = endingBraceIndex + 1;
}
}
return posToSkip;
}
private static void processDatabaseAndParameters(
String additionalParameters, Builder builder, Properties properties) {
int optIndex = additionalParameters.indexOf("?");
// Extract database name
String database;
if (optIndex < 0) {
database = (additionalParameters.length() > 1) ? additionalParameters.substring(1) : null;
} else {
database = extractDatabase(additionalParameters, optIndex);
processUrlParameters(additionalParameters.substring(optIndex + 1), properties);
}
builder.database(database);
}
private static String extractDatabase(String additionalParameters, int optIndex) {
if (optIndex == 0) {
return null;
}
String database = additionalParameters.substring(1, optIndex);
return database.isEmpty() ? null : database;
}
private static void processUrlParameters(String urlParameters, Properties properties) {
if (!urlParameters.isEmpty()) {
String[] parameters = urlParameters.split("&");
for (String parameter : parameters) {
int pos = parameter.indexOf('=');
if (pos == -1) {
properties.setProperty(parameter, "");
} else {
properties.setProperty(parameter.substring(0, pos), parameter.substring(pos + 1));
}
}
}
}
private static void mapPropertiesToOption(Builder builder, Properties properties) {
Properties nonMappedOptions = new Properties();
try {
processProperties(builder, properties, nonMappedOptions);
handleLegacySslSettings(builder, nonMappedOptions);
builder._nonMappedOptions = nonMappedOptions;
} catch (ReflectiveOperationException e) {
throw new IllegalArgumentException("Unexpected error while mapping properties", e);
}
}
private static void processProperties(
Builder builder, Properties properties, Properties nonMappedOptions)
throws ReflectiveOperationException {
for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
String realKey = getRealKey(entry.getKey().toString());
final Object propertyValue = entry.getValue();
if (propertyValue != null) {
processProperty(builder, realKey, propertyValue, entry.getKey(), nonMappedOptions);
}
}
}
private static String getRealKey(String key) {
String lowercaseKey = key.toLowerCase(Locale.ROOT);
String realKey = OptionAliases.OPTIONS_ALIASES.get(lowercaseKey);
return realKey != null ? realKey : key;
}
private static void processProperty(
Builder builder,
String realKey,
Object propertyValue,
Object originalKey,
Properties nonMappedOptions)
throws ReflectiveOperationException {
boolean used = false;
for (Field field : Builder.class.getDeclaredFields()) {
if (realKey.toLowerCase(Locale.ROOT).equals(field.getName().toLowerCase(Locale.ROOT))) {
used = true;
setFieldValue(builder, field, propertyValue, originalKey);
}
}
if (!used) {
nonMappedOptions.put(realKey, propertyValue);
}
}
private static void setFieldValue(
Builder builder, Field field, Object propertyValue, Object originalKey)
throws ReflectiveOperationException {
if (field.getGenericType().equals(String.class)) {
handleStringField(builder, field, propertyValue);
} else if (field.getGenericType().equals(Boolean.class)) {
handleBooleanField(builder, field, propertyValue, originalKey);
} else if (field.getGenericType().equals(Integer.class)) {
handleIntegerField(builder, field, propertyValue, originalKey);
}
}
private static void handleStringField(Builder builder, Field field, Object value)
throws ReflectiveOperationException {
String stringValue = value.toString();
if (!stringValue.isEmpty()) {
Method method = Builder.class.getDeclaredMethod(field.getName(), String.class);
method.invoke(builder, stringValue);
}
}
private static void handleBooleanField(
Builder builder, Field field, Object value, Object originalKey)
throws ReflectiveOperationException {
Method method = Builder.class.getDeclaredMethod(field.getName(), Boolean.class);
switch (value.toString().toLowerCase()) {
case "":
case "1":
case "true":
method.invoke(builder, Boolean.TRUE);
break;
case "0":
case "false":
method.invoke(builder, Boolean.FALSE);
break;
default:
throw new IllegalArgumentException(
String.format(
"Optional parameter %s must be boolean (true/false or 0/1) was '%s'",
originalKey, value));
}
}
private static void handleIntegerField(
Builder builder, Field field, Object value, Object originalKey)
throws ReflectiveOperationException {
try {
Method method = Builder.class.getDeclaredMethod(field.getName(), Integer.class);
final Integer intValue = Integer.parseInt(value.toString());
method.invoke(builder, intValue);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
String.format("Optional parameter %s must be Integer, was '%s'", originalKey, value));
}
}
private static void handleLegacySslSettings(Builder builder, Properties nonMappedOptions) {
if (isSet("useSsl", nonMappedOptions) || isSet("useSSL", nonMappedOptions)) {
Properties deprecatedDesc = new Properties();
try (InputStream inputStream =
Driver.class.getClassLoader().getResourceAsStream("deprecated.properties")) {
deprecatedDesc.load(inputStream);
logger.warn(deprecatedDesc.getProperty("useSsl"));
if (isSet("trustServerCertificate", nonMappedOptions)) {
builder.sslMode("trust");
logger.warn(deprecatedDesc.getProperty("trustServerCertificate"));
} else if (isSet("disableSslHostnameVerification", nonMappedOptions)) {
logger.warn(deprecatedDesc.getProperty("disableSslHostnameVerification"));
builder.sslMode("verify-ca");
} else {
builder.sslMode("verify-full");
}
} catch (IOException e) {
// Ignore IO exceptions when loading deprecation messages
}
}
}
private static boolean isSet(String key, Properties nonMappedOptions) {
String value = nonMappedOptions.getProperty(key);
return value != null && (value.equals("1") || value.equals("true") || value.isEmpty());
}
private static HaMode parseHaMode(String url, int separator) {
// parser is sure to have at least 2 colon, since jdbc:[mysql|mariadb]: is tested.
int firstColonPos = url.indexOf(':');
int secondColonPos = url.indexOf(':', firstColonPos + 1);
int thirdColonPos = url.indexOf(':', secondColonPos + 1);
if (thirdColonPos > separator || thirdColonPos == -1) {
if (secondColonPos == separator - 1) {
return HaMode.NONE;
}
thirdColonPos = separator;
}
try {
String haModeString = url.substring(secondColonPos + 1, thirdColonPos);
if ("FAILOVER".equalsIgnoreCase(haModeString)) {
haModeString = "LOADBALANCE";
}
return HaMode.from(haModeString);
} catch (IllegalArgumentException i) {
throw new IllegalArgumentException(
"wrong failover parameter format in connection String " + url);
}
}
/**
* Permit to have string information on how string is parsed. example :
* Configuration.toConf("jdbc:mariadb://localhost/test") will return a String containing: <code>
* Configuration:
* * resulting Url : jdbc:mariadb://localhost/test
* Unknown options : None
*
* Non default options :
* * database : test
*
* default options :
* * user : null
* ...
* </code>
*
* @param url url string
* @return string describing the configuration parsed from url
* @throws SQLException if parsing fails
*/
public static String toConf(String url) throws SQLException {
Configuration conf = Configuration.parseInternal(url, new Properties());
Configuration defaultConf = Configuration.parse("jdbc:mariadb://localhost/");
StringBuilder result = new StringBuilder();
appendBasicConfiguration(result, conf);
appendUnknownOptions(result, conf);
appendNonDefaultOptions(result, conf, defaultConf);
appendDefaultOptions(result, conf, defaultConf);
return result.toString();
}
private static void appendBasicConfiguration(StringBuilder sb, Configuration conf) {
sb.append("Configuration:\n * resulting Url : ").append(conf.initialUrl);
}
private static void appendUnknownOptions(StringBuilder sb, Configuration conf) {
sb.append("\nUnknown options : ");
if (conf.nonMappedOptions.isEmpty()) {
sb.append("None\n");
return;
}
conf.nonMappedOptions.entrySet().stream()
.map(
entry ->
new AbstractMap.SimpleEntry<>(
entry.getKey().toString(),
entry.getValue() != null ? entry.getValue().toString() : ""))
.sorted(Map.Entry.comparingByKey())
.forEach(
entry ->
sb.append("\n * ").append(entry.getKey()).append(" : ").append(entry.getValue()));
sb.append("\n");
}
private static void appendNonDefaultOptions(
StringBuilder sb, Configuration conf, Configuration defaultConf) {
try {
StringBuilder diffOpts = new StringBuilder();
processFields(conf, defaultConf, new StringBuilder(), diffOpts);
sb.append("\nNon default options : ");
if (diffOpts.length() == 0) {
sb.append("None\n");
} else {
sb.append(diffOpts);
}
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Error processing non-default options", e);
}
}
private static void appendDefaultOptions(
StringBuilder sb, Configuration conf, Configuration defaultConf) {
try {
StringBuilder defaultOpts = new StringBuilder();
processFields(conf, defaultConf, defaultOpts, new StringBuilder());
sb.append("\n\ndefault options :");
if (defaultOpts.length() == 0) {
sb.append("None\n");
} else {
sb.append(defaultOpts);
}
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Error processing default options", e);
}
}
private static void processFields(
Configuration conf,
Configuration defaultConf,
StringBuilder defaultOpts,
StringBuilder diffOpts)
throws IllegalAccessException {
Field[] fields = Configuration.class.getDeclaredFields();
Arrays.sort(fields, Comparator.comparing(Field::getName));
for (Field field : fields) {
if (PROPERTIES_TO_SKIP.contains(field.getName())) {
continue;
}
Object fieldValue = field.get(conf);
Object defaultValue = field.get(defaultConf);
processField(field, fieldValue, defaultValue, defaultOpts, diffOpts);
}
}
private static void processField(
Field field,
Object fieldValue,
Object defaultValue,
StringBuilder defaultOpts,
StringBuilder diffOpts) {
if (fieldValue == null) {
appendNullField(field, defaultValue, defaultOpts, diffOpts);
return;
}
if (field.getName().equals("haMode")) {
appendHaModeField(field, fieldValue, defaultValue, defaultOpts, diffOpts);
return;
}
String typeName = fieldValue.getClass().getSimpleName();
switch (typeName) {
case "String":
case "Boolean":
case "HaMode":
case "TransactionIsolation":
case "MetaExportedKeys":
case "Integer":
case "SslMode":
case "CatalogTerm":
appendSimpleField(field, fieldValue, defaultValue, defaultOpts, diffOpts);
break;
case "ArrayList":
appendListField(field, fieldValue, defaultValue, defaultOpts, diffOpts);
break;
case "Properties":
case "HashSet":
break;
default:
throw new IllegalArgumentException("Unexpected field type for: " + field.getName());
}
}
private static void appendNullField(
Field field, Object defaultValue, StringBuilder defaultOpts, StringBuilder diffOpts) {
StringBuilder target = defaultValue == null ? defaultOpts : diffOpts;
target.append("\n * ").append(field.getName()).append(" : null");
}
private static void appendHaModeField(
Field field,
Object fieldValue,
Object defaultValue,
StringBuilder defaultOpts,
StringBuilder diffOpts) {
StringBuilder target = Objects.equals(fieldValue, defaultValue) ? defaultOpts : diffOpts;
target.append("\n * ").append(field.getName()).append(" : ").append(fieldValue);
}
private static void appendSimpleField(
Field field,
Object fieldValue,
Object defaultValue,
StringBuilder defaultOpts,
StringBuilder diffOpts) {
StringBuilder target = Objects.equals(fieldValue, defaultValue) ? defaultOpts : diffOpts;
target.append("\n * ").append(field.getName()).append(" : ");
if (SENSITIVE_FIELDS.contains(field.getName())) {
target.append("***");
} else {
target.append(fieldValue);
}
}
private static void appendListField(
Field field,
Object fieldValue,
Object defaultValue,
StringBuilder defaultOpts,
StringBuilder diffOpts) {
StringBuilder target =
Objects.equals(fieldValue.toString(), defaultValue.toString()) ? defaultOpts : diffOpts;
target.append("\n * ").append(field.getName()).append(" : ").append(fieldValue);
}
/**
* Builds a JDBC URL from the provided configuration.
*
* @param conf Current configuration
* @return Complete JDBC URL string
*/
protected static String buildUrl(Configuration conf) {
try {
StringBuilder urlBuilder = new StringBuilder("jdbc:mariadb:");
appendHaModeIfPresent(urlBuilder, conf);
appendHostAddresses(urlBuilder, conf);
appendDatabase(urlBuilder, conf);
appendConfigurationParameters(urlBuilder, conf);
conf.loadCodecs();
return urlBuilder.toString();
} catch (SecurityException s) {
throw new IllegalArgumentException("Security too restrictive: " + s.getMessage());
}
}
private static void appendHostAddresses(StringBuilder sb, Configuration conf) {
sb.append("//");
for (int i = 0; i < conf.addresses.size(); i++) {
if (i > 0) sb.append(",");
appendHostAddress(sb, conf, conf.addresses.get(i), i);
}
sb.append("/");
}
private static void appendHostAddress(
StringBuilder sb, Configuration conf, HostAddress hostAddress, int index) {
boolean useSimpleFormat = shouldUseSimpleHostFormat(conf, hostAddress, index);
if (useSimpleFormat) {
sb.append(hostAddress.host);
if (hostAddress.port != 3306) {
sb.append(":").append(hostAddress.port);
}
} else {
sb.append(hostAddress);
}
}
private static boolean shouldUseSimpleHostFormat(
Configuration conf, HostAddress hostAddress, int index) {
return (conf.haMode == HaMode.NONE && hostAddress.primary)
|| (conf.haMode == HaMode.REPLICATION
&& ((index == 0 && hostAddress.primary) || (index != 0 && !hostAddress.primary)));
}
private static void appendDatabase(StringBuilder sb, Configuration conf) {
if (conf.database != null) {
sb.append(conf.database);
}
}
private static void appendHaModeIfPresent(StringBuilder sb, Configuration conf) {
if (conf.haMode != HaMode.NONE) {
sb.append(conf.haMode.toString().toLowerCase(Locale.ROOT).replace("_", "-")).append(":");
}
}
private static void appendConfigurationParameters(StringBuilder sb, Configuration conf) {
try {
Configuration defaultConf = new Configuration(new Builder());
ParameterAppender paramAppender = new ParameterAppender(sb);
for (Field field : Configuration.class.getDeclaredFields()) {
if (EXCLUDED_FIELDS.contains(field.getName())) {
continue;
}
Object value = field.get(conf);
if (value == null || (value instanceof Properties && ((Properties) value).isEmpty())) {
continue;
}
appendFieldParameter(paramAppender, field, value, defaultConf);
}
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private static void appendFieldParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf)
throws IllegalAccessException {
if (SECURE_FIELDS.contains(field.getName())) {
appender.appendParameter(field.getName(), "***");
return;
}
Class<?> fieldType = field.getType();
if (fieldType.equals(String.class)) {
appendStringParameter(appender, field, value, defaultConf);
} else if (fieldType.equals(boolean.class)) {
appendBooleanParameter(appender, field, value, defaultConf);
} else if (fieldType.equals(int.class)) {
appendIntParameter(appender, field, value, defaultConf);
} else if (fieldType.equals(Properties.class)) {
appendPropertiesParameter(appender, (Properties) value);
} else if (fieldType.equals(CatalogTerm.class)) {
appendCatalogTermParameter(appender, field, value, defaultConf);
} else if (fieldType.equals(CredentialPlugin.class)) {
appendCredentialPluginParameter(appender, field, value, defaultConf);
} else {
appendDefaultParameter(appender, field, value, defaultConf);
}
}
private static void appendStringParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf)
throws IllegalAccessException {
String defaultValue = (String) field.get(defaultConf);
if (!value.equals(defaultValue)) {
appender.appendParameter(field.getName(), (String) value);
}
}
private static void appendBooleanParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf)
throws IllegalAccessException {
boolean defaultValue = field.getBoolean(defaultConf);
if (!value.equals(defaultValue)) {
appender.appendParameter(field.getName(), value.toString());
}
}
private static void appendIntParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf) {
try {
int defaultValue = field.getInt(defaultConf);
if (!value.equals(defaultValue)) {
appender.appendParameter(field.getName(), value.toString());
}
} catch (IllegalAccessException e) {
// Ignore access errors for int fields
}
}
private static void appendPropertiesParameter(ParameterAppender appender, Properties props) {
for (Object key : props.keySet()) {
appender.appendParameter(key.toString(), props.get(key).toString());
}
}
private static void appendCatalogTermParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf)
throws IllegalAccessException {
Object defaultValue = field.get(defaultConf);
if (!value.equals(defaultValue)) {
appender.appendParameter(field.getName(), SCHEMA_TERM);
}
}
private static void appendCredentialPluginParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf)
throws IllegalAccessException {
Object defaultValue = field.get(defaultConf);
if (!value.equals(defaultValue)) {
appender.appendParameter(field.getName(), ((CredentialPlugin) value).type());
}
}
private static void appendDefaultParameter(
ParameterAppender appender, Field field, Object value, Configuration defaultConf)
throws IllegalAccessException {
Object defaultValue = field.get(defaultConf);
if (!value.equals(defaultValue)) {
appender.appendParameter(field.getName(), value.toString());
}
}
private static class ParameterAppender {
private final StringBuilder sb;
private boolean first = true;
ParameterAppender(StringBuilder sb) {
this.sb = sb;
}
void appendParameter(String name, String value) {
sb.append(first ? '?' : '&').append(name).append('=').append(value);
first = false;
}
}
private static String nullOrEmpty(String val) {
return (val == null || val.isEmpty()) ? null : val;
}
/**
* Clone configuration with another user/password
*
* @param username new username
* @param password new password
* @return new cloned configuration object
*/
public Configuration clone(String username, String password) {
return this.toBuilder()
.user(username != null && username.isEmpty() ? null : username)
.password(password != null && password.isEmpty() ? null : password)
.build();
}
public boolean havePrimaryHostOnly() {
for (HostAddress hostAddress : this.addresses) {
if (!hostAddress.primary) return false;
}
return true;
}
/**
* Connection default database
*
* @return database
*/
public String database() {
return database;
}
/**
* addresses
*
* @return addresses
*/
public List<HostAddress> addresses() {
return addresses;
}
/**
* High availability mode
*
* @return configuration HA mode
*/
public HaMode haMode() {
return haMode;
}
/**
* credential plugin to use
*
* @return credential plugin to use, null of none
*/
public CredentialPlugin credentialPlugin() {
return credentialType;
}
/**
* configuration user
*
* @return user
*/
public String user() {
return user;
}
/**
* configuration password
*
* @return password
*/
public String password() {
return password;
}
/**
* Configuration generated URL depending on current configuration option. Password will be hidden
* by "***"
*
* @return generated url
*/
public String initialUrl() {
return initialUrl;
}
/**
* server ssl certificate (file path / certificat content)
*
* @return server ssl certificate
*/
public String serverSslCert() {
return serverSslCert;
}
/**
* key store
*
* @return key store
*/
public String keyStore() {
return keyStore;
}
/**
* trust store
*
* @return trust store
*/
public String trustStore() {
return trustStore;
}
/**
* key store password
*
* @return key store password
*/
public String keyStorePassword() {
return keyStorePassword;
}
/**
* trust store password
*
* @return trust store password
*/
public String trustStorePassword() {
return trustStorePassword;
}
/**
* key store alias password
*
* @return key store alias password
*/
public String keyPassword() {
return keyPassword;
}
/**
* key store type (to replace default javax.net.ssl.keyStoreType system property)
*
* @return key store type
*/
public String keyStoreType() {
return keyStoreType;
}
/**
* trust store type (to replace default javax.net.ssl.keyStoreType system property)
*
* @return trust store type
*/
public String trustStoreType() {
return trustStoreType;
}
/**
* permitted ssl protocol list (comma separated)
*
* @return enabled ssl protocol list
*/
public String enabledSslProtocolSuites() {
return enabledSslProtocolSuites;
}
// do not remove, used with reflection
public String credentialType() {
return credentialType == null ? null : credentialType.type();
}
/**
* Indicate if keyStore option is not set to use keystore system property like
* "javax.net.ssl.keyStore"
*
* @return true if can use keystore system property
*/
public boolean fallbackToSystemKeyStore() {
return fallbackToSystemKeyStore;
}
/**
* Indicate if system default truststore implementation can be used
*
* @return true if system default truststore implementation can be used
*/
public boolean fallbackToSystemTrustStore() {
return fallbackToSystemTrustStore;
}
/**
* Socket factory class name
*
* @return socket factory
*/
public String socketFactory() {
return socketFactory;
}
/**
* socket connect timeout
*
* @return connect timeout
*/
public int connectTimeout() {
return connectTimeout;
}
/**
* Set connect timeout
*
* @param connectTimeout timeout value
* @return current configuration
*/
public Configuration connectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
/**
* Pipe path
*
* @return pipe value
*/
public String pipe() {
return pipe;
}
/**
* local socket configuration
*
* @return local socket path
*/
public String localSocket() {
return localSocket;
}
/**
* socket tcp keep alive
*
* @return socket tcp keep alive value
*/
public boolean tcpKeepAlive() {
return tcpKeepAlive;
}
/**
* must uuid fields return as String and not java.util.UUID
*
* @return must UUID return as String and not uuid
*/
public boolean uuidAsString() {
return uuidAsString;
}
/**
* socket tcp keep idle (java 11+ only)
*
* @return socket tcp keep idle
*/
public int tcpKeepIdle() {
return tcpKeepIdle;
}
/**
* socket tcp keep count (java 11+ only)
*
* @return socket tcp keep count
*/
public int tcpKeepCount() {
return tcpKeepCount;
}
/**
* socket tcp keep interval (java 11+ only)
*
* @return socket tcp keep interval
*/
public int tcpKeepInterval() {
return tcpKeepInterval;
}
/**
* close using TCP abortive close (RST TCP packet, in place or FIN packet)
*
* @return close using TCP abortive close
*/
public boolean tcpAbortiveClose() {
return tcpAbortiveClose;
}
/**
* local socket address path
*
* @return local socket address
*/
public String localSocketAddress() {
return localSocketAddress;
}
/**
* socket timeout
*
* @return socket timeout
*/
public int socketTimeout() {
return socketTimeout;
}
/**
* permit using multi queries command
*
* @return permit using multi queries command
*/
public boolean allowMultiQueries() {
return allowMultiQueries;
}
/**
* permits LOAD LOCAL INFILE commands
*
* @return allow LOAD LOCAL INFILE
*/
public boolean allowLocalInfile() {
return allowLocalInfile;
}
/**
* Enable compression if server has compression capability
*
* @return use compression
*/
public boolean useCompression() {
return useCompression;
}
/**
* force returning blank table metadata (for old oracle compatibility)
*
* @return metadata table return blank
*/
public boolean blankTableNameMeta() {
return blankTableNameMeta;
}
/**
* On connection creation, indicate behavior when password is expired. When true (default) throw
* an expired password error When false, connection succeed in "sandbox" mode, only queries
* related to password change are allowed
*
* @return must connection fails on expired password
*/
public boolean disconnectOnExpiredPasswords() {
return disconnectOnExpiredPasswords;
}
/**
* SSl mode
*
* @return ssl mode
*/
public SslMode sslMode() {
return sslMode;
}
/**
* Default transaction isolation
*
* @return default transaction isolation.
*/
public TransactionIsolation transactionIsolation() {
return transactionIsolation;
}
/**
* Default metadata getExportedKeys implementation.
*
* @return default implementation
*/
public MetaExportedKeys metaExportedKeys() {
return metaExportedKeys;
}
/**
* autorized cipher list.
*
* @return list of permitted ciphers
*/
public String enabledSslCipherSuites() {
return enabledSslCipherSuites;
}
/**
* coma separated Session variable list
*
* @return session variable
*/
public String sessionVariables() {
return sessionVariables;
}
/**
* Must tinyint(1) be considered as Bit
*
* @return true if tinyint(1) must be considered as Bit
*/
public boolean tinyInt1isBit() {
return tinyInt1isBit;
}
/**
* Must tinyint(1) be considered as Boolean or Bit
*
* @return true if tinyint(1) must be considered as Boolean
*/
public boolean transformedBitIsBoolean() {
return transformedBitIsBoolean;
}
/**
* Must year be return by default as Date in result-set
*
* @return year is Date type
*/
public boolean yearIsDateType() {
return yearIsDateType;
}
/**
* Set timezone
*
* @return timezone
*/
public String timezone() {
return timezone;
}
/**
* Set connectionTimeZone
*
* @return connectionTimeZone
*/
public String connectionTimeZone() {
return connectionTimeZone;
}
/**
* get connectionCollation
*
* @return connectionCollation
*/
public String connectionCollation() {
return connectionCollation;
}
/**
* forceConnectionTimeZoneToSession must connection timezone be forced
*
* @return forceConnectionTimeZoneToSession
*/
public Boolean forceConnectionTimeZoneToSession() {
return forceConnectionTimeZoneToSession;
}
/**
* Must timezone change preserve instants
*
* @return true if instants must be preserved
*/
public boolean preserveInstants() {
return preserveInstants;
}
/**
* Must query by logged on exception.
*
* @return dump queries on exception
*/
public boolean dumpQueriesOnException() {
return dumpQueriesOnException;
}
/**
* Prepare statement cache size.
*
* @return Prepare statement cache size
*/
public int prepStmtCacheSize() {
return prepStmtCacheSize;
}
/**
* Use affected row
*
* @return use affected rows
*/
public boolean useAffectedRows() {
return useAffectedRows;
}
/**
* Use server prepared statement. IF false, using client prepared statement.
*
* @return use server prepared statement
*/
public boolean useServerPrepStmts() {
return useServerPrepStmts;
}
/**
* Connections attributes
*
* @return connection meta informations
*/
public String connectionAttributes() {
return connectionAttributes;
}
/**
* Use server COM_STMT_BULK for batching.
*
* @return use server bulk command.
*/
public boolean useBulkStmts() {
return useBulkStmts;
}
/**
* Use server COM_STMT_BULK for batching inserts. if useBulkStmts is enabled,
* useBulkStmtsForInserts will be as well
*
* @return use server bulk command for inserts
*/
public boolean useBulkStmtsForInserts() {
return useBulkStmtsForInserts;
}
/**
* Disable pipeline.
*
* @return is pipeline disabled.
*/
public boolean disablePipeline() {
return disablePipeline;
}
/**
* Force session autocommit on connection creation
*
* @return autocommit forced value
*/
public Boolean autocommit() {
return autocommit;
}
/**
* Force returning MySQL metadata information
*
* @return force returning MySQL in metadata
*/
public boolean useMysqlMetadata() {
return useMysqlMetadata;
}
/**
* When enable, in DatabaseMetadata, will handle null database/schema (depending on
* UseCatalog=catalog/schema) as current
*
* @return must null value be considered as current catalog/schema
*/
public boolean nullDatabaseMeansCurrent() {
return nullDatabaseMeansCurrent;
}
/**
* Indicating using Catalog or Schema
*
* @return Indicating using Catalog or Schema
*/
public CatalogTerm useCatalogTerm() {
return useCatalogTerm;
}
/**
* create database if not exist
*
* @return create database if not exist
*/
public boolean createDatabaseIfNotExist() {
return createDatabaseIfNotExist;
}
/**
* use local state to avoid unnecessary queries. This means application must use JDBC dedicated
* methods, like connection.setTransactionIsolation and never queries like "SET SESSION
* TRANSACTION ISOLATION LEVEL X" directly
*
* @return can use local state
*/
public boolean useLocalSessionState() {
return useLocalSessionState;
}
/**
* Returns multi-values generated ids. For mariadb 2.x connector compatibility
*
* @return must returns multi-values generated ids.
*/
public boolean returnMultiValuesGeneratedIds() {
return returnMultiValuesGeneratedIds;
}
/**
* Force sql_mode to strict mode for JDBC compliance
*
* @return must force jdbc compliance
*/
public boolean jdbcCompliantTruncation() {
return jdbcCompliantTruncation;
}
/**
* Force Timestamp string representation compatible to 2.7 version Timestamp string representation
* will then correspond to Timestamp.toString() in place of taking field precision
*
* @return force 2.7 timestamp to string behavior
*/
public boolean oldModeNoPrecisionTimestamp() {
return oldModeNoPrecisionTimestamp;
}
/**
* must client redirect when required
*
* @return must client redirect when required
*/
public Boolean permitRedirect() {
return permitRedirect;
}
/**
* When enabled, ensure that for XA operation to use the same connection
*
* @return pinGlobalTxToPhysicalConnection
*/
public boolean pinGlobalTxToPhysicalConnection() {
return pinGlobalTxToPhysicalConnection;
}
/**
* Indicate if Statement/PreparedStatement.executeQuery for command that produce no result will
* return an exception or just an empty result-set
*
* <p>When enabled, command not returning no data will end returning an empty result-set When
* disabled, command not returning no data will end throwing an exception
*
* @return permitNoResults
*/
public boolean permitNoResults() {
return permitNoResults;
}
/**
* On deadlock exception, must driver execute additional commands to show innodb status in error
* description.
*
* @return includeInnodbStatusInDeadlockExceptions
*/
public boolean includeInnodbStatusInDeadlockExceptions() {
return includeInnodbStatusInDeadlockExceptions;
}
/**
* On deadlock exception, must driver display threads information on error description.
*
* @return include Thread Dump In Deadlock Exceptions
*/
public boolean includeThreadDumpInDeadlockExceptions() {
return includeThreadDumpInDeadlockExceptions;
}
/**
* Service principal name (GSSAPI option)
*
* @return service principal name
*/
public String servicePrincipalName() {
return servicePrincipalName;
}
/**
* result-set streaming default fetch size
*
* @return Default fetch size.
*/
public int defaultFetchSize() {
return defaultFetchSize;
}
/**
* non standard options
*
* @return non standard options
*/
public Properties nonMappedOptions() {
return nonMappedOptions;
}
/**
* TLS socket type
*
* @return TLS socket type
*/
public String tlsSocketType() {
return tlsSocketType;
}
/**
* query maximum size to log (query will be truncated of more than this limit)
*
* @return max query log size
*/
public int maxQuerySizeToLog() {
return maxQuerySizeToLog;
}
/**
* max_allowed_packet value to avoid sending packet with non supported size, droping the
* connection without reason.
*
* @return max_allowed_packet value
*/
public Integer maxAllowedPacket() {
return maxAllowedPacket;
}
/**
* retry the maximum retry number of attempts to reconnect after a failover.
*
* @return the maximum retry number of attempts to reconnect after a failover.
*/
public int retriesAllDown() {
return retriesAllDown;
}
/**
* Galera comma separated allowed state
*
* @return galera allowed state
*/
public String galeraAllowedState() {
return galeraAllowedState;
}
/**
* Create pool
*
* @return create pool if don't exists
*/
public boolean pool() {
return pool;
}
/**
* pool name
*
* @return pool name.
*/
public String poolName() {
return poolName;
}
/**
* max pool size
*
* @return maximum pool size
*/
public int maxPoolSize() {
return maxPoolSize;
}
/**
* Minimum pool size
*
* @return minimum pool size
*/
public int minPoolSize() {
return minPoolSize;
}
/**
* Max idle time
*
* @return pool max idle time.
*/
public int maxIdleTime() {
return maxIdleTime;
}
/**
* register pool information to JMX
*
* @return register pool to JMX
*/
public boolean registerJmxPool() {
return registerJmxPool;
}
/**
* Pool mininum validation delay.
*
* @return pool validation delay
*/
public int poolValidMinDelay() {
return poolValidMinDelay;
}
/**
* Must connection returned to pool be RESET
*
* @return use RESET on connection
*/
public boolean useResetConnection() {
return useResetConnection;
}
/**
* Server RSA public key file for caching_sha2_password authentication
*
* @return server key file
*/
public String serverRsaPublicKeyFile() {
return serverRsaPublicKeyFile;
}
/**
* permit mysql authentication to retrieve server certificate
*
* @return is driver allowed to retrieve server certificate from server
*/
public boolean allowPublicKeyRetrieval() {
return allowPublicKeyRetrieval;
}
/**
* Read all data from socket in advance
*
* @return use read ahead buffer implementation
*/
public boolean useReadAheadInput() {
return useReadAheadInput;
}
/**
* Cache prepared statement result.
*
* @return cache prepare results
*/
public boolean cachePrepStmts() {
return cachePrepStmts;
}
/**
* implements transaction replay failover
*
* @return true if transaction must be replayed on failover.
*/
public boolean transactionReplay() {
return transactionReplay;
}
/**
* transaction replay maximum number of saved command.
*
* @return transaction replay buffer size.
*/
public int transactionReplaySize() {
return transactionReplaySize;
}
/**
* geometry default decoding implementation
*
* @return geometry default type
*/
public String geometryDefaultType() {
return geometryDefaultType;
}
/**
* Restrict authentication plugin to comma separated plugin list
*
* @return authorized authentication list
*/
public String restrictedAuth() {
return restrictedAuth;
}
/**
* Execute initial command when connection is established
*
* @return initial SQL command
*/
public String initSql() {
return initSql;
}
/**
* datatype Encoder/decoder list
*
* @return codec list
*/
public Codec<?>[] codecs() {
return codecs;
}
/**
* ToString implementation.
*
* @return String value
*/
public String toString() {
return initialUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Configuration that = (Configuration) o;
if (password == null) {
return initialUrl.equals(that.initialUrl) && that.password == null;
}
return initialUrl.equals(that.initialUrl) && password.equals(that.password);
}
@SuppressWarnings("rawtypes")
private void loadCodecs() {
if (cacheCodecs && cachedCodecs != null) {
codecs = cachedCodecs;
return;
}
ServiceLoader<Codec> loader =
ServiceLoader.load(Codec.class, Configuration.class.getClassLoader());
List<Codec<?>> result = new ArrayList<>();
loader.iterator().forEachRemaining(result::add);
codecs = result.toArray(new Codec<?>[0]);
if (cacheCodecs) {
synchronized (Configuration.class) {
if (cachedCodecs == null) {
cachedCodecs = codecs;
}
}
}
}
@Override
public int hashCode() {
return initialUrl.hashCode();
}
/** A builder for {@link Configuration} instances. */
public static final class Builder implements Cloneable {
private Properties _nonMappedOptions;
private HaMode _haMode;
private List<HostAddress> _addresses = new ArrayList<>();
// standard options
private String user;
private String password;
private String database;
// various
private String timezone;
private String connectionTimeZone;
private String connectionCollation;
private Boolean forceConnectionTimeZoneToSession;
private Boolean preserveInstants;
private Boolean autocommit;
private Boolean useMysqlMetadata;
private Boolean nullDatabaseMeansCurrent;
private String useCatalogTerm;
private Boolean createDatabaseIfNotExist;
private Boolean useLocalSessionState;
private Boolean returnMultiValuesGeneratedIds;
private Boolean jdbcCompliantTruncation;
private Boolean oldModeNoPrecisionTimestamp;
private Boolean permitRedirect;
private Boolean pinGlobalTxToPhysicalConnection;
private Boolean permitNoResults;
private Boolean cacheCodecs;
private Integer defaultFetchSize;
private Integer maxQuerySizeToLog;
private Integer maxAllowedPacket;
private String geometryDefaultType;
private String restrictedAuth;
private String initSql;
private String transactionIsolation;
private String metaExportedKeys;
// socket
private String socketFactory;
private Integer connectTimeout;
private String pipe;
private String localSocket;
private Boolean tcpKeepAlive;
private Boolean uuidAsString;
private Integer tcpKeepIdle;
private Integer tcpKeepCount;
private Integer tcpKeepInterval;
private Boolean tcpAbortiveClose;
private String localSocketAddress;
private Integer socketTimeout;
private Boolean useReadAheadInput;
private String tlsSocketType;
// SSL
private String sslMode;
private String serverSslCert;
private String keyStore;
private String trustStore;
private String keyStorePassword;
private String trustStorePassword;
private String keyPassword;
private String keyStoreType;
private String trustStoreType;
private String enabledSslCipherSuites;
private String enabledSslProtocolSuites;
private Boolean fallbackToSystemKeyStore;
private Boolean fallbackToSystemTrustStore;
// protocol
private Boolean allowMultiQueries;
private Boolean allowLocalInfile;
private Boolean useCompression;
private Boolean useAffectedRows;
private Boolean useBulkStmts;
private Boolean useBulkStmtsForInserts;
private Boolean disablePipeline;
// prepare
private Boolean cachePrepStmts;
private Integer prepStmtCacheSize;
private Boolean useServerPrepStmts;
// authentication
private String credentialType;
private String sessionVariables;
private String connectionAttributes;
private String servicePrincipalName;
private Boolean disconnectOnExpiredPasswords;
// meta
private Boolean blankTableNameMeta;
private Boolean tinyInt1isBit;
private Boolean transformedBitIsBoolean;
private Boolean yearIsDateType;
private Boolean dumpQueriesOnException;
private Boolean includeInnodbStatusInDeadlockExceptions;
private Boolean includeThreadDumpInDeadlockExceptions;
// HA options
private Integer retriesAllDown;
private String galeraAllowedState;
private Boolean transactionReplay;
private Integer transactionReplaySize;
// Pool options
private Boolean pool;
private String poolName;
private Integer maxPoolSize;
private Integer minPoolSize;
private Integer maxIdleTime;
private Boolean registerJmxPool;
private Integer poolValidMinDelay;
private Boolean useResetConnection;
// MySQL sha authentication
private String serverRsaPublicKeyFile;
private Boolean allowPublicKeyRetrieval;
/**
* set user to authenticate to server
*
* @param user user
* @return this {@link Builder}
*/
public Builder user(String user) {
this.user = nullOrEmpty(user);
return this;
}
/**
* Server SSL certificate (path or file content)
*
* @param serverSslCert set Server SSL certificate (path or file content)
* @return this {@link Builder}
*/
public Builder serverSslCert(String serverSslCert) {
this.serverSslCert = nullOrEmpty(serverSslCert);
return this;
}
/**
* File path of the keyStore file that contain client private key store and associate
* certificates (similar to java System property \"javax.net.ssl.keyStore\", but ensure that
* only the private key's entries are used)
*
* @param keyStore client store certificates
* @return this {@link Builder}
*/
public Builder keyStore(String keyStore) {
this.keyStore = nullOrEmpty(keyStore);
return this;
}
/**
* File path of the trustStore file that contain trusted certificates (similar to java System
* property \"javax.net.ssl.trustStore\")
*
* @param trustStore client trust store certificates
* @return this {@link Builder}
*/
public Builder trustStore(String trustStore) {
this.trustStore = nullOrEmpty(trustStore);
return this;
}
/**
* Client keystore password
*
* @param keyStorePassword client store password
* @return this {@link Builder}
*/
public Builder keyStorePassword(String keyStorePassword) {
this.keyStorePassword = nullOrEmpty(keyStorePassword);
return this;
}
/**
* Client truststore password
*
* @param trustStorePassword client truststore password
* @return this {@link Builder}
*/
public Builder trustStorePassword(String trustStorePassword) {
this.trustStorePassword = nullOrEmpty(trustStorePassword);
return this;
}
/**
* Client keystore alias password
*
* @param keyPassword client store alias password
* @return this {@link Builder}
*/
public Builder keyPassword(String keyPassword) {
this.keyPassword = nullOrEmpty(keyPassword);
return this;
}
/**
* Key store type
*
* @param keyStoreType key store type
* @return this {@link Builder}
*/
public Builder keyStoreType(String keyStoreType) {
this.keyStoreType = nullOrEmpty(keyStoreType);
return this;
}
/**
* trust store type
*
* @param trustStoreType trust store type
* @return this {@link Builder}
*/
public Builder trustStoreType(String trustStoreType) {
this.trustStoreType = nullOrEmpty(trustStoreType);
return this;
}
/**
* User password
*
* @param password password
* @return this {@link Builder}
*/
public Builder password(String password) {
this.password = nullOrEmpty(password);
return this;
}
/**
* Set ssl protocol list to user (comma separated)
*
* @param enabledSslProtocolSuites set possible SSL(TLS) protocol to use
* @return this {@link Builder}
*/
public Builder enabledSslProtocolSuites(String enabledSslProtocolSuites) {
this.enabledSslProtocolSuites = nullOrEmpty(enabledSslProtocolSuites);
return this;
}
/**
* Indicate if keystore system properties can be used.
*
* @param fallbackToSystemKeyStore set if keystore system properties can be used.
* @return this {@link Builder}
*/
public Builder fallbackToSystemKeyStore(Boolean fallbackToSystemKeyStore) {
this.fallbackToSystemKeyStore = fallbackToSystemKeyStore;
return this;
}
/**
* Indicate if system default truststore can be used.
*
* @param fallbackToSystemTrustStore indicate if system default truststore can be used..
* @return this {@link Builder}
*/
public Builder fallbackToSystemTrustStore(Boolean fallbackToSystemTrustStore) {
this.fallbackToSystemTrustStore = fallbackToSystemTrustStore;
return this;
}
/**
* Set default database
*
* @param database database
* @return this {@link Builder}
*/
public Builder database(String database) {
this.database = database;
return this;
}
/**
* Set failover High-availability mode
*
* @param haMode High-availability mode
* @return this {@link Builder}
*/
public Builder haMode(HaMode haMode) {
this._haMode = haMode;
return this;
}
/**
* Add Host to possible addresses to connect
*
* @param host hostname or IP
* @param port port
* @return this {@link Builder}
*/
public Builder addHost(String host, int port) {
this._addresses.add(HostAddress.from(nullOrEmpty(host), port));
return this;
}
/**
* Add Host to possible addresses to connect
*
* @param host hostname or IP
* @param port port
* @param sslMode ssl mode. possible values disable/trust/verify-ca/verify-full
* @return this {@link Builder}
*/
public Builder addHost(String host, int port, String sslMode) {
this._addresses.add(HostAddress.from(nullOrEmpty(host), port, sslMode));
return this;
}
/**
* Add Host to possible addresses to connect
*
* @param host hostname or IP
* @param port port
* @param master is master or replica
* @return this {@link Builder}
*/
public Builder addHost(String host, int port, boolean master) {
this._addresses.add(HostAddress.from(nullOrEmpty(host), port, master));
return this;
}
/**
* Add Host to possible addresses to connect
*
* @param host hostname or IP
* @param port port
* @param master is master or replica
* @param sslMode ssl mode. possible values disable/trust/verify-ca/verify-full
* @return this {@link Builder}
*/
public Builder addHost(String host, int port, boolean master, String sslMode) {
this._addresses.add(HostAddress.from(nullOrEmpty(host), port, master, sslMode));
return this;
}
/**
* Add a windows pipe host
*
* @param pipe windows pipe path
* @return this {@link Builder}
*/
public Builder addPipeHost(String pipe) {
this._addresses.add(HostAddress.pipe(pipe));
return this;
}
/**
* Add a unix socket host
*
* @param localSocket unix socket path
* @return this {@link Builder}
*/
public Builder addLocalSocketHost(String localSocket) {
this._addresses.add(HostAddress.localSocket(localSocket));
return this;
}
/**
* add host addresses
*
* @param hostAddress host addresses
* @return this {@link Builder}
*/
public Builder addresses(HostAddress... hostAddress) {
this._addresses = new ArrayList<>();
this._addresses.addAll(Arrays.asList(hostAddress));
return this;
}
/**
* add host addresses
*
* @param hostAddress host addresses
* @return this {@link Builder}
*/
public Builder addresses(List<HostAddress> hostAddress) {
this._addresses.addAll(hostAddress);
return this;
}
/**
* Socket factory
*
* @param socketFactory socket factory
* @return this {@link Builder}
*/
public Builder socketFactory(String socketFactory) {
this.socketFactory = socketFactory;
return this;
}
/**
* Indicate connect timeout value, in milliseconds, or zero for no timeout. Default: 30000
*
* @param connectTimeout connect Timeout
* @return this {@link Builder}
*/
public Builder connectTimeout(Integer connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
/**
* Indicate to use windows named pipe, specify named pipe name to connect
*
* @param pipe windows named pipe
* @return this {@link Builder}
*/
public Builder pipe(String pipe) {
this.pipe = nullOrEmpty(pipe);
return this;
}
/**
* Indicate to use Unix domain socket, if the server allows it, specifying named pipe name to
* connect The value is the path of Unix domain socket (available with "select @@socket"
* command).
*
* @param localSocket local socket path
* @return this {@link Builder}
*/
public Builder localSocket(String localSocket) {
this.localSocket = nullOrEmpty(localSocket);
return this;
}
/**
* Indicate if TCP keep-alive must be enabled.
*
* @param tcpKeepAlive value
* @return this {@link Builder}
*/
public Builder tcpKeepAlive(Boolean tcpKeepAlive) {
this.tcpKeepAlive = tcpKeepAlive;
return this;
}
/**
* Indicate if UUID fields must returns as String
*
* @param uuidAsString value
* @return this {@link Builder}
*/
public Builder uuidAsString(Boolean uuidAsString) {
this.uuidAsString = uuidAsString;
return this;
}
/**
* Indicate TCP keep-idle value (for java 11+ only).
*
* @param tcpKeepIdle value
* @return this {@link Builder}
*/
public Builder tcpKeepIdle(Integer tcpKeepIdle) {
this.tcpKeepIdle = tcpKeepIdle;
return this;
}
/**
* Indicate TCP keep-count value (for java 11+ only).
*
* @param tcpKeepCount value
* @return this {@link Builder}
*/
public Builder tcpKeepCount(Integer tcpKeepCount) {
this.tcpKeepCount = tcpKeepCount;
return this;
}
/**
* Indicate TCP keep-interval value (for java 11+ only).
*
* @param tcpKeepInterval value
* @return this {@link Builder}
*/
public Builder tcpKeepInterval(Integer tcpKeepInterval) {
this.tcpKeepInterval = tcpKeepInterval;
return this;
}
/**
* Indicate that when connection fails, to send an RST TCP packet.
*
* @param tcpAbortiveClose value
* @return this {@link Builder}
*/
public Builder tcpAbortiveClose(Boolean tcpAbortiveClose) {
this.tcpAbortiveClose = tcpAbortiveClose;
return this;
}
/**
* Indicate what default Object type Geometry a resultset.getObject must return. possibility :
*
* <ul>
* <li>null or empty is WKB byte array
* <li>'default' will return org.mariadb.mariadb.jdbc.type Object
* </ul>
*
* In the future JTS might be implemented
*
* @param geometryDefault value
* @return this {@link Builder}
*/
public Builder geometryDefaultType(String geometryDefault) {
this.geometryDefaultType = nullOrEmpty(geometryDefault);
return this;
}
/**
* restrict authentication method to secure list. Default "default".
*
* @param restrictedAuth use authentication plugin list
* @return this {@link Builder}
*/
public Builder restrictedAuth(String restrictedAuth) {
this.restrictedAuth = restrictedAuth;
return this;
}
/**
* permit to execute an SQL command on connection creation
*
* @param initSql initial SQL command
* @return this {@link Builder}
*/
public Builder initSql(String initSql) {
this.initSql = initSql;
return this;
}
/**
* Indicate Hostname or IP address to bind the connection socket to a local (UNIX domain)
* socket.
*
* @param localSocketAddress Hostname or IP address
* @return this {@link Builder}
*/
public Builder localSocketAddress(String localSocketAddress) {
this.localSocketAddress = nullOrEmpty(localSocketAddress);
return this;
}
/**
* Indicate the network socket timeout (SO_TIMEOUT) in milliseconds. Value of 0 disables this
* timeout.
*
* <p>If the goal is to set a timeout for all queries, the server has permitted a solution to
* limit the query time by setting a system variable, max_statement_time. Default: 0
*
* @param socketTimeout socket timeout value
* @return this {@link Builder}
*/
public Builder socketTimeout(Integer socketTimeout) {
this.socketTimeout = socketTimeout;
return this;
}
/**
* Indicate that multi-queries are allowed. example: "insert into ab (i) values (1); insert into
* ab (i) values (2)".
*
* <p>If application build sql command string, this is probably a bad idea to enable this
* option, opening the door to sql injection. default: false.
*
* @param allowMultiQueries indicate if active
* @return this {@link Builder}
*/
public Builder allowMultiQueries(Boolean allowMultiQueries) {
this.allowMultiQueries = allowMultiQueries;
return this;
}
/**
* Indicate if LOAD DATA LOCAL INFILE are permitted. This will disable all pipelining
* implementation.
*
* @param allowLocalInfile indicate if permit LOAD DATA LOCAL INFILE commands
* @return this {@link Builder}
*/
public Builder allowLocalInfile(Boolean allowLocalInfile) {
this.allowLocalInfile = allowLocalInfile;
return this;
}
/**
* Indicate to compress exchanges with the database through gzip. This permits better
* performance when the database is not in the same location.
*
* @param useCompression to enable/disable compression
* @return this {@link Builder}
*/
public Builder useCompression(Boolean useCompression) {
this.useCompression = useCompression;
return this;
}
/**
* Set blank table name for metadata (old oracle compatibility)
*
* @param blankTableNameMeta use blank table name
* @return this {@link Builder}
*/
public Builder blankTableNameMeta(Boolean blankTableNameMeta) {
this.blankTableNameMeta = blankTableNameMeta;
return this;
}
/**
* On connection creation, indicate behavior when password is expired. When true (default) throw
* an expired password error When false, connection succeed in "sandbox" mode, only queries
* related to password change are allowed
*
* @return this {@link Builder}
*/
public Builder disconnectOnExpiredPasswords(Boolean disconnectOnExpiredPasswords) {
this.disconnectOnExpiredPasswords = disconnectOnExpiredPasswords;
return this;
}
/**
* set credential plugin type
*
* @param credentialType credential plugin type
* @return this {@link Builder}
*/
public Builder credentialType(String credentialType) {
this.credentialType = nullOrEmpty(credentialType);
return this;
}
/**
* Set ssl model
*
* @param sslMode ssl requirement
* @return this {@link Builder}
*/
public Builder sslMode(String sslMode) {
this.sslMode = sslMode;
return this;
}
/**
* force default transaction isolation, not using server default
*
* @param transactionIsolation indicate default transaction isolation
* @return this {@link Builder}
*/
public Builder transactionIsolation(String transactionIsolation) {
this.transactionIsolation = nullOrEmpty(transactionIsolation);
return this;
}
/**
* Indicate what implementation to use for metadata getExportedKeys. choice are
* "UseInformationSchema", "UseShowCreate" or "Auto"
*
* @param metaExportedKeys indicate implementation to use for metadata getExportedKeys
* @return this {@link Builder}
*/
public Builder metaExportedKeys(String metaExportedKeys) {
this.metaExportedKeys = nullOrEmpty(metaExportedKeys);
return this;
}
/**
* set possible cipher list (comma separated), not using default java cipher list
*
* @param enabledSslCipherSuites ssl cipher list
* @return this {@link Builder}
*/
public Builder enabledSslCipherSuites(String enabledSslCipherSuites) {
this.enabledSslCipherSuites = nullOrEmpty(enabledSslCipherSuites);
return this;
}
/**
* set connection session variables (comma separated)
*
* @param sessionVariables session variable list
* @return this {@link Builder}
*/
public Builder sessionVariables(String sessionVariables) {
this.sessionVariables = nullOrEmpty(sessionVariables);
return this;
}
/**
* TinyInt(1) to be considered as bit
*
* @param tinyInt1isBit Indicate if Tinyint(1) to be considered as bit
* @return this {@link Builder}
*/
public Builder tinyInt1isBit(Boolean tinyInt1isBit) {
this.tinyInt1isBit = tinyInt1isBit;
return this;
}
/**
* TinyInt(1) to be considered as boolean
*
* @param transformedBitIsBoolean Indicate if Tinyint(1) to be considered as boolean
* @return this {@link Builder}
*/
public Builder transformedBitIsBoolean(Boolean transformedBitIsBoolean) {
this.transformedBitIsBoolean = transformedBitIsBoolean;
return this;
}
/**
* Year datatype to be considered as date
*
* @param yearIsDateType indicate if Year must be considered as Date
* @return this {@link Builder}
*/
public Builder yearIsDateType(Boolean yearIsDateType) {
this.yearIsDateType = yearIsDateType;
return this;
}
/**
* Force timezone to use this timezone, not default java one
*
* @param timezone default timezone
* @return this {@link Builder}
*/
public Builder timezone(String timezone) {
this.timezone = nullOrEmpty(timezone);
return this;
}
/**
* indicate what timestamp timezone to use in exchanges with server possible value are
* LOCAL|SERVER|user-defined time zone
*
* @param connectionTimeZone default timezone
* @return this {@link Builder}
*/
public Builder connectionTimeZone(String connectionTimeZone) {
this.connectionTimeZone = nullOrEmpty(connectionTimeZone);
return this;
}
/**
* indicate what utf8mb4 collation to use. if not set, server default collation for utf8mb4 will
* be used
*
* @param connectionCollation utf8mb4 collation to use
* @return this {@link Builder}
*/
public Builder connectionCollation(String connectionCollation) {
this.connectionCollation = nullOrEmpty(connectionCollation);
return this;
}
/**
* Indicate if connectionTimeZone must be forced to session
*
* @param forceConnectionTimeZoneToSession must connector force connection timezone
* @return this {@link Builder}
*/
public Builder forceConnectionTimeZoneToSession(Boolean forceConnectionTimeZoneToSession) {
this.forceConnectionTimeZoneToSession = forceConnectionTimeZoneToSession;
return this;
}
/**
* Indicate if connection must preserve instants
*
* @param preserveInstants must connector preserve instants
* @return this {@link Builder}
*/
public Builder preserveInstants(Boolean preserveInstants) {
this.preserveInstants = preserveInstants;
return this;
}
/**
* Must queries be dump on exception stracktrace.
*
* @param dumpQueriesOnException must queries be dump on exception
* @return this {@link Builder}
*/
public Builder dumpQueriesOnException(Boolean dumpQueriesOnException) {
this.dumpQueriesOnException = dumpQueriesOnException;
return this;
}
/**
* If using server prepared statement, set LRU prepare cache size
*
* @param prepStmtCacheSize prepare cache size
* @return this {@link Builder}
*/
public Builder prepStmtCacheSize(Integer prepStmtCacheSize) {
this.prepStmtCacheSize = prepStmtCacheSize;
return this;
}
/**
* Indicate server to return affected rows in place of found rows. This impact the return number
* of rows affected by update
*
* @param useAffectedRows Indicate to user affected rows in place of found rows
* @return this {@link Builder}
*/
public Builder useAffectedRows(Boolean useAffectedRows) {
this.useAffectedRows = useAffectedRows;
return this;
}
/**
* Indicate to use Client or Server prepared statement
*
* @param useServerPrepStmts use Server prepared statement
* @return this {@link Builder}
*/
public Builder useServerPrepStmts(Boolean useServerPrepStmts) {
this.useServerPrepStmts = useServerPrepStmts;
return this;
}
/**
* Additional connection attributes to identify connection
*
* @param connectionAttributes additional connection attributes
* @return this {@link Builder}
*/
public Builder connectionAttributes(String connectionAttributes) {
this.connectionAttributes = nullOrEmpty(connectionAttributes);
return this;
}
/**
* Use server dedicated bulk batch command
*
* @param useBulkStmts use server bulk batch command.
* @return this {@link Builder}
*/
public Builder useBulkStmts(Boolean useBulkStmts) {
this.useBulkStmts = useBulkStmts;
return this;
}
/**
* Use server dedicated bulk batch command for insert (if useBulkStmts is enabled,
* useBulkStmtsForInserts will be enabled as well)
*
* @param useBulkStmtsForInserts use server bulk batch command.
* @return this {@link Builder}
*/
public Builder useBulkStmtsForInserts(Boolean useBulkStmtsForInserts) {
this.useBulkStmtsForInserts = useBulkStmtsForInserts;
return this;
}
/**
* Disable pipeline
*
* @param disablePipeline disable pipeline.
* @return this {@link Builder}
*/
public Builder disablePipeline(Boolean disablePipeline) {
this.disablePipeline = disablePipeline;
return this;
}
/**
* Permit to force autocommit connection value
*
* @param autocommit autocommit value
* @return this {@link Builder}
*/
public Builder autocommit(Boolean autocommit) {
this.autocommit = autocommit;
return this;
}
/**
* Permit indicating to force DatabaseMetadata.getDatabaseProductName() to return `MySQL` as
* database type, not real database type
*
* @param useMysqlMetadata force DatabaseMetadata.getDatabaseProductName() to return `MySQL`
* @return this {@link Builder}
*/
public Builder useMysqlMetadata(Boolean useMysqlMetadata) {
this.useMysqlMetadata = useMysqlMetadata;
return this;
}
/**
* Permit indicating in DatabaseMetadata if null value must be considered current schema/catalog
*
* @param nullDatabaseMeansCurrent indicating in DatabaseMetadata if null value must be
* considered current schema/catalog
* @return this {@link Builder}
*/
public Builder nullDatabaseMeansCurrent(Boolean nullDatabaseMeansCurrent) {
this.nullDatabaseMeansCurrent = nullDatabaseMeansCurrent;
return this;
}
/**
* "schema" and "database" are server synonymous. Connector historically get/set database using
* Connection.setCatalog()/getCatalog(), setSchema()/getSchema() being no-op This parameter
* indicate to change that behavior to use Schema in place of Catalog. Behavior will change
*
* <ul>
* <li>database change will be done with either Connection.setCatalog()/getCatalog() or
* Connection.setSchema()/getSchema()
* <li>DatabaseMetadata methods that use catalog or schema filtering
* <li>ResultsetMetadata database will be retrieved
* </ul>
*
* @param useCatalogTerm use CATALOG/SCHEMA
* @return this {@link Builder}
*/
public Builder useCatalogTerm(String useCatalogTerm) {
this.useCatalogTerm = useCatalogTerm;
return this;
}
/**
* Create database if not exist. This is mainly for test, since does require an additional query
* after connection
*
* @param createDatabaseIfNotExist must driver create database if doesn't exist
* @return this {@link Builder}
*/
public Builder createDatabaseIfNotExist(Boolean createDatabaseIfNotExist) {
this.createDatabaseIfNotExist = createDatabaseIfNotExist;
return this;
}
/**
* indicate if connector can use local state to avoid unnecessary queries. This means
* application must use JDBC dedicated methods, like connection.setTransactionIsolation and
* never queries like "SET SESSION TRANSACTION ISOLATION LEVEL X" directly
*
* @param useLocalSessionState can driver rely on local state
* @return this {@link Builder}
*/
public Builder useLocalSessionState(Boolean useLocalSessionState) {
this.useLocalSessionState = useLocalSessionState;
return this;
}
/**
* indicate if connector must return multi-generated ids. (For connector 2.x compatibility)
*
* @param returnMultiValuesGeneratedIds must return multi-values generated ids
* @return this {@link Builder}
*/
public Builder returnMultiValuesGeneratedIds(Boolean returnMultiValuesGeneratedIds) {
this.returnMultiValuesGeneratedIds = returnMultiValuesGeneratedIds;
return this;
}
/**
* indicate if connector must force sql_mode strict mode for jdbc compliance
*
* @param jdbcCompliantTruncation must force sql_mode strict mode for jdbc compliance
* @return this {@link Builder}
*/
public Builder jdbcCompliantTruncation(Boolean jdbcCompliantTruncation) {
this.jdbcCompliantTruncation = jdbcCompliantTruncation;
return this;
}
/**
* Force Timestamp string representation compatible 2.7 version Timestamp string representation
* will then correspond to Timestamp.toString() in place of taking field precision
*
* @param oldModeNoPrecisionTimestamp force 2.7 timestamp to string behavior
* @return this {@link Builder}
*/
public Builder oldModeNoPrecisionTimestamp(Boolean oldModeNoPrecisionTimestamp) {
this.oldModeNoPrecisionTimestamp = oldModeNoPrecisionTimestamp;
return this;
}
/**
* indicate if connector must redirect connection when receiving server redirect information
*
* @param permitRedirect must redirect when required
* @return this {@link Builder}
*/
public Builder permitRedirect(Boolean permitRedirect) {
this.permitRedirect = permitRedirect;
return this;
}
/**
* Indicate if for XA transaction, connector must reuse same connection.
*
* @param pinGlobalTxToPhysicalConnection force reuse of same connection
* @return this {@link Builder}
*/
public Builder pinGlobalTxToPhysicalConnection(Boolean pinGlobalTxToPhysicalConnection) {
this.pinGlobalTxToPhysicalConnection = pinGlobalTxToPhysicalConnection;
return this;
}
/**
* Indicate if Statement/PreparedStatement.executeQuery for command that produce no result will
* return an exception or just an empty result-set When enabled, command not returning no data
* will end returning an empty result-set When disabled, command not returning no data will end
* throwing an exception
*
* @param permitNoResults force reuse of same connection
* @return this {@link Builder}
*/
public Builder permitNoResults(Boolean permitNoResults) {
this.permitNoResults = permitNoResults;
return this;
}
/**
* Permit caching codecs
*
* @param cacheCodecs can codec load be cached
* @return this {@link Builder}
*/
public Builder cacheCodecs(Boolean cacheCodecs) {
this.cacheCodecs = cacheCodecs;
return this;
}
/**
* On dead-lock exception must add innodb status in exception error message. If enabled, an
* additional command will be done to retrieve innodb status when dead-lock occurs.
*
* @param includeInnodbStatusInDeadlockExceptions Must dead-lock exception must add innodb
* status in exception error message
* @return this {@link Builder}
*/
public Builder includeInnodbStatusInDeadlockExceptions(
Boolean includeInnodbStatusInDeadlockExceptions) {
this.includeInnodbStatusInDeadlockExceptions = includeInnodbStatusInDeadlockExceptions;
return this;
}
/**
* Dead-lock error will contain threads information
*
* @param includeThreadDumpInDeadlockExceptions must dead-lock error contain treads informations
* @return this {@link Builder}
*/
public Builder includeThreadDumpInDeadlockExceptions(
Boolean includeThreadDumpInDeadlockExceptions) {
this.includeThreadDumpInDeadlockExceptions = includeThreadDumpInDeadlockExceptions;
return this;
}
/**
* set service principal name (GSSAPI)
*
* @param servicePrincipalName service principal name (GSSAPI)
* @return this {@link Builder}
*/
public Builder servicePrincipalName(String servicePrincipalName) {
this.servicePrincipalName = nullOrEmpty(servicePrincipalName);
return this;
}
/**
* Set default fetch size
*
* @param defaultFetchSize default fetch size
* @return this {@link Builder}
*/
public Builder defaultFetchSize(Integer defaultFetchSize) {
this.defaultFetchSize = defaultFetchSize;
return this;
}
/**
* Permit to defined default tls plugin type
*
* @param tlsSocketType default tls socket plugin to use
* @return this {@link Builder}
*/
public Builder tlsSocketType(String tlsSocketType) {
this.tlsSocketType = nullOrEmpty(tlsSocketType);
return this;
}
/**
* Set the log size limit for query
*
* @param maxQuerySizeToLog set query size limit
* @return this {@link Builder}
*/
public Builder maxQuerySizeToLog(Integer maxQuerySizeToLog) {
this.maxQuerySizeToLog = maxQuerySizeToLog;
return this;
}
/**
* Indicate to driver server max_allowed_packet. This permit to driver to avoid sending commands
* too big, that would have make server to drop connection
*
* @param maxAllowedPacket indicate server max_allowed_packet value
* @return this {@link Builder}
*/
public Builder maxAllowedPacket(Integer maxAllowedPacket) {
this.maxAllowedPacket = maxAllowedPacket;
return this;
}
/**
* When failover occurs, how many connection attempt before throwing error when reconnecting
*
* @param retriesAllDown number of attemps to reconnect
* @return this {@link Builder}
*/
public Builder retriesAllDown(Integer retriesAllDown) {
this.retriesAllDown = retriesAllDown;
return this;
}
/**
* Indicate galera allowed state (comma separated), permitting to validate if galera node is
* synchronized
*
* @param galeraAllowedState galera allowed state
* @return this {@link Builder}
*/
public Builder galeraAllowedState(String galeraAllowedState) {
this.galeraAllowedState = nullOrEmpty(galeraAllowedState);
return this;
}
/**
* Create pool if not existing, or get a connection for the pool associate with this connection
* string if existing.
*
* @param pool use pool
* @return this {@link Builder}
*/
public Builder pool(Boolean pool) {
this.pool = pool;
return this;
}
/**
* set pool name
*
* @param poolName pool name
* @return this {@link Builder}
*/
public Builder poolName(String poolName) {
this.poolName = nullOrEmpty(poolName);
return this;
}
/**
* Set the limit number of connection in pool.
*
* @param maxPoolSize maximum connection size in pool.
* @return this {@link Builder}
*/
public Builder maxPoolSize(Integer maxPoolSize) {
this.maxPoolSize = maxPoolSize;
return this;
}
/**
* Minimum pool size.
*
* @param minPoolSize minimum pool size
* @return this {@link Builder}
*/
public Builder minPoolSize(Integer minPoolSize) {
this.minPoolSize = minPoolSize;
return this;
}
/**
* Set the maximum idle time of a connection indicating that connection must be released
*
* @param maxIdleTime maximum idle time of a connection in pool
* @return this {@link Builder}
*/
public Builder maxIdleTime(Integer maxIdleTime) {
this.maxIdleTime = maxIdleTime;
return this;
}
/**
* Must pool register JMX information
*
* @param registerJmxPool register pool to JMX
* @return this {@link Builder}
*/
public Builder registerJmxPool(Boolean registerJmxPool) {
this.registerJmxPool = registerJmxPool;
return this;
}
/**
* Pool will validate connection before giving it. This amount of time indicate that recently
* use connection can skip validation 0 means connection will be validated each time (even is
* just used)
*
* @param poolValidMinDelay time limit indicating that connection in pool must be validated
* @return this {@link Builder}
*/
public Builder poolValidMinDelay(Integer poolValidMinDelay) {
this.poolValidMinDelay = poolValidMinDelay;
return this;
}
/**
* Indicate that connection returned to pool must be RESETed like having proper connection
* state.
*
* @param useResetConnection use reset connection when returning connection to pool.
* @return this {@link Builder}
*/
public Builder useResetConnection(Boolean useResetConnection) {
this.useResetConnection = useResetConnection;
return this;
}
/**
* MySQL Authentication RSA server file, for mysql authentication
*
* @param serverRsaPublicKeyFile server RSA public key file
* @return this {@link Builder}
*/
public Builder serverRsaPublicKeyFile(String serverRsaPublicKeyFile) {
this.serverRsaPublicKeyFile = nullOrEmpty(serverRsaPublicKeyFile);
return this;
}
/**
* Allow RSA server file retrieval from MySQL server
*
* @param allowPublicKeyRetrieval Allow RSA server file retrieval from MySQL server
* @return this {@link Builder}
*/
public Builder allowPublicKeyRetrieval(Boolean allowPublicKeyRetrieval) {
this.allowPublicKeyRetrieval = allowPublicKeyRetrieval;
return this;
}
/**
* Cache all socket available information.
*
* @param useReadAheadInput cache available socket data when reading socket.
* @return this {@link Builder}
*/
public Builder useReadAheadInput(Boolean useReadAheadInput) {
this.useReadAheadInput = useReadAheadInput;
return this;
}
/**
* Cache server prepare result
*
* @param cachePrepStmts cache server prepared result
* @return this {@link Builder}
*/
public Builder cachePrepStmts(Boolean cachePrepStmts) {
this.cachePrepStmts = cachePrepStmts;
return this;
}
/**
* Must cache commands in transaction and replay transaction on failover.
*
* @param transactionReplay cache transaction and replay on failover
* @return this {@link Builder}
*/
public Builder transactionReplay(Boolean transactionReplay) {
this.transactionReplay = transactionReplay;
return this;
}
/**
* Transaction replay cache size
*
* @param transactionReplaySize transaction replay cache size
* @return this {@link Builder}
*/
public Builder transactionReplaySize(Integer transactionReplaySize) {
this.transactionReplaySize = transactionReplaySize;
return this;
}
/**
* Build a configuration
*
* @return a Configuration object
*/
public Configuration build() {
Configuration conf = new Configuration(this);
conf.initialUrl = buildUrl(conf);
return conf;
}
}
}