Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Autoregister a target driver #748

Merged
merged 5 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions config/checkstyle/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
<suppressions>
<suppress files="[\\/]MySQLExceptionHandler\.java" checks="IllegalImport"/>
<suppress files="[\\/]MysqlDialect\.java" checks="IllegalImport"/>
<suppress files="[\\/]MariadbDataSourceHelper\.java" checks="IllegalImport"/>
<suppress files="[\\/]MysqlConnectorJDataSourceHelper\.java" checks="IllegalImport"/>
<suppress files="[\\/]PgDataSourceHelper\.java" checks="IllegalImport"/>
<suppress files="[\\/]MariadbDriverHelper\.java" checks="IllegalImport"/>
<suppress files="[\\/]MysqlConnectorJDriverHelper\.java" checks="IllegalImport"/>
<suppress files="[\\/]PgDriverHelper\.java" checks="IllegalImport"/>
<suppress files="[\\/]test[\\/]" checks="IllegalImport"/>
<suppress files="[\\/]ExtendedFormatter\.java" checks="Header"/>
</suppressions>
12 changes: 6 additions & 6 deletions docs/using-the-jdbc-driver/UsingTheJdbcDriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ These parameters are applicable to any instance of the AWS JDBC Driver.

| Parameter | Value | Required | Description | Default Value |
|---------------------------------|-----------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
| `wrapperLogUnclosedConnections` | `Boolean` | No | Allows the AWS JDBC Driver to track a point in the code where connection has been opened but not closed. | `false` |
| `wrapperLoggerLevel` | `String` | No | Logger level of the AWS JDBC Driver. <br><br/>If it is used, it must be one of the following values: `OFF`, `SEVERE`, `WARNING`, `INFO`, `CONFIG`, `FINE`, `FINER`, `FINEST`, `ALL`. | `null` |
| `database` | `String` | No | Database name. | `null` |
| `user` | `String` | No | Database username. | `null` |
Expand All @@ -60,6 +59,7 @@ These parameters are applicable to any instance of the AWS JDBC Driver.
| `connectTimeout` | `Integer` | No | Socket connect timeout in milliseconds. | `null` |
| `socketTimeout` | `Integer` | No | Socket timeout in milliseconds. | `null` |
| `tcpKeepAlive` | `Boolean` | No | Enable or disable TCP keep-alive probe. | `false` |
| `targetDriverAutoRegister` | `Boolean` | No | Allows the AWS JDBC Driver to register a target driver based on `wrapperTargetDriverDialect` configuration parameter or, if it's missed, on a connection url protocol. | `true` |

## Plugins
The AWS JDBC Driver uses plugins to execute JDBC methods. You can think of a plugin as an extensible code module that adds extra logic around any JDBC method calls. The AWS JDBC Driver has a number of [built-in plugins](#list-of-available-plugins) available for use.
Expand All @@ -68,11 +68,11 @@ Plugins are loaded and managed through the Connection Plugin Manager and may be

### Connection Plugin Manager Parameters

| Parameter | Value | Required | Description | Default Value |
|------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
| `wrapperPlugins` | `String` | No | Comma separated list of connection plugin codes. <br><br>Example: `failover,efm` | `auroraConnectionTracker,failover,efm` |
| `autoSortWrapperPluginOrder` | `Boolean` | No | Allows the AWS JDBC Driver to sort connection plugins to prevent plugin misconfiguration. Allows a user to provide a custom plugin order if needed. | `true` |
| `wrapperProfileName` | `String` | No | Driver configuration profile name. Instead of listing plugin codes with `wrapperPlugins`, the driver profile can be set with this parameter. <br><br> Example: See [below](#configuration-profiles). | `null` |
| Parameter | Value | Required | Description | Default Value |
|-----------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
| `wrapperPlugins` | `String` | No | Comma separated list of connection plugin codes. <br><br>Example: `failover,efm` | `auroraConnectionTracker,failover,efm` |
| `autoSortWrapperPluginOrder` | `Boolean` | No | Allows the AWS JDBC Driver to sort connection plugins to prevent plugin misconfiguration. Allows a user to provide a custom plugin order if needed. | `true` |
| `wrapperProfileName` | `String` | No | Driver configuration profile name. Instead of listing plugin codes with `wrapperPlugins`, the driver profile can be set with this parameter. <br><br> Example: See [below](#configuration-profiles). | `null` |

To use a built-in plugin, specify its relevant plugin code for the `wrapperPlugins`.
The default value for `wrapperPlugins` is `auroraConnectionTracker,failover,efm`. These 3 plugins are enabled by default. To read more about these plugins, see the [List of Available Plugins](#list-of-available-plugins) section.
Expand Down
23 changes: 3 additions & 20 deletions wrapper/src/main/java/software/amazon/jdbc/Driver.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import software.amazon.jdbc.profile.ConfigurationProfile;
import software.amazon.jdbc.profile.DriverConfigurationProfiles;
Expand Down Expand Up @@ -134,26 +135,8 @@ public Connection connect(final String url, final Properties info) throws SQLExc
try {
final String driverUrl = url.replaceFirst(PROTOCOL_PREFIX, "jdbc:");

java.sql.Driver driver;
try {
driver = DriverManager.getDriver(driverUrl);
} catch (SQLException e) {
final List<String> registeredDrivers = Collections.list(DriverManager.getDrivers())
.stream()
.map(x -> x.getClass().getName())
.collect(Collectors.toList());
throw new SQLException(
Messages.get("Driver.missingDriver", new Object[] {driverUrl, registeredDrivers}), e);
}

if (driver == null) {
final List<String> registeredDrivers = Collections.list(DriverManager.getDrivers())
.stream()
.map(x -> x.getClass().getName())
.collect(Collectors.toList());
LOGGER.severe(() -> Messages.get("Driver.missingDriver", new Object[] {driverUrl, registeredDrivers}));
return null;
}
TargetDriverHelper helper = new TargetDriverHelper();
java.sql.Driver driver = helper.getTargetDriver(driverUrl, props);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: is getTargetDriver the right name for this function?
we first try to find if the driver is registered, then try to register the driver of the dialect

after this line, we either had a driver ready, either registered a new driver, either crashed
would instantiateDriver a better fit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method either return a target driver, or raises an exception.

What could be alternatives for getTargetDriver method name?


final String logLevelStr = PropertyDefinition.LOGGER_LEVEL.getString(props);
if (!StringUtils.isNullOrEmpty(logLevelStr)) {
Expand Down
72 changes: 72 additions & 0 deletions wrapper/src/main/java/software/amazon/jdbc/TargetDriverHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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;

import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager;
import software.amazon.jdbc.util.ConnectionUrlParser;
import software.amazon.jdbc.util.Messages;

public class TargetDriverHelper {

public java.sql.Driver getTargetDriver(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: it's a really minor thing but could we add some comments in some parts of the conditional just to explain the situation and what we're doing in that case

final @NonNull String driverUrl,
final @NonNull Properties props)
throws SQLException {

final ConnectionUrlParser parser = new ConnectionUrlParser();
final String protocol = parser.getProtocol(driverUrl);

TargetDriverDialectManager targetDriverDialectManager = new TargetDriverDialectManager();
java.sql.Driver targetDriver = null;
SQLException lastException = null;

try {
targetDriver = DriverManager.getDriver(driverUrl);
} catch (SQLException e) {
lastException = e;
}

if (targetDriver == null) {
boolean triedToRegister = targetDriverDialectManager.registerDriver(protocol, props);
if (triedToRegister) {
try {
targetDriver = DriverManager.getDriver(driverUrl);
} catch (SQLException e) {
lastException = e;
}
}
}

if (targetDriver == null) {
final List<String> registeredDrivers = Collections.list(DriverManager.getDrivers())
.stream()
.map(x -> x.getClass().getName())
.collect(Collectors.toList());
throw new SQLException(
Messages.get("Driver.missingDriver", new Object[] {driverUrl, registeredDrivers}), lastException);
}

return targetDriver;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
Expand All @@ -41,6 +44,7 @@
import software.amazon.jdbc.DriverConnectionProvider;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.PropertyDefinition;
import software.amazon.jdbc.TargetDriverHelper;
import software.amazon.jdbc.profile.ConfigurationProfile;
import software.amazon.jdbc.profile.DriverConfigurationProfiles;
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect;
Expand All @@ -61,6 +65,8 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ

private static final Logger LOGGER = Logger.getLogger(AwsWrapperDataSource.class.getName());

private static final String PROTOCOL_PREFIX = "jdbc:aws-wrapper:";

private static final String SERVER_NAME = "serverName";
private static final String SERVER_PORT = "serverPort";

Expand Down Expand Up @@ -123,7 +129,8 @@ public Connection getConnection(final String username, final String password) th
try {
// Identify the URL for connection.
if (!StringUtils.isNullOrEmpty(this.jdbcUrl)) {
finalUrl = this.jdbcUrl;
finalUrl = this.jdbcUrl.replaceFirst(PROTOCOL_PREFIX, "jdbc:");

parsePropertiesFromUrl(this.jdbcUrl, props);
setDatabasePropertyFromUrl(props);

Expand Down Expand Up @@ -209,12 +216,8 @@ public Connection getConnection(final String username, final String password) th
configurationProfile,
telemetryFactory);
} else {
final java.sql.Driver targetDriver = DriverManager.getDriver(finalUrl);

if (targetDriver == null) {
throw new SQLException(Messages.get("AwsWrapperDataSource.missingDriver",
new Object[]{finalUrl}));
}
TargetDriverHelper helper = new TargetDriverHelper();
final java.sql.Driver targetDriver = helper.getTargetDriver(finalUrl, props);

if (targetDriverDialect == null) {
final TargetDriverDialectManager targetDriverDialectManager = new TargetDriverDialectManager();
Expand Down Expand Up @@ -426,7 +429,7 @@ private void setCredentialPropertiesFromUrl(final String jdbcUrl) {
this.user = ConnectionUrlParser.parseUserFromUrl(jdbcUrl);
}

if (!StringUtils.isNullOrEmpty(this.password)) {
if (StringUtils.isNullOrEmpty(this.password)) {
this.password = ConnectionUrlParser.parsePasswordFromUrl(jdbcUrl);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.PropertyDefinition;
import software.amazon.jdbc.util.Messages;
import software.amazon.jdbc.util.PropertyUtils;

public class GenericTargetDriverDialect implements TargetDriverDialect {
Expand Down Expand Up @@ -86,4 +87,11 @@ public void prepareDataSource(
}
}

public boolean isDriverRegistered() throws SQLException {
throw new SQLException(Messages.get("TargetDriverDialect.unsupported"));
}

public void registerDriver() throws SQLException {
throw new SQLException(Messages.get("TargetDriverDialect.unsupported"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

import static software.amazon.jdbc.util.ConnectionUrlBuilder.buildUrl;

import com.mysql.cj.jdbc.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
Expand All @@ -30,10 +33,10 @@
import software.amazon.jdbc.util.Messages;
import software.amazon.jdbc.util.PropertyUtils;

public class MariadbDataSourceHelper {
public class MariadbDriverHelper {

private static final Logger LOGGER =
Logger.getLogger(MariadbDataSourceHelper.class.getName());
Logger.getLogger(MariadbDriverHelper.class.getName());

private static final String LOGIN_TIMEOUT = "loginTimeout";
private static final String DS_CLASS_NAME = MariaDbDataSource.class.getName();
Expand Down Expand Up @@ -78,4 +81,21 @@ public void prepareDataSource(
LOGGER.finest(() -> "Connecting to " + finalUrl);
mariaDbDataSource.setUrl(finalUrl);
}

public boolean isDriverRegistered() throws SQLException {
return Collections.list(DriverManager.getDrivers())
.stream()
.filter(x -> x instanceof org.mariadb.jdbc.Driver)
.map(x -> true)
.findAny()
.orElse(false);
}

public void registerDriver() throws SQLException {
try {
DriverManager.registerDriver(new org.mariadb.jdbc.Driver());
} catch (SQLException e) {
throw new SQLException(Messages.get("MariadbDriverHelper.canNotRegister"), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,19 @@ public void prepareDataSource(

// The logic is isolated to a separated class since it uses
// direct reference to org.mariadb.jdbc.MariaDbDataSource
final MariadbDataSourceHelper helper = new MariadbDataSourceHelper();
final MariadbDriverHelper helper = new MariadbDriverHelper();
helper.prepareDataSource(dataSource, protocol, hostSpec, props);
}

@Override
public boolean isDriverRegistered() throws SQLException {
final MariadbDriverHelper helper = new MariadbDriverHelper();
return helper.isDriverRegistered();
}

@Override
public void registerDriver() throws SQLException {
final MariadbDriverHelper helper = new MariadbDriverHelper();
helper.registerDriver();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,25 @@

package software.amazon.jdbc.targetdriverdialect;

import com.mysql.cj.jdbc.Driver;
import com.mysql.cj.jdbc.MysqlDataSource;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.checkerframework.checker.nullness.qual.NonNull;
import software.amazon.jdbc.AwsWrapperProperty;
import software.amazon.jdbc.HostSpec;
import software.amazon.jdbc.PropertyDefinition;
import software.amazon.jdbc.util.Messages;
import software.amazon.jdbc.util.PropertyUtils;
import software.amazon.jdbc.util.StringUtils;

public class MysqlConnectorJDataSourceHelper {
public class MysqlConnectorJDriverHelper {

private static final Logger LOGGER =
Logger.getLogger(MysqlConnectorJDataSourceHelper.class.getName());
Logger.getLogger(MysqlConnectorJDriverHelper.class.getName());

public void prepareDataSource(
final @NonNull DataSource dataSource,
Expand Down Expand Up @@ -73,4 +74,21 @@ public void prepareDataSource(

PropertyUtils.applyProperties(dataSource, props);
}

public boolean isDriverRegistered() throws SQLException {
return Collections.list(DriverManager.getDrivers())
.stream()
.filter(x -> x instanceof com.mysql.cj.jdbc.Driver)
.map(x -> true)
.findAny()
.orElse(false);
}

public void registerDriver() throws SQLException {
try {
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
} catch (SQLException e) {
throw new SQLException(Messages.get("MysqlConnectorJDriverHelper.canNotRegister"), e);
}
}
}
Loading
Loading