Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 35df6c9

Browse files
authoredNov 23, 2023
feat: Autoregister a target driver (#748)
1 parent 4a0d15b commit 35df6c9

18 files changed

+311
-64
lines changed
 

‎config/checkstyle/checkstyle-suppressions.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
<suppressions>
2424
<suppress files="[\\/]MySQLExceptionHandler\.java" checks="IllegalImport"/>
2525
<suppress files="[\\/]MysqlDialect\.java" checks="IllegalImport"/>
26-
<suppress files="[\\/]MariadbDataSourceHelper\.java" checks="IllegalImport"/>
27-
<suppress files="[\\/]MysqlConnectorJDataSourceHelper\.java" checks="IllegalImport"/>
28-
<suppress files="[\\/]PgDataSourceHelper\.java" checks="IllegalImport"/>
26+
<suppress files="[\\/]MariadbDriverHelper\.java" checks="IllegalImport"/>
27+
<suppress files="[\\/]MysqlConnectorJDriverHelper\.java" checks="IllegalImport"/>
28+
<suppress files="[\\/]PgDriverHelper\.java" checks="IllegalImport"/>
2929
<suppress files="[\\/]test[\\/]" checks="IllegalImport"/>
3030
<suppress files="[\\/]ExtendedFormatter\.java" checks="Header"/>
3131
</suppressions>

‎docs/using-the-jdbc-driver/UsingTheJdbcDriver.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ These parameters are applicable to any instance of the AWS JDBC Driver.
4949

5050
| Parameter | Value | Required | Description | Default Value |
5151
|---------------------------------|-----------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
52-
| `wrapperLogUnclosedConnections` | `Boolean` | No | Allows the AWS JDBC Driver to track a point in the code where connection has been opened but not closed. | `false` |
5352
| `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` |
5453
| `database` | `String` | No | Database name. | `null` |
5554
| `user` | `String` | No | Database username. | `null` |
@@ -60,6 +59,7 @@ These parameters are applicable to any instance of the AWS JDBC Driver.
6059
| `connectTimeout` | `Integer` | No | Socket connect timeout in milliseconds. | `null` |
6160
| `socketTimeout` | `Integer` | No | Socket timeout in milliseconds. | `null` |
6261
| `tcpKeepAlive` | `Boolean` | No | Enable or disable TCP keep-alive probe. | `false` |
62+
| `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` |
6363

6464
## Plugins
6565
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.
@@ -68,11 +68,11 @@ Plugins are loaded and managed through the Connection Plugin Manager and may be
6868

6969
### Connection Plugin Manager Parameters
7070

71-
| Parameter | Value | Required | Description | Default Value |
72-
|------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
73-
| `wrapperPlugins` | `String` | No | Comma separated list of connection plugin codes. <br><br>Example: `failover,efm` | `auroraConnectionTracker,failover,efm` |
74-
| `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` |
75-
| `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` |
71+
| Parameter | Value | Required | Description | Default Value |
72+
|-----------------------------------|-----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
73+
| `wrapperPlugins` | `String` | No | Comma separated list of connection plugin codes. <br><br>Example: `failover,efm` | `auroraConnectionTracker,failover,efm` |
74+
| `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` |
75+
| `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` |
7676

7777
To use a built-in plugin, specify its relevant plugin code for the `wrapperPlugins`.
7878
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.

‎wrapper/src/main/java/software/amazon/jdbc/Driver.java

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.logging.Level;
3131
import java.util.logging.Logger;
3232
import java.util.stream.Collectors;
33+
import org.checkerframework.checker.nullness.qual.NonNull;
3334
import org.checkerframework.checker.nullness.qual.Nullable;
3435
import software.amazon.jdbc.profile.ConfigurationProfile;
3536
import software.amazon.jdbc.profile.DriverConfigurationProfiles;
@@ -134,26 +135,8 @@ public Connection connect(final String url, final Properties info) throws SQLExc
134135
try {
135136
final String driverUrl = url.replaceFirst(PROTOCOL_PREFIX, "jdbc:");
136137

137-
java.sql.Driver driver;
138-
try {
139-
driver = DriverManager.getDriver(driverUrl);
140-
} catch (SQLException e) {
141-
final List<String> registeredDrivers = Collections.list(DriverManager.getDrivers())
142-
.stream()
143-
.map(x -> x.getClass().getName())
144-
.collect(Collectors.toList());
145-
throw new SQLException(
146-
Messages.get("Driver.missingDriver", new Object[] {driverUrl, registeredDrivers}), e);
147-
}
148-
149-
if (driver == null) {
150-
final List<String> registeredDrivers = Collections.list(DriverManager.getDrivers())
151-
.stream()
152-
.map(x -> x.getClass().getName())
153-
.collect(Collectors.toList());
154-
LOGGER.severe(() -> Messages.get("Driver.missingDriver", new Object[] {driverUrl, registeredDrivers}));
155-
return null;
156-
}
138+
TargetDriverHelper helper = new TargetDriverHelper();
139+
java.sql.Driver driver = helper.getTargetDriver(driverUrl, props);
157140

158141
final String logLevelStr = PropertyDefinition.LOGGER_LEVEL.getString(props);
159142
if (!StringUtils.isNullOrEmpty(logLevelStr)) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package software.amazon.jdbc;
18+
19+
import java.sql.DriverManager;
20+
import java.sql.SQLException;
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.Properties;
24+
import java.util.stream.Collectors;
25+
import org.checkerframework.checker.nullness.qual.NonNull;
26+
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager;
27+
import software.amazon.jdbc.util.ConnectionUrlParser;
28+
import software.amazon.jdbc.util.Messages;
29+
30+
public class TargetDriverHelper {
31+
32+
/**
33+
* The method returns a driver for specified url. If driver couldn't be found,
34+
* the method tries to identify a driver that corresponds to an url and register it.
35+
* Registration of the driver could be disabled by provided configuration properties.
36+
* If driver couldn't be found and couldn't be registered, the method raises an exception.
37+
*
38+
* @throws SQLException when a driver couldn't be found.
39+
*/
40+
public java.sql.Driver getTargetDriver(
41+
final @NonNull String driverUrl,
42+
final @NonNull Properties props)
43+
throws SQLException {
44+
45+
final ConnectionUrlParser parser = new ConnectionUrlParser();
46+
final String protocol = parser.getProtocol(driverUrl);
47+
48+
TargetDriverDialectManager targetDriverDialectManager = new TargetDriverDialectManager();
49+
java.sql.Driver targetDriver = null;
50+
SQLException lastException = null;
51+
52+
// Try to get a driver that can handle this url.
53+
try {
54+
targetDriver = DriverManager.getDriver(driverUrl);
55+
} catch (SQLException e) {
56+
lastException = e;
57+
}
58+
59+
// If the driver isn't found, it's possible to register a driver that corresponds to the protocol
60+
// and try again.
61+
if (targetDriver == null) {
62+
boolean triedToRegister = targetDriverDialectManager.registerDriver(protocol, props);
63+
if (triedToRegister) {
64+
// There was an attempt to register a corresponding to the protocol driver. Try to find the driver again.
65+
try {
66+
targetDriver = DriverManager.getDriver(driverUrl);
67+
} catch (SQLException e) {
68+
lastException = e;
69+
}
70+
}
71+
}
72+
73+
// The driver is not found yet. Let's raise an exception.
74+
if (targetDriver == null) {
75+
final List<String> registeredDrivers = Collections.list(DriverManager.getDrivers())
76+
.stream()
77+
.map(x -> x.getClass().getName())
78+
.collect(Collectors.toList());
79+
throw new SQLException(
80+
Messages.get("Driver.missingDriver", new Object[] {driverUrl, registeredDrivers}), lastException);
81+
}
82+
83+
return targetDriver;
84+
}
85+
}

‎wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
import java.sql.DriverManager;
2626
import java.sql.SQLException;
2727
import java.sql.SQLFeatureNotSupportedException;
28+
import java.util.Collections;
29+
import java.util.List;
2830
import java.util.Map;
2931
import java.util.Properties;
3032
import java.util.logging.Logger;
33+
import java.util.stream.Collectors;
3134
import javax.naming.NamingException;
3235
import javax.naming.Reference;
3336
import javax.naming.Referenceable;
@@ -41,6 +44,7 @@
4144
import software.amazon.jdbc.DriverConnectionProvider;
4245
import software.amazon.jdbc.HostSpec;
4346
import software.amazon.jdbc.PropertyDefinition;
47+
import software.amazon.jdbc.TargetDriverHelper;
4448
import software.amazon.jdbc.profile.ConfigurationProfile;
4549
import software.amazon.jdbc.profile.DriverConfigurationProfiles;
4650
import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect;
@@ -61,6 +65,8 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ
6165

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

68+
private static final String PROTOCOL_PREFIX = "jdbc:aws-wrapper:";
69+
6470
private static final String SERVER_NAME = "serverName";
6571
private static final String SERVER_PORT = "serverPort";
6672

@@ -123,7 +129,8 @@ public Connection getConnection(final String username, final String password) th
123129
try {
124130
// Identify the URL for connection.
125131
if (!StringUtils.isNullOrEmpty(this.jdbcUrl)) {
126-
finalUrl = this.jdbcUrl;
132+
finalUrl = this.jdbcUrl.replaceFirst(PROTOCOL_PREFIX, "jdbc:");
133+
127134
parsePropertiesFromUrl(this.jdbcUrl, props);
128135
setDatabasePropertyFromUrl(props);
129136

@@ -209,12 +216,8 @@ public Connection getConnection(final String username, final String password) th
209216
configurationProfile,
210217
telemetryFactory);
211218
} else {
212-
final java.sql.Driver targetDriver = DriverManager.getDriver(finalUrl);
213-
214-
if (targetDriver == null) {
215-
throw new SQLException(Messages.get("AwsWrapperDataSource.missingDriver",
216-
new Object[]{finalUrl}));
217-
}
219+
TargetDriverHelper helper = new TargetDriverHelper();
220+
final java.sql.Driver targetDriver = helper.getTargetDriver(finalUrl, props);
218221

219222
if (targetDriverDialect == null) {
220223
final TargetDriverDialectManager targetDriverDialectManager = new TargetDriverDialectManager();
@@ -426,7 +429,7 @@ private void setCredentialPropertiesFromUrl(final String jdbcUrl) {
426429
this.user = ConnectionUrlParser.parseUserFromUrl(jdbcUrl);
427430
}
428431

429-
if (!StringUtils.isNullOrEmpty(this.password)) {
432+
if (StringUtils.isNullOrEmpty(this.password)) {
430433
this.password = ConnectionUrlParser.parsePasswordFromUrl(jdbcUrl);
431434
}
432435
}

‎wrapper/src/main/java/software/amazon/jdbc/targetdriverdialect/GenericTargetDriverDialect.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.checkerframework.checker.nullness.qual.NonNull;
2727
import software.amazon.jdbc.HostSpec;
2828
import software.amazon.jdbc.PropertyDefinition;
29+
import software.amazon.jdbc.util.Messages;
2930
import software.amazon.jdbc.util.PropertyUtils;
3031

3132
public class GenericTargetDriverDialect implements TargetDriverDialect {
@@ -86,4 +87,11 @@ public void prepareDataSource(
8687
}
8788
}
8889

90+
public boolean isDriverRegistered() throws SQLException {
91+
throw new SQLException(Messages.get("TargetDriverDialect.unsupported"));
92+
}
93+
94+
public void registerDriver() throws SQLException {
95+
throw new SQLException(Messages.get("TargetDriverDialect.unsupported"));
96+
}
8997
}

‎wrapper/src/main/java/software/amazon/jdbc/targetdriverdialect/MariadbDataSourceHelper.java renamed to ‎wrapper/src/main/java/software/amazon/jdbc/targetdriverdialect/MariadbDriverHelper.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818

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

21+
import com.mysql.cj.jdbc.Driver;
22+
import java.sql.DriverManager;
2123
import java.sql.SQLException;
24+
import java.util.Collections;
2225
import java.util.Properties;
2326
import java.util.concurrent.TimeUnit;
2427
import java.util.logging.Logger;
@@ -30,10 +33,10 @@
3033
import software.amazon.jdbc.util.Messages;
3134
import software.amazon.jdbc.util.PropertyUtils;
3235

33-
public class MariadbDataSourceHelper {
36+
public class MariadbDriverHelper {
3437

3538
private static final Logger LOGGER =
36-
Logger.getLogger(MariadbDataSourceHelper.class.getName());
39+
Logger.getLogger(MariadbDriverHelper.class.getName());
3740

3841
private static final String LOGIN_TIMEOUT = "loginTimeout";
3942
private static final String DS_CLASS_NAME = MariaDbDataSource.class.getName();
@@ -78,4 +81,21 @@ public void prepareDataSource(
7881
LOGGER.finest(() -> "Connecting to " + finalUrl);
7982
mariaDbDataSource.setUrl(finalUrl);
8083
}
84+
85+
public boolean isDriverRegistered() throws SQLException {
86+
return Collections.list(DriverManager.getDrivers())
87+
.stream()
88+
.filter(x -> x instanceof org.mariadb.jdbc.Driver)
89+
.map(x -> true)
90+
.findAny()
91+
.orElse(false);
92+
}
93+
94+
public void registerDriver() throws SQLException {
95+
try {
96+
DriverManager.registerDriver(new org.mariadb.jdbc.Driver());
97+
} catch (SQLException e) {
98+
throw new SQLException(Messages.get("MariadbDriverHelper.canNotRegister"), e);
99+
}
100+
}
81101
}

‎wrapper/src/main/java/software/amazon/jdbc/targetdriverdialect/MariadbTargetDriverDialect.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,19 @@ public void prepareDataSource(
7979

8080
// The logic is isolated to a separated class since it uses
8181
// direct reference to org.mariadb.jdbc.MariaDbDataSource
82-
final MariadbDataSourceHelper helper = new MariadbDataSourceHelper();
82+
final MariadbDriverHelper helper = new MariadbDriverHelper();
8383
helper.prepareDataSource(dataSource, protocol, hostSpec, props);
8484
}
85+
86+
@Override
87+
public boolean isDriverRegistered() throws SQLException {
88+
final MariadbDriverHelper helper = new MariadbDriverHelper();
89+
return helper.isDriverRegistered();
90+
}
91+
92+
@Override
93+
public void registerDriver() throws SQLException {
94+
final MariadbDriverHelper helper = new MariadbDriverHelper();
95+
helper.registerDriver();
96+
}
8597
}

0 commit comments

Comments
 (0)
Please sign in to comment.