software.amazon.jdbc.dialect.DialectManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-advanced-jdbc-wrapper Show documentation
Show all versions of aws-advanced-jdbc-wrapper Show documentation
Amazon Web Services (AWS) Advanced JDBC Wrapper
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package software.amazon.jdbc.dialect;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.AwsWrapperProperty;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.PluginService;
import software.amazon.jdbc.util.CacheMap;
import software.amazon.jdbc.util.ConnectionUrlParser;
import software.amazon.jdbc.util.Messages;
import software.amazon.jdbc.util.RdsUrlType;
import software.amazon.jdbc.util.RdsUtils;
import software.amazon.jdbc.util.StringUtils;
import software.amazon.jdbc.util.Utils;
public class DialectManager implements DialectProvider {
private static final Logger LOGGER = Logger.getLogger(DialectManager.class.getName());
protected static Dialect customDialect;
public static final AwsWrapperProperty DIALECT = new AwsWrapperProperty(
"wrapperDialect", "",
"A unique identifier for the supported database dialect.");
/**
* Every Dialect implementation SHOULD BE stateless!!!
* Dialect objects are shared between different connections.
*/
protected static final Map knownDialectsByCode =
new HashMap() {
{
put(DialectCodes.MYSQL, new MysqlDialect());
put(DialectCodes.PG, new PgDialect());
put(DialectCodes.MARIADB, new MariaDbDialect());
put(DialectCodes.RDS_MYSQL, new RdsMysqlDialect());
put(DialectCodes.RDS_MULTI_AZ_MYSQL_CLUSTER, new RdsMultiAzDbClusterMysqlDialect());
put(DialectCodes.RDS_PG, new RdsPgDialect());
put(DialectCodes.RDS_MULTI_AZ_PG_CLUSTER, new RdsMultiAzDbClusterPgDialect());
put(DialectCodes.AURORA_MYSQL, new AuroraMysqlDialect());
put(DialectCodes.AURORA_PG, new AuroraPgDialect());
put(DialectCodes.UNKNOWN, new UnknownDialect());
}
};
/**
* In order to simplify dialect detection, there's an internal host-to-dialect cache.
* The cache contains host endpoints and identified dialect. Cache expiration time
* is defined by the variable below.
*/
protected static final long ENDPOINT_CACHE_EXPIRATION = TimeUnit.HOURS.toNanos(24);
// Map of host name, or url, by dialect code.
protected static final CacheMap knownEndpointDialects = new CacheMap<>();
private final RdsUtils rdsHelper = new RdsUtils();
private final ConnectionUrlParser connectionUrlParser = new ConnectionUrlParser();
private boolean canUpdate = false;
private Dialect dialect = null;
private String dialectCode;
private PluginService pluginService;
public DialectManager(PluginService pluginService) {
this.pluginService = pluginService;
}
public static void setCustomDialect(final @NonNull Dialect dialect) {
customDialect = dialect;
}
public static void resetCustomDialect() {
customDialect = null;
}
public static void resetEndpointCache() {
knownEndpointDialects.clear();
}
@Override
public Dialect getDialect(
final @NonNull String driverProtocol,
final @NonNull String url,
final @NonNull Properties props)
throws SQLException {
this.canUpdate = false;
this.dialect = null;
if (customDialect != null) {
this.dialectCode = DialectCodes.CUSTOM;
this.dialect = customDialect;
this.logCurrentDialect();
return this.dialect;
}
final String userDialectSetting = DIALECT.getString(props);
final String dialectCode = !StringUtils.isNullOrEmpty(userDialectSetting)
? userDialectSetting
: knownEndpointDialects.get(url);
if (!StringUtils.isNullOrEmpty(dialectCode)) {
final Dialect userDialect = knownDialectsByCode.get(dialectCode);
if (userDialect != null) {
this.dialectCode = dialectCode;
this.dialect = userDialect;
this.logCurrentDialect();
return userDialect;
} else {
throw new SQLException(
Messages.get("DialectManager.unknownDialectCode", new Object[] {dialectCode}));
}
}
if (StringUtils.isNullOrEmpty(driverProtocol)) {
throw new IllegalArgumentException("protocol");
}
String host = url;
final List hosts = this.connectionUrlParser.getHostsFromConnectionUrl(url, true,
() -> pluginService.getHostSpecBuilder());
if (!Utils.isNullOrEmpty(hosts)) {
host = hosts.get(0).getHost();
}
if (driverProtocol.contains("mysql")) {
RdsUrlType type = this.rdsHelper.identifyRdsType(host);
if (type.isRdsCluster()) {
this.canUpdate = true;
this.dialectCode = DialectCodes.AURORA_MYSQL;
this.dialect = knownDialectsByCode.get(DialectCodes.AURORA_MYSQL);
return this.dialect;
}
if (type.isRds()) {
this.canUpdate = true;
this.dialectCode = DialectCodes.RDS_MYSQL;
this.dialect = knownDialectsByCode.get(DialectCodes.RDS_MYSQL);
this.logCurrentDialect();
return this.dialect;
}
this.canUpdate = true;
this.dialectCode = DialectCodes.MYSQL;
this.dialect = knownDialectsByCode.get(DialectCodes.MYSQL);
this.logCurrentDialect();
return this.dialect;
}
if (driverProtocol.contains("postgresql")) {
RdsUrlType type = this.rdsHelper.identifyRdsType(host);
if (type.isRdsCluster()) {
this.canUpdate = true;
this.dialectCode = DialectCodes.AURORA_PG;
this.dialect = knownDialectsByCode.get(DialectCodes.AURORA_PG);
return this.dialect;
}
if (type.isRds()) {
this.canUpdate = true;
this.dialectCode = DialectCodes.RDS_PG;
this.dialect = knownDialectsByCode.get(DialectCodes.RDS_PG);
this.logCurrentDialect();
return this.dialect;
}
this.canUpdate = true;
this.dialectCode = DialectCodes.PG;
this.dialect = knownDialectsByCode.get(DialectCodes.PG);
this.logCurrentDialect();
return this.dialect;
}
if (driverProtocol.contains("mariadb")) {
this.canUpdate = true;
this.dialectCode = DialectCodes.MARIADB;
this.dialect = knownDialectsByCode.get(DialectCodes.MARIADB);
this.logCurrentDialect();
return this.dialect;
}
this.canUpdate = true;
this.dialectCode = DialectCodes.UNKNOWN;
this.dialect = knownDialectsByCode.get(DialectCodes.UNKNOWN);
this.logCurrentDialect();
return this.dialect;
}
@Override
public Dialect getDialect(
final @NonNull String originalUrl,
final @NonNull HostSpec hostSpec,
final @NonNull Connection connection) throws SQLException {
if (!this.canUpdate) {
this.logCurrentDialect();
return this.dialect;
}
final List dialectCandidates = this.dialect.getDialectUpdateCandidates();
if (dialectCandidates != null) {
for (String dialectCandidateCode : dialectCandidates) {
Dialect dialectCandidate = knownDialectsByCode.get(dialectCandidateCode);
if (dialectCandidate == null) {
throw new SQLException(
Messages.get("DialectManager.unknownDialectCode", new Object[] {dialectCandidateCode}));
}
boolean isDialect = dialectCandidate.isDialect(connection);
if (isDialect) {
this.canUpdate = false;
this.dialectCode = dialectCandidateCode;
this.dialect = dialectCandidate;
knownEndpointDialects.put(originalUrl, dialectCandidateCode, ENDPOINT_CACHE_EXPIRATION);
knownEndpointDialects.put(hostSpec.getUrl(), dialectCandidateCode, ENDPOINT_CACHE_EXPIRATION);
this.logCurrentDialect();
return this.dialect;
}
}
}
if (DialectCodes.UNKNOWN.equals(this.dialectCode)) {
throw new SQLException(Messages.get("DialectManager.unknownDialect"));
}
this.canUpdate = false;
knownEndpointDialects.put(originalUrl, this.dialectCode, ENDPOINT_CACHE_EXPIRATION);
knownEndpointDialects.put(hostSpec.getUrl(), this.dialectCode, ENDPOINT_CACHE_EXPIRATION);
this.logCurrentDialect();
return this.dialect;
}
private void logCurrentDialect() {
LOGGER.finest(() -> String.format("Current dialect: %s, %s, canUpdate: %b",
this.dialectCode,
this.dialect == null ? "" : this.dialect,
this.canUpdate));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy