diff --git a/src/main/java/org/mariadb/jdbc/HostAddress.java b/src/main/java/org/mariadb/jdbc/HostAddress.java index ba511cadb..c694772b2 100644 --- a/src/main/java/org/mariadb/jdbc/HostAddress.java +++ b/src/main/java/org/mariadb/jdbc/HostAddress.java @@ -164,13 +164,13 @@ private static HostAddress parseSimpleHostAddress(String str) { int ind = str.indexOf(']'); result.host = str.substring(1, ind); if (ind != (str.length() - 1) && str.charAt(ind + 1) == ':') { - result.port = Integer.parseInt(str.substring(ind + 2)); + result.port = getPort(str.substring(ind + 2)); } } else if (str.contains(":")) { /* Parse host:port */ String[] hostPort = str.split(":"); result.host = hostPort[0]; - result.port = Integer.parseInt(hostPort[1]); + result.port = getPort(hostPort[1]); } else { /* Just host name is given */ result.host = str; @@ -178,6 +178,14 @@ private static HostAddress parseSimpleHostAddress(String str) { return result; } + private static int getPort(String portString) { + try { + return Integer.parseInt(portString); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("Incorrect port value : " + portString); + } + } + private static HostAddress parseParameterHostAddress(String str) { HostAddress result = new HostAddress(); String[] array = str.split("(?=\\()|(?<=\\))"); @@ -191,7 +199,7 @@ private static HostAddress parseParameterHostAddress(String str) { if ("host".equals(key)) { result.host = value.replace("[", "").replace("]", ""); } else if ("port".equals(key)) { - result.port = Integer.parseInt(value); + result.port = getPort(value); } else if ("type".equals(key) && (value.equals(ParameterConstant.TYPE_MASTER) || value.equals(ParameterConstant.TYPE_SLAVE))) { result.type = value; diff --git a/src/main/java/org/mariadb/jdbc/UrlParser.java b/src/main/java/org/mariadb/jdbc/UrlParser.java index 1b46bb813..b0f2b4827 100644 --- a/src/main/java/org/mariadb/jdbc/UrlParser.java +++ b/src/main/java/org/mariadb/jdbc/UrlParser.java @@ -59,6 +59,7 @@ import org.mariadb.jdbc.internal.util.constant.ParameterConstant; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.regex.Matcher; @@ -92,7 +93,7 @@ * {@code jdbc:mariadb://address=(type=master)(host=master1),address=(port=3307)(type=slave)(host=slave1)/database?user=greg&password=pass}
*

*/ -public class UrlParser { +public class UrlParser implements Cloneable { private static final String DISABLE_MYSQL_URL = "disableMariaDbDriver"; private String database; @@ -121,7 +122,34 @@ protected UrlParser(String database, List addresses, Options option } } } + + DefaultOptions.optionCoherenceValidation(options); + StringBuilder sb = new StringBuilder(); + sb.append("jdbc:mariadb:"); + if (haMode != HaMode.NONE) { + sb.append(haMode).append(":"); + } + sb.append("//"); + boolean first = true; + for (HostAddress hostAddress : addresses) { + if (first) { + first = false; + } else { + sb.append(","); + } + sb.append("address=(host=").append(hostAddress.host).append(")") + .append("(port=").append(hostAddress.port).append(")"); + if (hostAddress.type != null) { + sb.append("(type=").append(hostAddress.type).append(")"); + } + } + + sb.append("/"); + if (database != null) sb.append(database); + DefaultOptions.propertyString(options, haMode, sb); + initialUrl = sb.toString(); multiMaster = loadMultiMasterValue(); + } /** @@ -203,7 +231,7 @@ private static void parseInternal(UrlParser urlParser, String url, Properties pr setDefaultHostAddressType(urlParser); } catch (IllegalArgumentException i) { - throw new SQLException(i.getMessage()); + throw new SQLException("error parsing url : " + i.getMessage(), i); } } @@ -229,12 +257,14 @@ private static void defineUrlParserParameters(UrlParser urlParser, Properties pr urlParser.database = matcher.group(2); urlParser.options = DefaultOptions.parse(urlParser.haMode, matcher.group(4), properties, urlParser.options); + DefaultOptions.optionCoherenceValidation(urlParser.options); if (urlParser.database != null && urlParser.database.isEmpty()) urlParser.database = null; } else { urlParser.database = null; urlParser.options = DefaultOptions.parse(urlParser.haMode, "", properties, urlParser.options); + DefaultOptions.optionCoherenceValidation(urlParser.options); } @@ -242,6 +272,7 @@ private static void defineUrlParserParameters(UrlParser urlParser, Properties pr urlParser.database = null; urlParser.options = DefaultOptions.parse(urlParser.haMode, "", properties, urlParser.options); + DefaultOptions.optionCoherenceValidation(urlParser.options); } @@ -340,7 +371,7 @@ public String getUsername() { return options.user; } - protected void setUsername(String username) { + public void setUsername(String username) { options.user = username; } @@ -348,7 +379,7 @@ public String getPassword() { return options.password; } - protected void setPassword(String password) { + public void setPassword(String password) { options.password = password; } @@ -356,7 +387,7 @@ public String getDatabase() { return database; } - protected void setDatabase(String database) { + public void setDatabase(String database) { this.database = database; } @@ -404,6 +435,14 @@ public boolean equals(Object parser) { && (getPassword() != null ? getPassword().equals(urlParser.getPassword()) : urlParser.getPassword() == null); } + @Override + public int hashCode() { + int result = options.password != null ? options.password.hashCode() : 0; + result = 31 * result + (options.user != null ? options.user.hashCode() : 0); + result = 31 * result + initialUrl.hashCode(); + return result; + } + private boolean loadMultiMasterValue() { if (haMode == HaMode.SEQUENTIAL || haMode == HaMode.REPLICATION @@ -425,4 +464,13 @@ private boolean loadMultiMasterValue() { public boolean isMultiMaster() { return multiMaster; } + + @Override + public Object clone() throws CloneNotSupportedException { + UrlParser tmpUrlParser = (UrlParser) super.clone(); + tmpUrlParser.options = (Options) options.clone(); + tmpUrlParser.addresses = new ArrayList<>(); + tmpUrlParser.addresses.addAll(addresses); + return tmpUrlParser; + } } \ No newline at end of file diff --git a/src/main/java/org/mariadb/jdbc/internal/util/DefaultOptions.java b/src/main/java/org/mariadb/jdbc/internal/util/DefaultOptions.java index c9a73ed2d..e80c4f7f3 100644 --- a/src/main/java/org/mariadb/jdbc/internal/util/DefaultOptions.java +++ b/src/main/java/org/mariadb/jdbc/internal/util/DefaultOptions.java @@ -54,10 +54,11 @@ import org.mariadb.jdbc.internal.util.constant.HaMode; +import java.lang.reflect.Field; import java.util.Properties; -@SuppressWarnings("ALL") public enum DefaultOptions { + /** * Database user name. */ @@ -67,6 +68,9 @@ public enum DefaultOptions { */ PASSWORD("password", "1.0.0"), + /** + * The connect timeout value, in milliseconds, or zero for no timeout. + */ CONNECT_TIMEOUT("connectTimeout", (Integer) null, 0, "1.1.8"), /** @@ -309,7 +313,7 @@ public enum DefaultOptions { *

* (legacy alias trustCertificateKeyStoreUrl) */ - TRUST_CERTIFICATE_KEYSTORE_URL("trustStore", "1.3.0"), + TRUSTSTORE("trustStore", "1.3.0"), /** * Password for the trusted root certificate file (similar to java System property "javax.net.ssl.trustStorePassword"). @@ -323,13 +327,13 @@ public enum DefaultOptions { * (similar to java System property "javax.net.ssl.keyStore", but ensure that only the private key's entries are used). * (legacy alias clientCertificateKeyStoreUrl) */ - CLIENT_CERTIFICATE_KEYSTORE_URL("keyStore", "1.3.0"), + KEYSTORE("keyStore", "1.3.0"), /** * Password for the client certificate keystore (similar to java System property "javax.net.ssl.keyStorePassword"). * (legacy alias clientCertificateKeyStorePassword) */ - CLIENT_CERTIFICATE_KEYSTORE_PASSWORD("keyStorePassword", "1.3.0"), + KEYSTORE_PASSWORD("keyStorePassword", "1.3.0"), /** * Password for the private key contain in client certificate keystore. @@ -448,7 +452,6 @@ public enum DefaultOptions { /** * When using ssl, driver check hostname against the server's identity as presented in the server's Certificate * (checking alternative names or certificate CN) to prevent man-in-the-middle attack. - * * This option permit to deactivate this validation. */ SSL_HOSTNAME_VERIFICATION("disableSslHostnameVerification", Boolean.FALSE, "2.1.0"), @@ -460,34 +463,33 @@ public enum DefaultOptions { */ USE_BULK_PROTOCOL("useBulkStmts", Boolean.TRUE, "2.1.0"); - private final String name; + private final String optionName; private final Object objType; private final Object defaultValue; private final Object minValue; private final Object maxValue; private final String implementationVersion; - protected Object value = null; - DefaultOptions(String name, String implementationVersion) { - this.name = name; + DefaultOptions(final String optionName, final String implementationVersion) { + this.optionName = optionName; this.implementationVersion = implementationVersion; - objType = String.class; - defaultValue = null; - minValue = null; - maxValue = null; + this.objType = String.class; + this.defaultValue = null; + this.minValue = null; + this.maxValue = null; } - DefaultOptions(String name, Boolean defaultValue, String implementationVersion) { - this.name = name; + DefaultOptions(final String optionName, final Boolean defaultValue, final String implementationVersion) { + this.optionName = optionName; this.objType = Boolean.class; this.defaultValue = defaultValue; this.implementationVersion = implementationVersion; - minValue = null; - maxValue = null; + this.minValue = null; + this.maxValue = null; } - DefaultOptions(String name, Integer defaultValue, Integer minValue, String implementationVersion) { - this.name = name; + DefaultOptions(final String optionName, final Integer defaultValue, final Integer minValue, final String implementationVersion) { + this.optionName = optionName; this.objType = Integer.class; this.defaultValue = defaultValue; this.minValue = minValue; @@ -495,8 +497,8 @@ public enum DefaultOptions { this.implementationVersion = implementationVersion; } - DefaultOptions(String name, Long defaultValue, Long minValue, String implementationVersion) { - this.name = name; + DefaultOptions(final String optionName, final Long defaultValue, final Long minValue, final String implementationVersion) { + this.optionName = optionName; this.objType = Long.class; this.defaultValue = defaultValue; this.minValue = minValue; @@ -505,8 +507,8 @@ public enum DefaultOptions { } - DefaultOptions(String name, Integer[] defaultValue, Integer minValue, String implementationVersion) { - this.name = name; + DefaultOptions(final String optionName, final Integer[] defaultValue, final Integer minValue, final String implementationVersion) { + this.optionName = optionName; this.objType = Integer.class; this.defaultValue = defaultValue; this.minValue = minValue; @@ -514,17 +516,31 @@ public enum DefaultOptions { this.implementationVersion = implementationVersion; } - public static Options defaultValues(HaMode haMode) { + public String getOptionName() { + return optionName; + } + + public static Options defaultValues(final HaMode haMode) { return parse(haMode, "", new Properties()); } - public static void parse(HaMode haMode, String urlParameters, Options options) { + /** + * Parse additional properties. + * + * @param haMode current haMode. + * @param urlParameters options defined in url + * @param options initial options + */ + public static void parse(final HaMode haMode, final String urlParameters, final Options options) { Properties prop = new Properties(); parse(haMode, urlParameters, prop, options); + optionCoherenceValidation(options); } - private static Options parse(HaMode haMode, String urlParameters, Properties properties) { - return parse(haMode, urlParameters, properties, null); + private static Options parse(final HaMode haMode, final String urlParameters, final Properties properties) { + Options options = parse(haMode, urlParameters, properties, null); + optionCoherenceValidation(options); + return options; } /** @@ -536,7 +552,7 @@ private static Options parse(HaMode haMode, String urlParameters, Properties pro * @param options initial options * @return options */ - public static Options parse(HaMode haMode, String urlParameters, Properties properties, Options options) { + public static Options parse(final HaMode haMode, final String urlParameters, final Properties properties, final Options options) { if (urlParameters != null && !urlParameters.isEmpty()) { String[] parameters = urlParameters.split("&"); for (String parameter : parameters) { @@ -556,73 +572,79 @@ public static Options parse(HaMode haMode, String urlParameters, Properties prop return parse(haMode, properties, options); } - private static Options parse(HaMode haMode, Properties properties, Options paramOptions) { - Options options = paramOptions != null ? paramOptions : new Options(); + private static Options parse(final HaMode haMode, final Properties properties, final Options paramOptions) { + final Options options = paramOptions != null ? paramOptions : new Options(); try { - for (DefaultOptions o : DefaultOptions.values()) { - - String propertyValue = handleAlias(o.name, properties); - - if (propertyValue != null) { + //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 String key : properties.stringPropertyNames()) { + final String propertyValue = properties.getProperty(key); + final DefaultOptions o = OptionUtils.OPTIONS_MAP.get(key); + if (o != null && propertyValue != null) { + final Field field = Options.class.getField(o.optionName); if (o.objType.equals(String.class)) { - Options.class.getField(o.name).set(options, propertyValue); + field.set(options, propertyValue); } else if (o.objType.equals(Boolean.class)) { switch (propertyValue.toLowerCase()) { case "": case "1": case "true": - Options.class.getField(o.name).set(options, Boolean.TRUE); + field.set(options, Boolean.TRUE); break; case "0": case "false": - Options.class.getField(o.name).set(options, Boolean.FALSE); + field.set(options, Boolean.FALSE); break; default: - throw new IllegalArgumentException("Optional parameter " + o.name + throw new IllegalArgumentException("Optional parameter " + o.optionName + " must be boolean (true/false or 0/1) was \"" + propertyValue + "\""); } } else if (o.objType.equals(Integer.class)) { try { - Integer value = Integer.parseInt(propertyValue); + final Integer value = Integer.parseInt(propertyValue); assert o.minValue != null; assert o.maxValue != null; if (value.compareTo((Integer) o.minValue) < 0 || value.compareTo((Integer) o.maxValue) > 0) { - throw new IllegalArgumentException("Optional parameter " + o.name + " must be greater or equal to " + o.minValue + throw new IllegalArgumentException("Optional parameter " + o.optionName + + " must be greater or equal to " + o.minValue + (((Integer) o.maxValue != Integer.MAX_VALUE) ? " and smaller than " + o.maxValue : " ") + ", was \"" + propertyValue + "\""); } - Options.class.getField(o.name).set(options, value); + field.set(options, value); } catch (NumberFormatException n) { - throw new IllegalArgumentException("Optional parameter " + o.name + " must be Integer, was \"" + propertyValue + "\""); + throw new IllegalArgumentException("Optional parameter " + o.optionName + + " must be Integer, was \"" + propertyValue + "\""); } } else if (o.objType.equals(Long.class)) { try { - Long value = Long.parseLong(propertyValue); + final Long value = Long.parseLong(propertyValue); assert o.minValue != null; assert o.maxValue != null; if (value.compareTo((Long) o.minValue) < 0 || value.compareTo((Long) o.maxValue) > 0) { - throw new IllegalArgumentException("Optional parameter " + o.name + " must be greater or equal to " + o.minValue + throw new IllegalArgumentException("Optional parameter " + o.optionName + + " must be greater or equal to " + o.minValue + (((Long) o.maxValue != Long.MAX_VALUE) ? " and smaller than " + o.maxValue : " ") + ", was \"" + propertyValue + "\""); } - Options.class.getField(o.name).set(options, value); + field.set(options, value); } catch (NumberFormatException n) { - throw new IllegalArgumentException("Optional parameter " + o.name + " must be Long, was \"" + propertyValue + "\""); - } - } - } else { - if (paramOptions == null) { - if (o.defaultValue instanceof Integer[]) { - Options.class.getField(o.name).set(options, ((Integer[]) o.defaultValue)[haMode.ordinal()]); - } else { - Options.class.getField(o.name).set(options, o.defaultValue); + throw new IllegalArgumentException("Optional parameter " + o.optionName + + " must be Long, was \"" + propertyValue + "\""); } } } } + + //special case : field with multiple default according to HA_MODE + if (options.socketTimeout == null) { + options.socketTimeout = ((Integer[]) SOCKET_TIMEOUT.defaultValue)[haMode.ordinal()]; + } + } catch (NoSuchFieldException | IllegalAccessException n) { n.printStackTrace(); } catch (SecurityException s) { @@ -630,56 +652,14 @@ private static Options parse(HaMode haMode, Properties properties, Options param throw new IllegalArgumentException("Security too restrictive : " + s.getMessage()); } - optionCoherenceValidation(options); - return options; } - /** - * If properties with alias are set, will be used to set value. - * - * @param optionName current option name - * @param properties list of properties - * @return properties or alias value if existant - */ - private static String handleAlias(String optionName, Properties properties) { - String propertyValue = properties.getProperty(optionName); - - if (propertyValue == null) { - switch (optionName) { - case "createDatabaseIfNotExist": - propertyValue = properties.getProperty("createDB"); - break; - case "useSsl": - propertyValue = properties.getProperty("useSSL"); - break; - case "profileSql": - propertyValue = properties.getProperty("profileSQL"); - break; - case "enabledSslCipherSuites": - propertyValue = properties.getProperty("enabledSSLCipherSuites"); - break; - case "trustStorePassword": - propertyValue = properties.getProperty("trustCertificateKeyStorePassword"); - break; - case "trustStore": - propertyValue = properties.getProperty("trustCertificateKeyStoreUrl"); - break; - case "keyStorePassword": - propertyValue = properties.getProperty("clientCertificateKeyStorePassword"); - break; - case "keyStore": - propertyValue = properties.getProperty("clientCertificateKeyStoreUrl"); - break; - default: - //no alias - } - } - return propertyValue; - } - - private static void optionCoherenceValidation(Options options) { + * Option initialisation end : set option value to a coherent state. + * @param options options + */ + public static void optionCoherenceValidation(final Options options) { //disable use server prepare id using client rewrite if (options.rewriteBatchedStatements) { @@ -698,4 +678,41 @@ private static void optionCoherenceValidation(Options options) { } + /** + * Generate parameter String equivalent to options. + * + * @param options options + * @param haMode high availability Mode + * @param sb String builder + */ + public static void propertyString(final Options options, final HaMode haMode, final StringBuilder sb) { + try { + boolean first = true; + for (DefaultOptions o : DefaultOptions.values()) { + final Object value = Options.class.getField(o.optionName).get(options); + + if (value != null && !value.equals(o.defaultValue)) { + if (first) { + first = false; + sb.append('?'); + } else { + sb.append('&'); + } + sb.append(o.optionName) + .append('='); + if (o.objType.equals(String.class)) { + sb.append((String) value); + } else if (o.objType.equals(Boolean.class)) { + sb.append(((Boolean) value).toString()); + } else if (o.objType.equals(Integer.class)) { + sb.append((Integer) value); + } else if (o.objType.equals(Long.class)) { + sb.append((Long) value); + } + } + } + } catch (NoSuchFieldException | IllegalAccessException n) { + n.printStackTrace(); + } + } } diff --git a/src/main/java/org/mariadb/jdbc/internal/util/OptionUtils.java b/src/main/java/org/mariadb/jdbc/internal/util/OptionUtils.java new file mode 100644 index 000000000..9c66266b2 --- /dev/null +++ b/src/main/java/org/mariadb/jdbc/internal/util/OptionUtils.java @@ -0,0 +1,25 @@ +package org.mariadb.jdbc.internal.util; + +import java.util.HashMap; +import java.util.Map; + +public class OptionUtils { + + public static final Map OPTIONS_MAP; + + static { + OPTIONS_MAP = new HashMap<>(); + for (DefaultOptions defaultOption : DefaultOptions.values()) { + OPTIONS_MAP.put(defaultOption.getOptionName(), defaultOption); + } + //add alias + OPTIONS_MAP.put("createDB", DefaultOptions.CREATE_DATABASE_IF_NOT_EXISTS); + OPTIONS_MAP.put("useSSL", DefaultOptions.USE_SSL); + OPTIONS_MAP.put("profileSQL", DefaultOptions.PROFILESQL); + OPTIONS_MAP.put("enabledSSLCipherSuites", DefaultOptions.ENABLED_SSL_CIPHER_SUITES); + OPTIONS_MAP.put("trustCertificateKeyStorePassword", DefaultOptions.TRUST_CERTIFICATE_KEYSTORE_PASSWORD); + OPTIONS_MAP.put("trustCertificateKeyStoreUrl", DefaultOptions.TRUSTSTORE); + OPTIONS_MAP.put("clientCertificateKeyStorePassword", DefaultOptions.KEYSTORE_PASSWORD); + OPTIONS_MAP.put("clientCertificateKeyStoreUrl", DefaultOptions.KEYSTORE); + } +} diff --git a/src/main/java/org/mariadb/jdbc/internal/util/Options.java b/src/main/java/org/mariadb/jdbc/internal/util/Options.java index dea682094..e2a17ec78 100644 --- a/src/main/java/org/mariadb/jdbc/internal/util/Options.java +++ b/src/main/java/org/mariadb/jdbc/internal/util/Options.java @@ -55,7 +55,8 @@ import java.lang.reflect.Field; @SuppressWarnings("ConstantConditions") -public class Options { +public class Options implements Cloneable { + //standard options public String user; public String password; @@ -69,15 +70,15 @@ public class Options { public String keyStorePassword; public String keyPassword; public String enabledSslProtocolSuites; - public boolean useFractionalSeconds; + public boolean useFractionalSeconds = true; public boolean pinGlobalTxToPhysicalConnection; public String socketFactory; public Integer connectTimeout; public String pipe; public String localSocket; public String sharedMemory; - public boolean tcpNoDelay; - public boolean tcpKeepAlive; + public boolean tcpNoDelay = true; + public boolean tcpKeepAlive = true; public Integer tcpRcvBuf; public Integer tcpSndBuf; public boolean tcpAbortiveClose; @@ -92,47 +93,47 @@ public class Options { public boolean useSsl; public String enabledSslCipherSuites; public String sessionVariables; - public boolean tinyInt1isBit; - public boolean yearIsDateType; + public boolean tinyInt1isBit = true; + public boolean yearIsDateType = true; public boolean createDatabaseIfNotExist; public String serverTimezone; - public boolean nullCatalogMeansCurrent; - public boolean dumpQueriesOnException; + public boolean nullCatalogMeansCurrent = true; + public boolean dumpQueriesOnException = true; public boolean useOldAliasMetadataBehavior; - public boolean allowLocalInfile; - public boolean cachePrepStmts; - public Integer prepStmtCacheSize; - public Integer prepStmtCacheSqlLimit; - public boolean useLegacyDatetimeCode; + public boolean allowLocalInfile = true; + public boolean cachePrepStmts = true; + public int prepStmtCacheSize = 250; + public Integer prepStmtCacheSqlLimit = 2048; + public boolean useLegacyDatetimeCode = true; public boolean maximizeMysqlCompatibility; public boolean useServerPrepStmts; - public boolean continueBatchOnError; - public boolean jdbcCompliantTruncation; - public boolean cacheCallableStmts; - public Integer callableStmtCacheSize; + public boolean continueBatchOnError = true; + public boolean jdbcCompliantTruncation = true; + public boolean cacheCallableStmts = true; + public int callableStmtCacheSize = 150; public String connectionAttributes; public Boolean useBatchMultiSend; - public int useBatchMultiSendNumber; + public int useBatchMultiSendNumber = 100; public Boolean usePipelineAuth; - public boolean killFetchStmtOnClose; + public boolean killFetchStmtOnClose = true; public boolean enablePacketDebug; - public boolean useBulkStmts; + public boolean useBulkStmts = true; public boolean disableSslHostnameVerification; //logging options public boolean log; public boolean profileSql; - public Integer maxQuerySizeToLog; + public int maxQuerySizeToLog = 1024; public Long slowQueryThresholdNanos; //HA options public boolean assureReadOnly; public boolean autoReconnect; public boolean failOnReadOnly; - public int retriesAllDown; + public int retriesAllDown = 120; public int validConnectionTimeout; - public int loadBalanceBlacklistTimeout; - public int failoverLoopRetries; + public int loadBalanceBlacklistTimeout = 50; + public int failoverLoopRetries = 120; @Override public String toString() { @@ -220,7 +221,7 @@ public boolean equals(Object obj) { return false; } if (socketFactory != null ? !socketFactory.equals(opt.socketFactory) : opt.socketFactory != null) return false; - if (connectTimeout != opt.connectTimeout) return false; + if (connectTimeout != null ? !connectTimeout.equals(opt.connectTimeout) : opt.connectTimeout != null) return false; if (pipe != null ? !pipe.equals(opt.pipe) : opt.pipe != null) return false; if (localSocket != null ? !localSocket.equals(opt.localSocket) : opt.localSocket != null) return false; if (sharedMemory != null ? !sharedMemory.equals(opt.sharedMemory) : opt.sharedMemory != null) return false; @@ -239,19 +240,17 @@ public boolean equals(Object obj) { } if (sessionVariables != null ? !sessionVariables.equals(opt.sessionVariables) : opt.sessionVariables != null) return false; if (serverTimezone != null ? !serverTimezone.equals(opt.serverTimezone) : opt.serverTimezone != null) return false; - if (prepStmtCacheSize != null ? !prepStmtCacheSize.equals(opt.prepStmtCacheSize) : opt.prepStmtCacheSize != null) return false; + if (prepStmtCacheSize != opt.prepStmtCacheSize) return false; if (prepStmtCacheSqlLimit != null ? !prepStmtCacheSqlLimit.equals(opt.prepStmtCacheSqlLimit) : opt.prepStmtCacheSqlLimit != null) { return false; } - if (callableStmtCacheSize != null ? !callableStmtCacheSize.equals(opt.callableStmtCacheSize) : opt.callableStmtCacheSize != null) { - return false; - } + if (callableStmtCacheSize != opt.callableStmtCacheSize) return false; if (connectionAttributes != null ? !connectionAttributes.equals(opt.connectionAttributes) : opt.connectionAttributes != null) { return false; } if (useBatchMultiSend != null ? !useBatchMultiSend.equals(opt.useBatchMultiSend) : opt.useBatchMultiSend != null) return false; if (usePipelineAuth != null ? !usePipelineAuth.equals(opt.usePipelineAuth) : opt.usePipelineAuth != null) return false; - if (maxQuerySizeToLog != null ? !maxQuerySizeToLog.equals(opt.maxQuerySizeToLog) : opt.maxQuerySizeToLog != null) return false; + if (maxQuerySizeToLog != opt.maxQuerySizeToLog) return false; if (slowQueryThresholdNanos != null ? !slowQueryThresholdNanos.equals(opt.slowQueryThresholdNanos) : opt.slowQueryThresholdNanos != null) { return false; } @@ -274,7 +273,7 @@ public int hashCode() { result = 31 * result + (useFractionalSeconds ? 1 : 0); result = 31 * result + (pinGlobalTxToPhysicalConnection ? 1 : 0); result = 31 * result + (socketFactory != null ? socketFactory.hashCode() : 0); - result = 31 * result + connectTimeout; + result = 31 * result + (connectTimeout != null ? connectTimeout.hashCode() : 0); result = 31 * result + (pipe != null ? pipe.hashCode() : 0); result = 31 * result + (localSocket != null ? localSocket.hashCode() : 0); result = 31 * result + (sharedMemory != null ? sharedMemory.hashCode() : 0); @@ -302,7 +301,7 @@ public int hashCode() { result = 31 * result + (useOldAliasMetadataBehavior ? 1 : 0); result = 31 * result + (allowLocalInfile ? 1 : 0); result = 31 * result + (cachePrepStmts ? 1 : 0); - result = 31 * result + (prepStmtCacheSize != null ? prepStmtCacheSize.hashCode() : 0); + result = 31 * result + prepStmtCacheSize; result = 31 * result + (prepStmtCacheSqlLimit != null ? prepStmtCacheSqlLimit.hashCode() : 0); result = 31 * result + (useLegacyDatetimeCode ? 1 : 0); result = 31 * result + (maximizeMysqlCompatibility ? 1 : 0); @@ -310,7 +309,7 @@ public int hashCode() { result = 31 * result + (continueBatchOnError ? 1 : 0); result = 31 * result + (jdbcCompliantTruncation ? 1 : 0); result = 31 * result + (cacheCallableStmts ? 1 : 0); - result = 31 * result + (callableStmtCacheSize != null ? callableStmtCacheSize.hashCode() : 0); + result = 31 * result + callableStmtCacheSize; result = 31 * result + (connectionAttributes != null ? connectionAttributes.hashCode() : 0); result = 31 * result + (useBatchMultiSend != null ? useBatchMultiSend.hashCode() : 0); result = 31 * result + useBatchMultiSendNumber; @@ -321,7 +320,7 @@ public int hashCode() { result = 31 * result + (disableSslHostnameVerification ? 1 : 0); result = 31 * result + (log ? 1 : 0); result = 31 * result + (profileSql ? 1 : 0); - result = 31 * result + (maxQuerySizeToLog != null ? maxQuerySizeToLog.hashCode() : 0); + result = 31 * result + maxQuerySizeToLog; result = 31 * result + (slowQueryThresholdNanos != null ? slowQueryThresholdNanos.hashCode() : 0); result = 31 * result + (assureReadOnly ? 1 : 0); result = 31 * result + (autoReconnect ? 1 : 0); diff --git a/src/test/java/org/mariadb/jdbc/internal/util/DefaultOptionsTest.java b/src/test/java/org/mariadb/jdbc/internal/util/DefaultOptionsTest.java new file mode 100644 index 000000000..ab57ec565 --- /dev/null +++ b/src/test/java/org/mariadb/jdbc/internal/util/DefaultOptionsTest.java @@ -0,0 +1,131 @@ +package org.mariadb.jdbc.internal.util; + +import org.junit.Test; +import org.mariadb.jdbc.internal.util.constant.HaMode; + +import java.lang.reflect.Field; +import java.util.Properties; + +import static org.junit.Assert.*; + +public class DefaultOptionsTest { + + @Test + public void parseDefault() throws Exception { + //check that default option object correspond to default + Options option = new Options(); + DefaultOptions.optionCoherenceValidation(option); + for (HaMode haMode : HaMode.values()) { + Options defaultOption = DefaultOptions.parse(haMode, "", new Properties(), null); + DefaultOptions.optionCoherenceValidation(defaultOption); + for (DefaultOptions o : DefaultOptions.values()) { + Field field = Options.class.getField(o.getOptionName()); + assertEquals("field :" + field.getName(), field.get(o.defaultValues(HaMode.NONE)), field.get(option)); + assertEquals("field :" + field.getName(), field.get(o.defaultValues(haMode)), field.get(defaultOption)); + } + } + } + + + @Test + public void parseOption() throws Exception { + Options option = new Options(); + String param = generateParam(); + for (HaMode haMode : HaMode.values()) { + Options resultOptions = DefaultOptions.parse(haMode, param, new Properties(), null); + for (Field field : Options.class.getFields()) { + switch (field.getType().getName()) { + case "java.lang.String": + assertEquals("field " + field.getName() + " value error for param" + param, + field.get(resultOptions), field.getName() + "1"); + break; + case "int": + assertEquals("field " + field.getName() + " value error for param" + param, + field.getInt(resultOptions), 9999); + break; + case "java.lang.Integer": + assertEquals("field " + field.getName() + " value error for param" + param, + ((Integer) field.get(resultOptions)).intValue(), 9999); + break; + case "java.lang.Long": + assertEquals("field " + field.getName() + " value error for param" + param, + ((Long) field.get(resultOptions)).intValue(), 9999); + break; + case "java.lang.Boolean": + Boolean bool = (Boolean) field.get(option); + if (bool == null) { + assertTrue("field " + field.getName() + " value error for param" + param, + (Boolean) field.get(resultOptions)); + } else { + assertEquals("field " + field.getName() + " value error for param" + param, + (Boolean) field.get(resultOptions), !bool); + } + break; + case "boolean": + System.out.println(field.getName() + ": " + field.getBoolean(resultOptions) + " " + field.get(resultOptions)); + assertEquals("field " + field.getName() + " value error for param" + param, + field.getBoolean(resultOptions), !field.getBoolean(option)); + break; + default: + fail("type not used normally ! " + field.getType().getName()); + } + } + } + } + + private String generateParam() throws IllegalAccessException { + Options option = new Options(); + //check option url settings + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (DefaultOptions defaultOption : DefaultOptions.values()) { + for (Field field : Options.class.getFields()) { + if (defaultOption.getOptionName().equals(field.getName())) { //for having same order + if (first) { + first = false; + } else { + sb.append("&"); + } + sb.append(field.getName()).append("="); + switch (field.getType().getName()) { + case "java.lang.String": + sb.append(field.getName()).append("1"); + break; + case "int": + case "java.lang.Integer": + case "java.lang.Long": + sb.append("9999"); + break; + case "java.lang.Boolean": + Boolean bool = (Boolean) field.get(option); + if (bool == null) { + sb.append("true"); + } else { + sb.append((!((Boolean) field.get(option)))); + } + break; + case "boolean": + sb.append(!(boolean) (field.get(option))); + break; + default: + fail("type not used normally ! : " + field.getType().getName()); + } + } + } + } + return sb.toString(); + } + + @Test + public void buildTest() throws Exception { + String param = generateParam(); + for (HaMode haMode : HaMode.values()) { + Options resultOptions = DefaultOptions.parse(haMode, param, new Properties(), null); + StringBuilder sb = new StringBuilder(); + DefaultOptions.propertyString(resultOptions, haMode, sb); + + assertEquals("?" + param, sb.toString()); + } + } + +} \ No newline at end of file