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

org.mariadb.jdbc.UrlParser Maven / Gradle / Ivy

There is a newer version: 3.4.1
Show newest version
/*
 *
 * MariaDB Client for Java
 *
 * Copyright (c) 2012-2014 Monty Program Ab.
 * Copyright (c) 2015-2019 MariaDB Ab.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to Monty Program Ab [email protected].
 *
 * This particular MariaDB Client for Java file is work
 * derived from a Drizzle-JDBC. Drizzle-JDBC file which is covered by subject to
 * the following copyright and notice provisions:
 *
 * Copyright (c) 2009-2011, Marcus Eriksson
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this list
 * of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * Neither the name of the driver nor the names of its contributors may not be
 * used to endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS  AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 */

package org.mariadb.jdbc;

import org.mariadb.jdbc.credential.*;
import org.mariadb.jdbc.internal.logging.*;
import org.mariadb.jdbc.internal.util.constant.*;
import org.mariadb.jdbc.util.*;

import java.sql.*;
import java.util.*;
import java.util.regex.*;

/**
 * parse and verification of URL.
 *
 * 

basic syntax :
* {@code * jdbc:(mysql|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 UrlParser implements Cloneable { private static final String DISABLE_MYSQL_URL = "disableMariaDbDriver"; private static final Pattern URL_PARAMETER = Pattern.compile("(\\/([^\\?]*))?(\\?(.+))*", Pattern.DOTALL); private static final Pattern AWS_PATTERN = Pattern.compile("(.+)\\.([a-z0-9\\-]+\\.rds\\.amazonaws\\.com)", Pattern.CASE_INSENSITIVE); private String database; private Options options = null; private List addresses; private HaMode haMode; private String initialUrl; private boolean multiMaster; private CredentialPlugin credentialPlugin; private UrlParser() {} /** * Constructor. * * @param database database * @param addresses list of hosts * @param options connection option * @param haMode High availability mode * @throws SQLException if credential plugin cannot be loaded */ public UrlParser(String database, List addresses, Options options, HaMode haMode) throws SQLException { this.options = options; this.database = database; this.addresses = addresses; this.haMode = haMode; if (haMode == HaMode.AURORA) { for (HostAddress hostAddress : addresses) { hostAddress.type = null; } } else { for (HostAddress hostAddress : addresses) { if (hostAddress.type == null) { hostAddress.type = ParameterConstant.TYPE_MASTER; } } } this.credentialPlugin = CredentialPluginLoader.get(options.credentialType); DefaultOptions.postOptionProcess(options, credentialPlugin); setInitialUrl(); loadMultiMasterValue(); } /** * 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(DISABLE_MYSQL_URL))); } public static UrlParser 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 UrlParser parse(final String url, Properties prop) throws SQLException { if (url != null && (url.startsWith("jdbc:mariadb:") || url.startsWith("jdbc:mysql:") && !url.contains(DISABLE_MYSQL_URL))) { UrlParser urlParser = new UrlParser(); parseInternal(urlParser, url, (prop == null) ? new Properties() : prop); return urlParser; } return null; } /** * Parses the connection URL in order to set the UrlParser instance with all the information * provided through the URL. * * @param urlParser object instance in which all data from the connection url is stored * @param url connection URL * @param properties properties * @throws SQLException if format is incorrect */ private static void parseInternal(UrlParser urlParser, String url, Properties properties) throws SQLException { try { urlParser.initialUrl = url; int separator = url.indexOf("//"); if (separator == -1) { throw new IllegalArgumentException( "url parsing error : '//' is not present in the url " + url); } urlParser.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 > -1) || (dbIndex > paramIndex && paramIndex < 0)) { hostAddressesString = urlSecondPart.substring(0, dbIndex); additionalParameters = urlSecondPart.substring(dbIndex); } else { hostAddressesString = urlSecondPart; additionalParameters = null; } defineUrlParserParameters(urlParser, properties, hostAddressesString, additionalParameters); setDefaultHostAddressType(urlParser); urlParser.loadMultiMasterValue(); } catch (IllegalArgumentException i) { throw new SQLException("error parsing url : " + i.getMessage(), i); } } /* Parse ConnectorJ compatible urls jdbc:[mariadb|mysql]://host:port/database Example: jdbc:mariadb://localhost:3306/test?user=root&password=passwd */ /** * Sets the parameters of the UrlParser instance: addresses, database and options. It parses * through the additional parameters given in order to extract the database and the options for * the connection. * * @param urlParser object instance in which all data from the connection URL is stored * @param properties properties * @param hostAddressesString string that holds all the host addresses * @param additionalParameters string that holds all parameters defined for the connection * @throws SQLException if credential plugin cannot be loaded */ private static void defineUrlParserParameters( UrlParser urlParser, Properties properties, String hostAddressesString, String additionalParameters) throws SQLException { if (additionalParameters != null) { //noinspection Annotator Matcher matcher = URL_PARAMETER.matcher(additionalParameters); if (matcher.find()) { urlParser.database = matcher.group(2); urlParser.options = DefaultOptions.parse(urlParser.haMode, matcher.group(4), properties, 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); } } else { urlParser.database = null; urlParser.options = DefaultOptions.parse(urlParser.haMode, "", properties, urlParser.options); } urlParser.credentialPlugin = CredentialPluginLoader.get(urlParser.options.credentialType); DefaultOptions.postOptionProcess(urlParser.options, urlParser.credentialPlugin); LoggerFactory.init( urlParser.options.log || urlParser.options.profileSql || urlParser.options.slowQueryThresholdNanos != null); urlParser.addresses = HostAddress.parse(hostAddressesString, urlParser.haMode); } 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).toUpperCase(Locale.ROOT); if ("FAILOVER".equals(haModeString)) { haModeString = "LOADBALANCE"; } return HaMode.valueOf(haModeString); } catch (IllegalArgumentException i) { throw new IllegalArgumentException( "wrong failover parameter format in connection String " + url); } } private static void setDefaultHostAddressType(UrlParser urlParser) { if (urlParser.haMode == HaMode.AURORA) { for (HostAddress hostAddress : urlParser.addresses) { hostAddress.type = null; } } else { for (HostAddress hostAddress : urlParser.addresses) { if (hostAddress.type == null) { hostAddress.type = ParameterConstant.TYPE_MASTER; } } } } private void setInitialUrl() { StringBuilder sb = new StringBuilder(); sb.append("jdbc:mariadb:"); if (haMode != HaMode.NONE) { sb.append(haMode.toString().toLowerCase(Locale.ROOT)).append(":"); } sb.append("//"); for (int i = 0; i < addresses.size(); i++) { HostAddress hostAddress = addresses.get(i); if (i > 0) { 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(); } /** * Permit to set parameters not forced. if options useBatchMultiSend and usePipelineAuth are not * explicitly set in connection string, value will default to true or false according if aurora * detection. * * @return UrlParser for easy testing */ public UrlParser auroraPipelineQuirks() { // Aurora has issue with pipelining, depending on network speed. // Driver must rely on information provided by user : hostname if dns, and HA mode.

boolean disablePipeline = isAurora(); if (options.useBatchMultiSend == null) { options.useBatchMultiSend = disablePipeline ? Boolean.FALSE : Boolean.TRUE; } if (options.usePipelineAuth == null) { options.usePipelineAuth = disablePipeline ? Boolean.FALSE : Boolean.TRUE; } return this; } /** * Detection of Aurora. * *

Aurora rely on MySQL, then cannot be identified by protocol. But Aurora doesn't permit some * behaviour normally working with MySQL : pipelining. So Driver must identified if server is * Aurora to disable pipeline options that are enable by default. * * @return true if aurora. */ public boolean isAurora() { if (haMode == HaMode.AURORA) { return true; } if (addresses != null) { for (HostAddress hostAddress : addresses) { Matcher matcher = AWS_PATTERN.matcher(hostAddress.host); if (matcher.find()) { return true; } } } return false; } /** * Parse url connection string. * * @param url connection string * @throws SQLException if url format is incorrect */ public void parseUrl(String url) throws SQLException { if (acceptsUrl(url)) { parseInternal(this, url, new Properties()); } } public String getUsername() { return options.user; } public void setUsername(String username) { options.user = username; } public String getPassword() { return options.password; } public void setPassword(String password) { options.password = password; } public String getDatabase() { return database; } public void setDatabase(String database) { this.database = database; } public List getHostAddresses() { return this.addresses; } public Options getOptions() { return options; } protected void setProperties(String urlParameters) { DefaultOptions.parse(this.haMode, urlParameters, this.options); setInitialUrl(); } public CredentialPlugin getCredentialPlugin() { return credentialPlugin; } /** * ToString implementation. * * @return String value */ public String toString() { return initialUrl; } public String getInitialUrl() { return initialUrl; } public HaMode getHaMode() { return haMode; } @Override public boolean equals(Object parser) { if (this == parser) { return true; } if (!(parser instanceof UrlParser)) { return false; } UrlParser urlParser = (UrlParser) parser; return (initialUrl != null ? initialUrl.equals(urlParser.getInitialUrl()) : urlParser.getInitialUrl() == null) && (getUsername() != null ? getUsername().equals(urlParser.getUsername()) : urlParser.getUsername() == null) && (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 void loadMultiMasterValue() { if (haMode == HaMode.SEQUENTIAL || haMode == HaMode.REPLICATION || haMode == HaMode.LOADBALANCE) { boolean firstMaster = false; for (HostAddress host : addresses) { if (host.type.equals(ParameterConstant.TYPE_MASTER)) { if (firstMaster) { multiMaster = true; return; } else { firstMaster = true; } } } } multiMaster = false; } 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy