org.mariadb.jdbc.Configuration Maven / Gradle / Ivy
Show all versions of mariadb-java-client Show documentation
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2023 MariaDB Corporation Ab
package org.mariadb.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
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.log.Logger;
import org.mariadb.jdbc.util.log.Loggers;
import org.mariadb.jdbc.util.options.OptionAliases;
/**
* parse and verification of URL.
*
* basic syntax :
* {@code
* jdbc:mariadb:[replication:|failover|loadbalance:|aurora:]//[,]/[database>]
* [?=[&=]] }
*
* hostDescription:
* - simple :
* {@code :}
* (for example localhost:3306)
*
* - complex :
* {@code address=[(type=(master|slave))][(port=)](host=)}
*
*
* type is by default master
* port is by default 3306
*
* host can be dns name, ipv4 or ipv6.
* in case of ipv6 and simple host description, the ip must be written inside bracket.
* exemple : {@code jdbc:mariadb://[2001:0660:7401:0200:0000:0000:0edf:bdd7]:3306}
*
*
Some examples :
* {@code jdbc:mariadb://localhost:3306/database?user=greg&password=pass}
* {@code
* jdbc:mariadb://address=(type=master)(host=master1),address=(port=3307)(type=slave)(host=slave1)/database?user=greg&password=pass}
*
*/
public class Configuration {
private static final Logger logger = Loggers.getLogger(Configuration.class);
// standard options
private String user = null;
private String password = null;
private String database = null;
private List addresses = null;
private HaMode haMode = HaMode.NONE;
private String initialUrl = null;
private Properties nonMappedOptions = null;
// various
private String timezone = null;
private Boolean autocommit = null;
private boolean useMysqlMetadata = false;
private CatalogTerm useCatalogTerm = CatalogTerm.UseCatalog;
private boolean createDatabaseIfNotExist = false;
private boolean useLocalSessionState = false;
private TransactionIsolation transactionIsolation = null;
private int defaultFetchSize = 0;
private int maxQuerySizeToLog = 1024;
private Integer maxAllowedPacket = null;
private String geometryDefaultType = null;
private String restrictedAuth = null;
private String initSql = null;
// socket
private String socketFactory = null;
private int connectTimeout =
DriverManager.getLoginTimeout() > 0 ? DriverManager.getLoginTimeout() * 1000 : 30_000;
private String pipe = null;
private String localSocket = null;
private boolean uuidAsString = false;
private boolean tcpKeepAlive = true;
private int tcpKeepIdle = 0;
private int tcpKeepCount = 0;
private int tcpKeepInterval = 0;
private boolean tcpAbortiveClose = false;
private String localSocketAddress = null;
private int socketTimeout = 0;
private boolean useReadAheadInput = false;
private String tlsSocketType = null;
// SSL
private SslMode sslMode = SslMode.DISABLE;
private String serverSslCert = null;
private String keyStore = null;
private String keyStorePassword = null;
private String keyPassword = null;
private String keyStoreType = null;
private String trustStoreType = null;
private String enabledSslCipherSuites = null;
private String enabledSslProtocolSuites = null;
// protocol
private boolean allowMultiQueries = false;
private boolean allowLocalInfile = true;
private boolean useCompression = false;
private boolean useAffectedRows = false;
private boolean useBulkStmts = false;
private boolean useBulkStmtsForInserts = true;
private boolean disablePipeline = false;
// prepare
private boolean cachePrepStmts = true;
private int prepStmtCacheSize = 250;
private boolean useServerPrepStmts = false;
// authentication
private CredentialPlugin credentialType = null;
private String sessionVariables = null;
private String connectionAttributes = null;
private String servicePrincipalName = null;
// meta
private boolean blankTableNameMeta = false;
private boolean tinyInt1isBit = true;
private boolean transformedBitIsBoolean = true;
private boolean yearIsDateType = true;
private boolean dumpQueriesOnException = false;
private boolean includeInnodbStatusInDeadlockExceptions = false;
private boolean includeThreadDumpInDeadlockExceptions = false;
// HA options
private int retriesAllDown = 120;
private String galeraAllowedState = null;
private boolean transactionReplay = false;
private int transactionReplaySize = 64;
// Pool options
private boolean pool = false;
private String poolName = null;
private int maxPoolSize = 8;
private int minPoolSize = 8;
private int maxIdleTime = 600_000;
private boolean registerJmxPool = true;
private int poolValidMinDelay = 1000;
private boolean useResetConnection = false;
// MySQL sha authentication
private String serverRsaPublicKeyFile = null;
private boolean allowPublicKeyRetrieval = false;
private Codec>[] codecs = null;
private Configuration() {}
private Configuration(
String user,
String password,
String database,
List addresses,
HaMode haMode,
Properties nonMappedOptions,
String timezone,
Boolean autocommit,
boolean useMysqlMetadata,
CatalogTerm useCatalogTerm,
boolean createDatabaseIfNotExist,
boolean useLocalSessionState,
TransactionIsolation transactionIsolation,
int defaultFetchSize,
int maxQuerySizeToLog,
Integer maxAllowedPacket,
String geometryDefaultType,
String restrictedAuth,
String initSql,
String socketFactory,
int connectTimeout,
String pipe,
String localSocket,
boolean tcpKeepAlive,
boolean uuidAsString,
int tcpKeepIdle,
int tcpKeepCount,
int tcpKeepInterval,
boolean tcpAbortiveClose,
String localSocketAddress,
int socketTimeout,
boolean useReadAheadInput,
String tlsSocketType,
SslMode sslMode,
String serverSslCert,
String keyStore,
String keyStorePassword,
String keyPassword,
String keyStoreType,
String trustStoreType,
String enabledSslCipherSuites,
String enabledSslProtocolSuites,
boolean allowMultiQueries,
boolean allowLocalInfile,
boolean useCompression,
boolean useAffectedRows,
boolean useBulkStmts,
boolean useBulkStmtsForInserts,
boolean disablePipeline,
boolean cachePrepStmts,
int prepStmtCacheSize,
boolean useServerPrepStmts,
CredentialPlugin credentialType,
String sessionVariables,
String connectionAttributes,
String servicePrincipalName,
boolean blankTableNameMeta,
boolean tinyInt1isBit,
boolean transformedBitIsBoolean,
boolean yearIsDateType,
boolean dumpQueriesOnException,
boolean includeInnodbStatusInDeadlockExceptions,
boolean includeThreadDumpInDeadlockExceptions,
int retriesAllDown,
String galeraAllowedState,
boolean transactionReplay,
int transactionReplaySize,
boolean pool,
String poolName,
int maxPoolSize,
int minPoolSize,
int maxIdleTime,
boolean registerJmxPool,
int poolValidMinDelay,
boolean useResetConnection,
String serverRsaPublicKeyFile,
boolean allowPublicKeyRetrieval) {
this.user = user;
this.password = password;
this.database = database;
this.addresses = addresses;
this.haMode = haMode;
this.nonMappedOptions = nonMappedOptions;
this.timezone = timezone;
this.autocommit = autocommit;
this.useMysqlMetadata = useMysqlMetadata;
this.useCatalogTerm = useCatalogTerm;
this.createDatabaseIfNotExist = createDatabaseIfNotExist;
this.useLocalSessionState = useLocalSessionState;
this.transactionIsolation = transactionIsolation;
this.defaultFetchSize = defaultFetchSize;
this.maxQuerySizeToLog = maxQuerySizeToLog;
this.maxAllowedPacket = maxAllowedPacket;
this.geometryDefaultType = geometryDefaultType;
this.restrictedAuth = restrictedAuth;
this.initSql = initSql;
this.socketFactory = socketFactory;
this.connectTimeout = connectTimeout;
this.pipe = pipe;
this.localSocket = localSocket;
this.tcpKeepAlive = tcpKeepAlive;
this.uuidAsString = uuidAsString;
this.tcpKeepIdle = tcpKeepIdle;
this.tcpKeepCount = tcpKeepCount;
this.tcpKeepInterval = tcpKeepInterval;
this.tcpAbortiveClose = tcpAbortiveClose;
this.localSocketAddress = localSocketAddress;
this.socketTimeout = socketTimeout;
this.useReadAheadInput = useReadAheadInput;
this.tlsSocketType = tlsSocketType;
this.sslMode = sslMode;
this.serverSslCert = serverSslCert;
this.keyStore = keyStore;
this.keyStorePassword = keyStorePassword;
this.keyPassword = keyPassword;
this.keyStoreType = keyStoreType;
this.trustStoreType = trustStoreType;
this.enabledSslCipherSuites = enabledSslCipherSuites;
this.enabledSslProtocolSuites = enabledSslProtocolSuites;
this.allowMultiQueries = allowMultiQueries;
this.allowLocalInfile = allowLocalInfile;
this.useCompression = useCompression;
this.useAffectedRows = useAffectedRows;
this.useBulkStmts = useBulkStmts;
this.useBulkStmtsForInserts = useBulkStmtsForInserts;
this.disablePipeline = disablePipeline;
this.cachePrepStmts = cachePrepStmts;
this.prepStmtCacheSize = prepStmtCacheSize;
this.useServerPrepStmts = useServerPrepStmts;
this.credentialType = credentialType;
this.sessionVariables = sessionVariables;
this.connectionAttributes = connectionAttributes;
this.servicePrincipalName = servicePrincipalName;
this.blankTableNameMeta = blankTableNameMeta;
this.tinyInt1isBit = tinyInt1isBit;
this.transformedBitIsBoolean = transformedBitIsBoolean;
this.yearIsDateType = yearIsDateType;
this.dumpQueriesOnException = dumpQueriesOnException;
this.includeInnodbStatusInDeadlockExceptions = includeInnodbStatusInDeadlockExceptions;
this.includeThreadDumpInDeadlockExceptions = includeThreadDumpInDeadlockExceptions;
this.retriesAllDown = retriesAllDown;
this.galeraAllowedState = galeraAllowedState;
this.transactionReplay = transactionReplay;
this.transactionReplaySize = transactionReplaySize;
this.pool = pool;
this.poolName = poolName;
this.maxPoolSize = maxPoolSize;
this.minPoolSize = minPoolSize;
this.maxIdleTime = maxIdleTime;
this.registerJmxPool = registerJmxPool;
this.poolValidMinDelay = poolValidMinDelay;
this.useResetConnection = useResetConnection;
this.serverRsaPublicKeyFile = serverRsaPublicKeyFile;
this.allowPublicKeyRetrieval = allowPublicKeyRetrieval;
this.initialUrl = buildUrl(this);
}
private Configuration(
String database,
List addresses,
HaMode haMode,
String user,
String password,
String enabledSslProtocolSuites,
String socketFactory,
Integer connectTimeout,
String pipe,
String localSocket,
Boolean tcpKeepAlive,
Boolean uuidAsString,
Integer tcpKeepIdle,
Integer tcpKeepCount,
Integer tcpKeepInterval,
Boolean tcpAbortiveClose,
String localSocketAddress,
Integer socketTimeout,
Boolean allowMultiQueries,
Boolean allowLocalInfile,
Boolean useCompression,
Boolean blankTableNameMeta,
String credentialType,
String sslMode,
String transactionIsolation,
String enabledSslCipherSuites,
String sessionVariables,
Boolean tinyInt1isBit,
Boolean transformedBitIsBoolean,
Boolean yearIsDateType,
String timezone,
Boolean dumpQueriesOnException,
Integer prepStmtCacheSize,
Boolean useAffectedRows,
Boolean useServerPrepStmts,
String connectionAttributes,
Boolean useBulkStmts,
Boolean useBulkStmtsForInserts,
Boolean disablePipeline,
Boolean autocommit,
Boolean useMysqlMetadata,
String useCatalogTerm,
Boolean createDatabaseIfNotExist,
Boolean useLocalSessionState,
Boolean includeInnodbStatusInDeadlockExceptions,
Boolean includeThreadDumpInDeadlockExceptions,
String servicePrincipalName,
Integer defaultFetchSize,
String tlsSocketType,
Integer maxQuerySizeToLog,
Integer maxAllowedPacket,
Integer retriesAllDown,
String galeraAllowedState,
Boolean pool,
String poolName,
Integer maxPoolSize,
Integer minPoolSize,
Integer maxIdleTime,
Boolean registerJmxPool,
Integer poolValidMinDelay,
Boolean useResetConnection,
String serverRsaPublicKeyFile,
Boolean allowPublicKeyRetrieval,
String serverSslCert,
String keyStore,
String keyStorePassword,
String keyPassword,
String keyStoreType,
String trustStoreType,
Boolean useReadAheadInput,
Boolean cachePrepStmts,
Boolean transactionReplay,
Integer transactionReplaySize,
String geometryDefaultType,
String restrictedAuth,
String initSql,
Properties nonMappedOptions)
throws SQLException {
this.database = database;
this.addresses = addresses;
this.nonMappedOptions = nonMappedOptions;
if (haMode != null) this.haMode = haMode;
this.credentialType = CredentialPluginLoader.get(credentialType);
this.user = user;
this.password = password;
this.enabledSslProtocolSuites = enabledSslProtocolSuites;
this.socketFactory = socketFactory;
if (connectTimeout != null) this.connectTimeout = connectTimeout;
this.pipe = pipe;
this.localSocket = localSocket;
if (tcpKeepAlive != null) this.tcpKeepAlive = tcpKeepAlive;
if (uuidAsString != null) this.uuidAsString = uuidAsString;
if (tcpKeepIdle != null) this.tcpKeepIdle = tcpKeepIdle;
if (tcpKeepCount != null) this.tcpKeepCount = tcpKeepCount;
if (tcpKeepInterval != null) this.tcpKeepInterval = tcpKeepInterval;
if (tcpAbortiveClose != null) this.tcpAbortiveClose = tcpAbortiveClose;
this.localSocketAddress = localSocketAddress;
if (socketTimeout != null) this.socketTimeout = socketTimeout;
if (allowMultiQueries != null) this.allowMultiQueries = allowMultiQueries;
if (allowLocalInfile != null) this.allowLocalInfile = allowLocalInfile;
if (useCompression != null) this.useCompression = useCompression;
if (blankTableNameMeta != null) this.blankTableNameMeta = blankTableNameMeta;
if (this.credentialType != null
&& this.credentialType.mustUseSsl()
&& (sslMode == null || SslMode.from(sslMode) == SslMode.DISABLE)) {
this.sslMode = SslMode.VERIFY_FULL;
} else {
this.sslMode = sslMode != null ? SslMode.from(sslMode) : SslMode.DISABLE;
}
if (transactionIsolation != null)
this.transactionIsolation = TransactionIsolation.from(transactionIsolation);
this.enabledSslCipherSuites = enabledSslCipherSuites;
this.sessionVariables = sessionVariables;
if (tinyInt1isBit != null) this.tinyInt1isBit = tinyInt1isBit;
if (transformedBitIsBoolean != null) this.transformedBitIsBoolean = transformedBitIsBoolean;
if (yearIsDateType != null) this.yearIsDateType = yearIsDateType;
this.timezone = timezone;
if (dumpQueriesOnException != null) this.dumpQueriesOnException = dumpQueriesOnException;
if (prepStmtCacheSize != null) this.prepStmtCacheSize = prepStmtCacheSize;
if (useAffectedRows != null) this.useAffectedRows = useAffectedRows;
if (useServerPrepStmts != null) this.useServerPrepStmts = useServerPrepStmts;
this.connectionAttributes = connectionAttributes;
if (useBulkStmts != null) this.useBulkStmts = useBulkStmts;
if (useBulkStmtsForInserts != null) this.useBulkStmtsForInserts = useBulkStmtsForInserts;
if (disablePipeline != null) this.disablePipeline = disablePipeline;
if (autocommit != null) this.autocommit = autocommit;
if (useMysqlMetadata != null) this.useMysqlMetadata = useMysqlMetadata;
if (useCatalogTerm != null) {
if (!"CATALOG".equalsIgnoreCase(useCatalogTerm)
&& !"SCHEMA".equalsIgnoreCase(useCatalogTerm)) {
throw new IllegalArgumentException(
"useCatalogTerm can only have CATALOG/SCHEMA value, current set value is "
+ useCatalogTerm);
}
this.useCatalogTerm =
"CATALOG".equalsIgnoreCase(useCatalogTerm)
? CatalogTerm.UseCatalog
: CatalogTerm.UseSchema;
}
if (createDatabaseIfNotExist != null) this.createDatabaseIfNotExist = createDatabaseIfNotExist;
if (useLocalSessionState != null) this.useLocalSessionState = useLocalSessionState;
if (includeInnodbStatusInDeadlockExceptions != null)
this.includeInnodbStatusInDeadlockExceptions = includeInnodbStatusInDeadlockExceptions;
if (includeThreadDumpInDeadlockExceptions != null)
this.includeThreadDumpInDeadlockExceptions = includeThreadDumpInDeadlockExceptions;
if (servicePrincipalName != null) this.servicePrincipalName = servicePrincipalName;
if (defaultFetchSize != null) this.defaultFetchSize = defaultFetchSize;
if (tlsSocketType != null) this.tlsSocketType = tlsSocketType;
if (maxQuerySizeToLog != null) this.maxQuerySizeToLog = maxQuerySizeToLog;
if (maxAllowedPacket != null) this.maxAllowedPacket = maxAllowedPacket;
if (retriesAllDown != null) this.retriesAllDown = retriesAllDown;
if (galeraAllowedState != null) this.galeraAllowedState = galeraAllowedState;
if (pool != null) this.pool = pool;
if (poolName != null) this.poolName = poolName;
if (maxPoolSize != null) this.maxPoolSize = maxPoolSize;
// if min pool size default to maximum pool size if not set
if (minPoolSize != null) {
this.minPoolSize = minPoolSize;
} else {
this.minPoolSize = this.maxPoolSize;
}
if (maxIdleTime != null) this.maxIdleTime = maxIdleTime;
if (registerJmxPool != null) this.registerJmxPool = registerJmxPool;
if (poolValidMinDelay != null) this.poolValidMinDelay = poolValidMinDelay;
if (useResetConnection != null) this.useResetConnection = useResetConnection;
if (serverRsaPublicKeyFile != null) this.serverRsaPublicKeyFile = serverRsaPublicKeyFile;
if (allowPublicKeyRetrieval != null) this.allowPublicKeyRetrieval = allowPublicKeyRetrieval;
if (useReadAheadInput != null) this.useReadAheadInput = useReadAheadInput;
if (cachePrepStmts != null) this.cachePrepStmts = cachePrepStmts;
if (transactionReplay != null) this.transactionReplay = transactionReplay;
if (transactionReplaySize != null) this.transactionReplaySize = transactionReplaySize;
if (geometryDefaultType != null) this.geometryDefaultType = geometryDefaultType;
if (restrictedAuth != null) this.restrictedAuth = restrictedAuth;
if (initSql != null) this.initSql = initSql;
if (serverSslCert != null) this.serverSslCert = serverSslCert;
if (keyStore != null) this.keyStore = keyStore;
if (keyStorePassword != null) this.keyStorePassword = keyStorePassword;
if (keyPassword != null) this.keyPassword = keyPassword;
if (keyStoreType != null) this.keyStoreType = keyStoreType;
if (trustStoreType != null) this.trustStoreType = trustStoreType;
// *************************************************************
// host primary check
// *************************************************************
boolean first = true;
for (HostAddress host : addresses) {
boolean primary = haMode != HaMode.REPLICATION || first;
if (host.primary == null) {
host.primary = primary;
}
first = false;
}
// *************************************************************
// option value verification
// *************************************************************
// int fields must all be positive
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 SQLException(
String.format("Value for %s must be >= 1 (value is %s)", field.getName(), val));
}
}
}
} catch (IllegalArgumentException | IllegalAccessException ie) {
// eat
}
}
/**
* 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();
int separator = url.indexOf("//");
if (separator == -1) {
throw new IllegalArgumentException(
"url parsing error : '//' is not present in the url " + url);
}
builder.haMode(parseHaMode(url, separator));
String urlSecondPart = url.substring(separator + 2);
int dbIndex = urlSecondPart.indexOf("/");
int paramIndex = urlSecondPart.indexOf("?");
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;
}
if (additionalParameters != null) {
int optIndex = additionalParameters.indexOf("?");
String database;
if (optIndex < 0) {
database = (additionalParameters.length() > 1) ? additionalParameters.substring(1) : null;
} else {
if (optIndex == 0) {
database = null;
} else {
database = additionalParameters.substring(1, optIndex);
if (database.isEmpty()) database = null;
}
String urlParameters = additionalParameters.substring(optIndex + 1);
if (urlParameters != null && !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));
}
}
}
}
builder.database(database);
} else {
builder.database(null);
}
mapPropertiesToOption(builder, properties);
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 mapPropertiesToOption(Builder builder, Properties properties) {
Properties nonMappedOptions = new Properties();
try {
// Option object is already initialized to default values.
// loop on properties,
// - check DefaultOption to check that property value correspond to type (and range)
// - set values
for (final Object keyObj : properties.keySet()) {
String realKey =
OptionAliases.OPTIONS_ALIASES.get(keyObj.toString().toLowerCase(Locale.ROOT));
if (realKey == null) realKey = keyObj.toString();
final Object propertyValue = properties.get(keyObj);
if (propertyValue != null && realKey != null) {
boolean used = false;
for (Field field : Builder.class.getDeclaredFields()) {
if (realKey.toLowerCase(Locale.ROOT).equals(field.getName().toLowerCase(Locale.ROOT))) {
field.setAccessible(true);
used = true;
if (field.getGenericType().equals(String.class)
&& !propertyValue.toString().isEmpty()) {
field.set(builder, propertyValue);
} else if (field.getGenericType().equals(Boolean.class)) {
switch (propertyValue.toString().toLowerCase()) {
case "":
case "1":
case "true":
field.set(builder, Boolean.TRUE);
break;
case "0":
case "false":
field.set(builder, Boolean.FALSE);
break;
default:
throw new IllegalArgumentException(
String.format(
"Optional parameter %s must be boolean (true/false or 0/1) was '%s'",
keyObj, propertyValue));
}
} else if (field.getGenericType().equals(Integer.class)) {
try {
final Integer value = Integer.parseInt(propertyValue.toString());
field.set(builder, value);
} catch (NumberFormatException n) {
throw new IllegalArgumentException(
String.format(
"Optional parameter %s must be Integer, was '%s'",
keyObj, propertyValue));
}
}
}
}
if (!used) nonMappedOptions.put(realKey, propertyValue);
}
}
// for compatibility with 2.x
if (isSet("useSsl", nonMappedOptions) || isSet("useSSL", nonMappedOptions)) {
Properties deprecatedDesc = new Properties();
try (InputStream inputStream =
Driver.class.getClassLoader().getResourceAsStream("deprecated.properties")) {
deprecatedDesc.load(inputStream);
} catch (IOException io) {
// eat
}
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 (IllegalAccessException | SecurityException s) {
throw new IllegalArgumentException("Unexpected error", s);
}
builder._nonMappedOptions = nonMappedOptions;
}
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);
}
}
/**
* 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 new Configuration(
username != null && username.isEmpty() ? null : username,
password != null && password.isEmpty() ? null : password,
this.database,
this.addresses,
this.haMode,
this.nonMappedOptions,
this.timezone,
this.autocommit,
this.useMysqlMetadata,
this.useCatalogTerm,
this.createDatabaseIfNotExist,
this.useLocalSessionState,
this.transactionIsolation,
this.defaultFetchSize,
this.maxQuerySizeToLog,
this.maxAllowedPacket,
this.geometryDefaultType,
this.restrictedAuth,
this.initSql,
this.socketFactory,
this.connectTimeout,
this.pipe,
this.localSocket,
this.tcpKeepAlive,
this.uuidAsString,
this.tcpKeepIdle,
this.tcpKeepCount,
this.tcpKeepInterval,
this.tcpAbortiveClose,
this.localSocketAddress,
this.socketTimeout,
this.useReadAheadInput,
this.tlsSocketType,
this.sslMode,
this.serverSslCert,
this.keyStore,
this.keyStorePassword,
this.keyPassword,
this.keyStoreType,
this.trustStoreType,
this.enabledSslCipherSuites,
this.enabledSslProtocolSuites,
this.allowMultiQueries,
this.allowLocalInfile,
this.useCompression,
this.useAffectedRows,
this.useBulkStmts,
this.useBulkStmtsForInserts,
this.disablePipeline,
this.cachePrepStmts,
this.prepStmtCacheSize,
this.useServerPrepStmts,
this.credentialType,
this.sessionVariables,
this.connectionAttributes,
this.servicePrincipalName,
this.blankTableNameMeta,
this.tinyInt1isBit,
this.transformedBitIsBoolean,
this.yearIsDateType,
this.dumpQueriesOnException,
this.includeInnodbStatusInDeadlockExceptions,
this.includeThreadDumpInDeadlockExceptions,
this.retriesAllDown,
this.galeraAllowedState,
this.transactionReplay,
this.transactionReplaySize,
this.pool,
this.poolName,
this.maxPoolSize,
this.minPoolSize,
this.maxIdleTime,
this.registerJmxPool,
this.poolValidMinDelay,
this.useResetConnection,
this.serverRsaPublicKeyFile,
this.allowPublicKeyRetrieval);
}
/**
* Connection default database
*
* @return database
*/
public String database() {
return database;
}
/**
* addresses
*
* @return addresses
*/
public List 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;
}
/**
* key store password
*
* @return key store password
*/
public String keyStorePassword() {
return keyStorePassword;
}
/**
* 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;
}
/**
* 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;
}
/**
* SSl mode
*
* @return ssl mode
*/
public SslMode sslMode() {
return sslMode;
}
/**
* Default transaction isolation
*
* @return default transaction isolation.
*/
public TransactionIsolation transactionIsolation() {
return transactionIsolation;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* Permit to have string information on how string is parsed. example :
* Configuration.toConf("jdbc:mariadb://localhost/test") will return a String containing:
* Configuration:
* * resulting Url : jdbc:mariadb://localhost/test
* Unknown options : None
*
* Non default options :
* * database : test
*
* default options :
* * user : null
* ...
*
*
* @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());
StringBuilder sb = new StringBuilder();
StringBuilder sbUnknownOpts = new StringBuilder();
if (conf.nonMappedOptions.isEmpty()) {
sbUnknownOpts.append("None");
} else {
for (Map.Entry