From 14314019c76a4001d32eb5a81994b59294a89aa9 Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Mon, 27 Mar 2023 11:05:16 -0400 Subject: [PATCH 1/2] WIP: Use checker framework to check for nulls --- build.gradle.kts | 14 ++++++ .../amazon/jdbc/AwsWrapperProperty.java | 7 +++ .../jdbc/DataSourceConnectionProvider.java | 4 +- .../java/software/amazon/jdbc/HostSpec.java | 8 +++- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 4 +- .../software/amazon/jdbc/util/CacheMap.java | 14 +++--- .../jdbc/util/ConnectionUrlBuilder.java | 43 +++++++++---------- .../amazon/jdbc/util/ConnectionUrlParser.java | 18 +++++--- .../software/amazon/jdbc/util/Messages.java | 6 ++- .../amazon/jdbc/util/PropertyUtils.java | 16 ++++--- .../software/amazon/jdbc/util/RdsUtils.java | 10 +++-- .../amazon/jdbc/util/SqlMethodAnalyzer.java | 3 +- .../amazon/jdbc/util/StringUtils.java | 8 ++-- 13 files changed, 97 insertions(+), 58 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4a46ac4b6..ac13f9176 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,7 @@ import com.github.vlsi.gradle.dsl.configureEach import com.github.vlsi.gradle.properties.dsl.props import software.amazon.jdbc.buildtools.JavaCommentPreprocessorTask import com.github.vlsi.gradle.publishing.dsl.simplifyXml +import org.checkerframework.gradle.plugin.CheckerFrameworkExtension plugins { java @@ -26,6 +27,8 @@ plugins { id("com.github.vlsi.gradle-extensions") id("com.github.vlsi.stage-vote-release") id("com.github.vlsi.ide") + id("org.checkerframework") + } val versionMajor = project.property("aws-advanced-jdbc-wrapper.version.major") @@ -45,6 +48,7 @@ allprojects { apply(plugin = "signing") apply(plugin = "maven-publish") apply(plugin = "com.github.vlsi.ide") + apply(plugin = "org.checkerframework") tasks { configureEach { @@ -81,6 +85,16 @@ allprojects { ) } + configure { + checkers = listOf( + "org.checkerframework.checker.nullness.NullnessChecker", + "org.checkerframework.checker.optional.OptionalChecker", + "org.checkerframework.checker.regex.RegexChecker" + ) + } + checkerFramework { + excludeTests = true + } publishing { publications { if (project.props.bool("nexus.publish", default = true)) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/AwsWrapperProperty.java b/wrapper/src/main/java/software/amazon/jdbc/AwsWrapperProperty.java index 942fa90e4..cfc5ba449 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/AwsWrapperProperty.java +++ b/wrapper/src/main/java/software/amazon/jdbc/AwsWrapperProperty.java @@ -33,6 +33,7 @@ public AwsWrapperProperty( this(name, defaultValue, description, required, (String[]) null); } + @SuppressWarnings({"nullness:argument","nullness:assignment"}) public AwsWrapperProperty( String name, @Nullable String defaultValue, @@ -46,10 +47,12 @@ public AwsWrapperProperty( this.choices = choices; } + @SuppressWarnings("nullness:argument") public @Nullable String getString(Properties properties) { return properties.getProperty(name, defaultValue); } + @SuppressWarnings("nullness:argument") public boolean getBoolean(Properties properties) { Object value = properties.get(name); if (value instanceof Boolean) { @@ -58,6 +61,7 @@ public boolean getBoolean(Properties properties) { return Boolean.parseBoolean(properties.getProperty(name, defaultValue)); } + @SuppressWarnings("nullness:argument") public int getInteger(Properties properties) { Object value = properties.get(name); if (value instanceof Integer) { @@ -66,6 +70,7 @@ public int getInteger(Properties properties) { return Integer.parseInt(properties.getProperty(name, defaultValue)); } + @SuppressWarnings("nullness:argument") public long getLong(Properties properties) { Object value = properties.get(name); if (value instanceof Long) { @@ -74,6 +79,7 @@ public long getLong(Properties properties) { return Long.parseLong(properties.getProperty(name, defaultValue)); } + @SuppressWarnings("nullness:argument") public void set(Properties properties, @Nullable String value) { if (value == null) { properties.remove(name); @@ -82,6 +88,7 @@ public void set(Properties properties, @Nullable String value) { } } + @SuppressWarnings("nullness:argument") public DriverPropertyInfo toDriverPropertyInfo(Properties properties) { DriverPropertyInfo propertyInfo = new DriverPropertyInfo(name, getString(properties)); propertyInfo.required = required; diff --git a/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java index 9131e87dd..7a9f8525a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java @@ -34,14 +34,14 @@ public class DataSourceConnectionProvider implements ConnectionProvider { private final @NonNull DataSource dataSource; - private final @Nullable String serverPropertyName; + private final @NonNull String serverPropertyName; private final @Nullable String portPropertyName; private final @Nullable String urlPropertyName; private final @Nullable String databasePropertyName; public DataSourceConnectionProvider( final @NonNull DataSource dataSource, - final @Nullable String serverPropertyName, + final @NonNull String serverPropertyName, final @Nullable String portPropertyName, final @Nullable String urlPropertyName, final @Nullable String databasePropertyName) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java b/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java index 44fd42008..dc0ba9115 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java +++ b/wrapper/src/main/java/software/amazon/jdbc/HostSpec.java @@ -16,6 +16,7 @@ package software.amazon.jdbc; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Arrays; import java.util.Collections; import java.util.Objects; @@ -26,7 +27,7 @@ * An object representing connection info for a given host. Modifiable fields are thread-safe to support sharing this * object with the EFM monitor thread. */ -public class HostSpec { +public final class HostSpec { public static final int NO_PORT = -1; @@ -144,7 +145,10 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { + if (obj == null) { + return false; + } if (obj == this) { return true; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 313feaba4..624113f16 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -68,8 +68,8 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ protected @Nullable String jdbcUrl; protected @Nullable String targetDataSourceClassName; protected @Nullable Properties targetDataSourceProperties; - protected @Nullable String jdbcProtocol; - protected @Nullable String serverPropertyName; + protected @NonNull String jdbcProtocol; + protected @NonNull String serverPropertyName; protected @Nullable String portPropertyName; protected @Nullable String urlPropertyName; protected @Nullable String databasePropertyName; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java b/wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java index bb772983b..c67b14121 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -31,7 +33,8 @@ public class CacheMap { public CacheMap() { } - public V get(final K key) { + @SuppressWarnings("return") + public @Nullable V get(final @NonNull K key) { CacheItem cacheItem = cache.computeIfPresent(key, (kk, vv) -> vv.isExpired() ? null : vv); return cacheItem == null ? null : cacheItem.item; } @@ -44,7 +47,7 @@ public V get(final K key, final V defaultItemValue, long itemExpirationNano) { return cacheItem.item; } - public void put(final K key, final V item, long itemExpirationNano) { + public void put(final @NonNull K key, final V item, long itemExpirationNano) { cache.put(key, new CacheItem<>(item, System.nanoTime() + itemExpirationNano)); cleanUp(); } @@ -54,7 +57,7 @@ public void putIfAbsent(final K key, final V item, long itemExpirationNano) { cleanUp(); } - public void remove(final K key) { + public void remove(final @NonNull K key) { cache.remove(key); cleanUp(); } @@ -79,7 +82,8 @@ private void cleanUp() { if (this.cleanupTimeNanos.get() < System.nanoTime()) { this.cleanupTimeNanos.set(System.nanoTime() + cleanupIntervalNanos); cache.forEach((key, value) -> { - if (value == null || value.isExpired()) { + //TODO why are we doing this ? + if (key != null && (value == null || value.isExpired())) { cache.remove(key); } }); @@ -108,7 +112,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlBuilder.java index e12cef375..2a17a1769 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlBuilder.java @@ -22,25 +22,20 @@ import java.sql.SQLException; import java.util.Enumeration; import java.util.Properties; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PropertyDefinition; public class ConnectionUrlBuilder { // Builds a connection URL of the generic format: "protocol//[hosts][/database][?properties]" - public static String buildUrl(String jdbcProtocol, - HostSpec hostSpec, - String serverPropertyName, - String portPropertyName, - String databasePropertyName, + public static String buildUrl(@NonNull String jdbcProtocol, + @NonNull HostSpec hostSpec, + @NonNull String serverPropertyName, + @Nullable String portPropertyName, + @Nullable String databasePropertyName, Properties props) throws SQLException { - if (StringUtils.isNullOrEmpty(jdbcProtocol) - || ((StringUtils.isNullOrEmpty(serverPropertyName) - || StringUtils.isNullOrEmpty( - props.getProperty(serverPropertyName))) - && hostSpec == null)) { - throw new SQLException(Messages.get("ConnectionUrlBuilder.missingJdbcProtocol")); - } final Properties copy = PropertyUtils.copyProperties(props); final StringBuilder urlBuilder = new StringBuilder(); @@ -75,22 +70,24 @@ public static String buildUrl(String jdbcProtocol, final StringBuilder queryBuilder = new StringBuilder(); final Enumeration propertyNames = copy.propertyNames(); while (propertyNames.hasMoreElements()) { - String propertyName = propertyNames.nextElement().toString(); + String propertyName = (String)propertyNames.nextElement(); if (queryBuilder.length() != 0) { queryBuilder.append("&"); } if (!StringUtils.isNullOrEmpty(propertyName)) { final String propertyValue = copy.getProperty(propertyName); - try { - queryBuilder - .append(propertyName) - .append("=") - .append(URLEncoder.encode(propertyValue, StandardCharsets.UTF_8.toString())); - } catch (UnsupportedEncodingException e) { - throw new SQLException( - Messages.get("ConnectionUrlBuilder.failureEncodingConnectionUrl"), - e); + if (!StringUtils.isNullOrEmpty(propertyValue)) { + try { + queryBuilder + .append(propertyName) + .append("=") + .append(URLEncoder.encode(propertyValue, StandardCharsets.UTF_8.toString())); + } catch (UnsupportedEncodingException e) { + throw new SQLException( + Messages.get("ConnectionUrlBuilder.failureEncodingConnectionUrl"), + e); + } } } } @@ -102,7 +99,7 @@ public static String buildUrl(String jdbcProtocol, return urlBuilder.toString(); } - private static void removeProperty(String propertyKey, Properties props) { + private static void removeProperty(@Nullable String propertyKey, Properties props) { if (!StringUtils.isNullOrEmpty(propertyKey) && !StringUtils.isNullOrEmpty(props.getProperty(propertyKey))) { props.remove(propertyKey); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlParser.java b/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlParser.java index 42a2b478d..c28effff3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlParser.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ConnectionUrlParser.java @@ -22,16 +22,18 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.regex.qual.Regex; import software.amazon.jdbc.HostRole; import software.amazon.jdbc.HostSpec; public class ConnectionUrlParser { private static final Logger LOGGER = Logger.getLogger(ConnectionUrlParser.class.getName()); - private static final String HOSTS_SEPARATOR = ","; - static final String HOST_PORT_SEPARATOR = ":"; - static final Pattern CONNECTION_STRING_PATTERN = + private static final @Regex String HOSTS_SEPARATOR = ","; + private static final @Regex String HOST_PORT_SEPARATOR = ":"; + private static final Pattern CONNECTION_STRING_PATTERN = Pattern.compile( "(?[\\w(\\-)?+:%]+)\\s*" // Driver protocol. "word1:word2:..." or "word1-word2:word3:..." + "(?://(?[^/?#]*))?\\s*" // Optional list of host(s) starting with // and @@ -41,6 +43,7 @@ public class ConnectionUrlParser { static final Pattern EMPTY_STRING_IN_QUOTATIONS = Pattern.compile("\"(\\s*)\""); private static final RdsUtils rdsUtils = new RdsUtils(); + @SuppressWarnings("dereference.of.nullable") public List getHostsFromConnectionUrl(final String initialConnection, boolean singleWriterConnectionString) { final List hostsList = new ArrayList<>(); @@ -105,7 +108,7 @@ private static int parsePortAsInt(String port) { // Get the database name from a given url of the generic format: // "protocol//[hosts][/database][?properties]" - public static String parseDatabaseFromUrl(String url) { + public static @Nullable String parseDatabaseFromUrl(String url) { String[] dbName = url.split("//")[1].split("\\?")[0].split("/"); if (dbName.length == 1) { @@ -117,7 +120,7 @@ public static String parseDatabaseFromUrl(String url) { // Get the user name from a given url of the generic format: // "protocol//[hosts][/database][?properties]" - public static String parseUserFromUrl(String url) { + public static @Nullable String parseUserFromUrl(String url) { final Pattern userPattern = Pattern.compile("user=(?[^&]*)"); final Matcher matcher = userPattern.matcher(url); if (matcher.find()) { @@ -129,7 +132,7 @@ public static String parseUserFromUrl(String url) { // Get the password from a given url of the generic format: // "protocol//[hosts][/database][?properties]" - public static String parsePasswordFromUrl(String url) { + public static @Nullable String parsePasswordFromUrl(String url) { final Pattern passwordPattern = Pattern.compile("password=(?[^&]*)"); final Matcher matcher = passwordPattern.matcher(url); if (matcher.find()) { @@ -170,7 +173,8 @@ public static void parsePropertiesFromUrl(String url, Properties props) { } } - private static @Nullable String urlDecode(String url) { + @SuppressWarnings("nullness:argument") + private static @Nullable String urlDecode(@NonNull String url) { try { return StringUtils.decode(url); } catch (IllegalArgumentException e) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java b/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java index 753356150..d27da60a3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import java.text.MessageFormat; import java.util.ResourceBundle; @@ -30,11 +32,11 @@ public class Messages { * @param key The key mapped to an error message. * @return The associated localized error message. */ - public static String get(String key) { + public static @NonNull String get(String key) { return get(key, emptyArgs); } - public static String get(String key, Object[] args) { + public static @NonNull String get(@NonNull String key, @Nullable Object[] args) { final String message = MESSAGES.getString(key); return MessageFormat.format(message, args); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java b/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java index afaedb66c..7337b242f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java @@ -38,13 +38,15 @@ public static void applyProperties(final Object target, final Properties propert Enumeration propertyNames = properties.propertyNames(); while (propertyNames.hasMoreElements()) { Object key = propertyNames.nextElement(); - String propName = key.toString(); - Object propValue = properties.getProperty(propName); - if (propValue == null) { - propValue = properties.get(key); - } + if (key != null) { + String propName = key.toString(); + Object propValue = properties.getProperty(propName); + if (propValue == null) { + propValue = properties.get(key); + } - setPropertyOnTarget(target, propName, propValue, methods); + setPropertyOnTarget(target, propName, propValue, methods); + } } } @@ -100,7 +102,7 @@ public static void setPropertyOnTarget( () -> Messages.get( "PropertyUtils.failedToSetPropertyWithReason", - new Object[] {propName, target.getClass(), ex.getCause().getMessage()})); + new Object[] {propName, target.getClass(), ex.getCause() == null ? null: ex.getCause().getMessage()})); throw new RuntimeException(ex.getCause()); } catch (Exception e) { LOGGER.warning( diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/RdsUtils.java b/wrapper/src/main/java/software/amazon/jdbc/util/RdsUtils.java index d5e496032..5e248220f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/RdsUtils.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/RdsUtils.java @@ -16,6 +16,7 @@ package software.amazon.jdbc.util; +import org.checkerframework.checker.nullness.qual.Nullable; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -183,7 +184,7 @@ public String getRdsInstanceHostPattern(String host) { return "?"; } - public String getRdsRegion(String host) { + public @Nullable String getRdsRegion(@Nullable String host) { if (StringUtils.isNullOrEmpty(host)) { return null; } @@ -199,7 +200,7 @@ public String getRdsRegion(String host) { return null; } - public boolean isWriterClusterDns(String host) { + public boolean isWriterClusterDns(@Nullable String host) { if (StringUtils.isNullOrEmpty(host)) { return false; } @@ -215,7 +216,7 @@ public boolean isWriterClusterDns(String host) { return false; } - public boolean isReaderClusterDns(String host) { + public boolean isReaderClusterDns(@Nullable String host) { if (StringUtils.isNullOrEmpty(host)) { return false; } @@ -231,7 +232,8 @@ public boolean isReaderClusterDns(String host) { return false; } - public String getRdsClusterHostUrl(String host) { + @SuppressWarnings("regex:argument") + public @Nullable String getRdsClusterHostUrl(@Nullable String host) { if (StringUtils.isNullOrEmpty(host)) { return null; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/SqlMethodAnalyzer.java b/wrapper/src/main/java/software/amazon/jdbc/util/SqlMethodAnalyzer.java index 8c9cff118..27db6ad58 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/SqlMethodAnalyzer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/SqlMethodAnalyzer.java @@ -16,6 +16,7 @@ package software.amazon.jdbc.util; +import org.checkerframework.checker.nullness.qual.Nullable; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; @@ -140,7 +141,7 @@ public boolean doesSwitchAutoCommitFalseTrue(final Connection conn, final String return !oldAutoCommitVal && Boolean.TRUE.equals(newAutoCommitVal); } - public Boolean getAutoCommitValueFromSqlStatement(final Object[] args) { + public @Nullable Boolean getAutoCommitValueFromSqlStatement(final Object[] args) { if (args == null || args.length < 1) { return null; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/StringUtils.java b/wrapper/src/main/java/software/amazon/jdbc/util/StringUtils.java index 4e492a21b..385ae69f3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/StringUtils.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/StringUtils.java @@ -25,14 +25,16 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.regex.qual.Regex; public class StringUtils { private static final String ENCODING_FOR_URL = System.getProperty("software.amazon.jdbc.Driver.url.encoding", "UTF-8"); - public static String decode(String encoded) { + public static @NonNull String decode(@NonNull String encoded) { try { return URLDecoder.decode(encoded, ENCODING_FOR_URL); } catch (UnsupportedEncodingException e) { @@ -41,7 +43,7 @@ public static String decode(String encoded) { } } - public static String encode(String plain) { + public static @NonNull String encode(@NonNull String plain) { try { return URLEncoder.encode(plain, "UTF-8"); } catch (UnsupportedEncodingException e) { @@ -70,7 +72,7 @@ public static boolean isNullOrEmpty(@Nullable String s) { * @return the list of strings, split by delimiter * @throws IllegalArgumentException if an error occurs */ - public static List split(String stringToSplit, String delimiter, boolean trim) { + public static List split(String stringToSplit, @Regex String delimiter, boolean trim) { if (stringToSplit == null || "".equals(stringToSplit)) { return new ArrayList<>(); } From e122cc12f57874237625885ae2ed5843427a8d4e Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Tue, 11 Apr 2023 14:27:13 -0400 Subject: [PATCH 2/2] Latest changes --- .../amazon/jdbc/ConnectionPlugin.java | 32 +++---- .../amazon/jdbc/ConnectionPluginManager.java | 88 +++++++++++-------- .../software/amazon/jdbc/JdbcCallable.java | 4 +- .../amazon/jdbc/PluginServiceImpl.java | 12 +-- .../jdbc/plugin/DefaultConnectionPlugin.java | 10 ++- .../software/amazon/jdbc/util/Messages.java | 4 +- .../amazon/jdbc/util/PropertyUtils.java | 13 +-- .../amazon/jdbc/util/WrapperUtils.java | 35 +++++--- .../jdbc/wrapper/ConnectionWrapper.java | 55 +++++++----- 9 files changed, 146 insertions(+), 107 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java index 443c763d7..aa017568e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java @@ -16,6 +16,8 @@ package software.amazon.jdbc; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import java.sql.Connection; import java.sql.SQLException; import java.util.EnumSet; @@ -32,28 +34,28 @@ public interface ConnectionPlugin { Set getSubscribedMethods(); T execute( - final Class resultClass, - final Class exceptionClass, - final Object methodInvokeOn, - final String methodName, - final JdbcCallable jdbcMethodFunc, - final Object[] jdbcMethodArgs) + final @Nullable Class resultClass, + final @NonNull Class exceptionClass, + final @NonNull Object methodInvokeOn, + final @NonNull String methodName, + final @NonNull JdbcCallable jdbcMethodFunc, + final @Nullable Object @NonNull[] jdbcMethodArgs) throws E; Connection connect( - final String driverProtocol, - final HostSpec hostSpec, - final Properties props, + final @NonNull String driverProtocol, + final @NonNull HostSpec hostSpec, + final @NonNull Properties props, final boolean isInitialConnection, - final JdbcCallable connectFunc) + final @NonNull JdbcCallable connectFunc) throws SQLException; void initHostProvider( - final String driverProtocol, - final String initialUrl, - final Properties props, - final HostListProviderService hostListProviderService, - final JdbcCallable initHostProviderFunc) + final @NonNull String driverProtocol, + final @NonNull String initialUrl, + final @NonNull Properties props, + final @NonNull HostListProviderService hostListProviderService, + final @Nullable JdbcCallable initHostProviderFunc) throws SQLException; OldConnectionSuggestedAction notifyConnectionChanged(EnumSet changes); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index a06674360..da73c6e5f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -23,10 +23,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.cleanup.CanReleaseResources; @@ -58,6 +62,7 @@ */ public class ConnectionPluginManager implements CanReleaseResources { + @SuppressWarnings("method.invocation") protected static final Map> pluginFactoriesByCode = new HashMap>() { { @@ -88,21 +93,23 @@ public class ConnectionPluginManager implements CanReleaseResources { protected Properties props = new Properties(); protected List plugins; - protected final ConnectionProvider connectionProvider; - protected final ConnectionWrapper connectionWrapper; - protected PluginService pluginService; + protected final @NotOnlyInitialized @NonNull ConnectionProvider connectionProvider; + protected final @NotOnlyInitialized @NonNull ConnectionWrapper connectionWrapper; + @SuppressWarnings ("initialization.fields.uninitialized") + protected @NotOnlyInitialized PluginService pluginService; @SuppressWarnings("rawtypes") protected final Map pluginChainFuncMap = new HashMap<>(); - public ConnectionPluginManager(ConnectionProvider connectionProvider, ConnectionWrapper connectionWrapper) { + @SuppressWarnings("initialization.fields.uninitialized") + public ConnectionPluginManager(ConnectionProvider connectionProvider, @UnderInitialization ConnectionWrapper connectionWrapper) { this.connectionProvider = connectionProvider; this.connectionWrapper = connectionWrapper; } /** * This constructor is for testing purposes only. - */ + */ ConnectionPluginManager( ConnectionProvider connectionProvider, Properties props, @@ -116,6 +123,7 @@ public ConnectionPluginManager(ConnectionProvider connectionProvider, Connection /** * This constructor is for testing purposes only. */ + @SuppressWarnings("initialization.fields.uninitialized") ConnectionPluginManager( ConnectionProvider connectionProvider, Properties props, @@ -149,7 +157,9 @@ public void unlock() { * @throws SQLException if errors occurred during the execution. */ public void init( - PluginService pluginService, Properties props, PluginManagerService pluginManagerService) + @UnderInitialization @NonNull PluginService pluginService, + @NonNull Properties props, + @NonNull PluginManagerService pluginManagerService) throws SQLException { this.props = props; @@ -222,10 +232,10 @@ public void init( this.plugins.add(defaultPlugin); } - protected T executeWithSubscribedPlugins( + protected @Nullable T executeWithSubscribedPlugins( final String methodName, - final PluginPipeline pluginPipeline, - final JdbcCallable jdbcMethodFunc) + final @NonNull PluginPipeline pluginPipeline, + final @NonNull JdbcCallable jdbcMethodFunc) throws E { if (pluginPipeline == null) { @@ -241,7 +251,9 @@ protected T executeWithSubscribedPlugins( if (pluginChainFunc == null) { pluginChainFunc = this.makePluginChainFunc(methodName); - this.pluginChainFuncMap.put(methodName, pluginChainFunc); + if (pluginChainFunc != null) { + this.pluginChainFuncMap.put(methodName, pluginChainFunc); + } } if (pluginChainFunc == null) { @@ -251,8 +263,7 @@ protected T executeWithSubscribedPlugins( return pluginChainFunc.call(pluginPipeline, jdbcMethodFunc); } - @Nullable - protected PluginChainJdbcCallable makePluginChainFunc( + protected @Nullable PluginChainJdbcCallable makePluginChainFunc( final @NonNull String methodName) { PluginChainJdbcCallable pluginChainFunc = null; @@ -266,11 +277,12 @@ protected PluginChainJdbcCallable makePluginChain if (isSubscribed) { if (pluginChainFunc == null) { - pluginChainFunc = (pipelineFunc, jdbcFunc) -> pipelineFunc.call(plugin, jdbcFunc); + pluginChainFunc = (pipelineFunc, jdbcFunc) -> Objects.requireNonNull(pipelineFunc.call(plugin, jdbcFunc)); } else { final PluginChainJdbcCallable finalPluginChainFunc = pluginChainFunc; pluginChainFunc = (pipelineFunc, jdbcFunc) -> - pipelineFunc.call(plugin, () -> finalPluginChainFunc.call(pipelineFunc, jdbcFunc)); + Objects.requireNonNull(pipelineFunc.call(plugin, + () -> finalPluginChainFunc.call(pipelineFunc, jdbcFunc))); } } } @@ -278,9 +290,9 @@ protected PluginChainJdbcCallable makePluginChain } protected void notifySubscribedPlugins( - final String methodName, - final PluginPipeline pluginPipeline, - final ConnectionPlugin skipNotificationForThisPlugin) + final @NonNull String methodName, + final @Nullable PluginPipeline pluginPipeline, + final @Nullable ConnectionPlugin skipNotificationForThisPlugin) throws E { if (pluginPipeline == null) { @@ -302,16 +314,16 @@ protected void notifySubscribedPlugins( } } - public ConnectionWrapper getConnectionWrapper() { + public @NonNull @UnknownInitialization ConnectionWrapper getConnectionWrapper() { return this.connectionWrapper; } - public T execute( - final Class resultType, - final Class exceptionClass, - final Object methodInvokeOn, - final String methodName, - final JdbcCallable jdbcMethodFunc, + public @Nullable T execute( + final @Nullable Class resultType, + final @NonNull Class exceptionClass, + final @NonNull Object methodInvokeOn, + final @NonNull String methodName, + final @NonNull JdbcCallable jdbcMethodFunc, final Object[] jdbcMethodArgs) throws E { @@ -328,25 +340,25 @@ public T execute( methodName, (plugin, func) -> plugin.execute( - resultType, exceptionClass, methodInvokeOn, methodName, func, jdbcMethodArgs), + resultType, exceptionClass, methodInvokeOn, methodName, Objects.requireNonNull(func), jdbcMethodArgs), jdbcMethodFunc); } - public Connection connect( - final String driverProtocol, - final HostSpec hostSpec, - final Properties props, + public @NonNull Connection connect( + final @NonNull String driverProtocol, + final @NonNull HostSpec hostSpec, + final @NonNull Properties props, final boolean isInitialConnection) throws SQLException { try { - return executeWithSubscribedPlugins( + return Objects.requireNonNull(executeWithSubscribedPlugins( CONNECT_METHOD, (plugin, func) -> - plugin.connect(driverProtocol, hostSpec, props, isInitialConnection, func), + plugin.connect(driverProtocol, hostSpec, props, isInitialConnection, Objects.requireNonNull(func)), () -> { throw new SQLException("Shouldn't be called."); - }); + })); } catch (SQLException | RuntimeException e) { throw e; } catch (Exception e) { @@ -355,10 +367,10 @@ public Connection connect( } public void initHostProvider( - final String driverProtocol, - final String initialUrl, - final Properties props, - final HostListProviderService hostListProviderService) + final @NonNull String driverProtocol, + final @NonNull String initialUrl, + final @NonNull Properties props, + final @NonNull HostListProviderService hostListProviderService) throws SQLException { executeWithSubscribedPlugins( @@ -425,11 +437,11 @@ public void releaseResources() { private interface PluginPipeline { - T call(final @NonNull ConnectionPlugin plugin, final @Nullable JdbcCallable jdbcMethodFunc) throws E; + @Nullable T call(final @NonNull ConnectionPlugin plugin, final @Nullable JdbcCallable jdbcMethodFunc) throws E; } private interface PluginChainJdbcCallable { - T call(final @NonNull PluginPipeline pipelineFunc, final @NonNull JdbcCallable jdbcMethodFunc) throws E; + @NonNull T call(final @NonNull PluginPipeline pipelineFunc, final @NonNull JdbcCallable jdbcMethodFunc) throws E; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/JdbcCallable.java b/wrapper/src/main/java/software/amazon/jdbc/JdbcCallable.java index 89a13dbea..28ca6609b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/JdbcCallable.java +++ b/wrapper/src/main/java/software/amazon/jdbc/JdbcCallable.java @@ -16,7 +16,9 @@ package software.amazon.jdbc; +import org.checkerframework.checker.nullness.qual.Nullable; + public interface JdbcCallable { - T call() throws E; + @Nullable T call() throws E; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 2fbbd8fa5..e2473c7b2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -29,6 +29,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnderInitialization; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.cleanup.CanReleaseResources; @@ -44,7 +46,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); - protected final ConnectionPluginManager pluginManager; + protected final @NonNull @NotOnlyInitialized ConnectionPluginManager pluginManager; private final Properties props; private final String originalUrl; private final String driverProtocol; @@ -58,9 +60,9 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, private final ExceptionManager exceptionManager; public PluginServiceImpl( - @NonNull final ConnectionPluginManager pluginManager, - @NonNull final Properties props, - @NonNull final String originalUrl, + final @NonNull @UnderInitialization ConnectionPluginManager pluginManager, + final @NonNull Properties props, + final @NonNull String originalUrl, final String targetDriverProtocol) { this(pluginManager, new ExceptionManager(), props, originalUrl, targetDriverProtocol); } @@ -133,7 +135,7 @@ public void setCurrentConnection( public synchronized EnumSet setCurrentConnection( final @NonNull Connection connection, final @NonNull HostSpec hostSpec, - @Nullable final ConnectionPlugin skipNotificationForThisPlugin) + final @Nullable ConnectionPlugin skipNotificationForThisPlugin) throws SQLException { if (this.currentConnection == null) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java index ec6dad72f..b989b5a86 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java @@ -29,6 +29,8 @@ import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostAvailability; @@ -55,14 +57,14 @@ public final class DefaultConnectionPlugin implements ConnectionPlugin { Collections.singletonList("*"))); private static final SqlMethodAnalyzer sqlMethodAnalyzer = new SqlMethodAnalyzer(); - private final ConnectionProvider connectionProvider; + private final @UnknownInitialization ConnectionProvider connectionProvider; private final PluginService pluginService; private final PluginManagerService pluginManagerService; public DefaultConnectionPlugin( - final PluginService pluginService, - final ConnectionProvider connectionProvider, - final PluginManagerService pluginManagerService) { + final @NonNull PluginService pluginService, + final @NonNull @UnknownInitialization ConnectionProvider connectionProvider, + final @NonNull PluginManagerService pluginManagerService) { if (pluginService == null) { throw new IllegalArgumentException("pluginService"); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java b/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java index d27da60a3..34ba4b4aa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/Messages.java @@ -24,7 +24,7 @@ public class Messages { private static final ResourceBundle MESSAGES = ResourceBundle.getBundle("messages"); - private static final Object[] emptyArgs = {}; + public static final Object[] emptyArgs = {}; /** * Retrieve the localized error message associated with the provided key. @@ -36,7 +36,7 @@ public class Messages { return get(key, emptyArgs); } - public static @NonNull String get(@NonNull String key, @Nullable Object[] args) { + public static @NonNull String get(@NonNull String key, @Nullable Object @NonNull[] args) { final String message = MESSAGES.getString(key); return MessageFormat.format(message, args); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java b/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java index 7337b242f..1a559d7ce 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/PropertyUtils.java @@ -25,6 +25,7 @@ import java.util.Properties; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; public class PropertyUtils { private static final Logger LOGGER = Logger.getLogger(PropertyUtils.class.getName()); @@ -51,11 +52,11 @@ public static void applyProperties(final Object target, final Properties propert } public static void setPropertyOnTarget( - final Object target, - final String propName, - final Object propValue, - final List methods) { - Method writeMethod = null; + final @NonNull Object target, + final @NonNull String propName, + final @NonNull Object propValue, + final @NonNull List methods) { + @Nullable Method writeMethod = null; String methodName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1); for (Method method : methods) { @@ -102,7 +103,7 @@ public static void setPropertyOnTarget( () -> Messages.get( "PropertyUtils.failedToSetPropertyWithReason", - new Object[] {propName, target.getClass(), ex.getCause() == null ? null: ex.getCause().getMessage()})); + new Object[] {propName, target.getClass() == null ? Messages.emptyArgs : target.getClass(), ex.getCause() == null ? Messages.emptyArgs : ex.getCause().getMessage()})); throw new RuntimeException(ex.getCause()); } catch (Exception e) { LOGGER.warning( diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java b/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java index 538801893..d9f5ccd99 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/WrapperUtils.java @@ -50,6 +50,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.ConnectionPluginManager; import software.amazon.jdbc.JdbcCallable; @@ -81,6 +82,7 @@ public class WrapperUtils { private static final ConcurrentMap, Boolean> isJdbcInterfaceCache = new ConcurrentHashMap<>(); + @SuppressWarnings("method.invocation") private static final Map, Class> availableWrappers = new HashMap, Class>() { { @@ -105,6 +107,7 @@ public class WrapperUtils { } }; + @SuppressWarnings("method.invocation") private static final Set> allWrapperClasses = new HashSet>() { { add(ArrayWrapper.class); @@ -171,7 +174,7 @@ public static void runWithPlugins( jdbcMethodArgs); } - public static T executeWithPlugins( + public static @Nullable T executeWithPlugins( final Class resultClass, final ConnectionPluginManager pluginManager, final Object methodInvokeOn, @@ -202,7 +205,7 @@ public static T executeWithPlugins( } } - public static T executeWithPlugins( + public static @Nullable T executeWithPlugins( final Class resultClass, final Class exceptionClass, final ConnectionPluginManager pluginManager, @@ -235,7 +238,7 @@ public static T executeWithPlugins( } protected static @Nullable T wrapWithProxyIfNeeded( - final Class resultClass, @Nullable final T toProxy, final ConnectionPluginManager pluginManager) + final Class resultClass, final @Nullable T toProxy, final ConnectionPluginManager pluginManager) throws InstantiationException { if (toProxy == null) { @@ -292,7 +295,7 @@ public static T executeWithPlugins( * @param packageName the name of the package to analyze * @return true if the given package is a JDBC package */ - public static boolean isJdbcPackage(@Nullable final String packageName) { + public static boolean isJdbcPackage(final @Nullable String packageName) { return packageName != null && (packageName.startsWith("java.sql") || packageName.startsWith("javax.sql") @@ -407,16 +410,16 @@ public static List loadClasses( } } catch (final Throwable t) { - throw new InstantiationException(Messages.get(errorMessageResourceKey, new Object[] {lastClass.getName()})); + throw new InstantiationException(Messages.get(errorMessageResourceKey, lastClass == null ? Messages.emptyArgs : new Object[] {lastClass.getName()})); } return instances; } - public static T createInstance( - final Class classToInstantiate, - final Class resultClass, - final Class[] constructorArgClasses, + public static @NonNull T createInstance( + final @Nullable Class classToInstantiate, + final @Nullable Class resultClass, + final Class @Nullable[] constructorArgClasses, final Object... constructorArgs) throws InstantiationException { @@ -450,7 +453,7 @@ public static T createInstance( } } - public static T createInstance( + public static @NonNull T createInstance( final String className, final Class resultClass, final Object... constructorArgs) throws InstantiationException { @@ -475,7 +478,7 @@ public static T createInstance( return createInstance(loaded, resultClass, null, constructorArgs); } - public static Object getFieldValue(Object target, final String accessor) { + public static @Nullable Object getFieldValue(Object target, final String accessor) { if (target == null) { return null; } @@ -520,7 +523,7 @@ public static Object getFieldValue(Object target, final String accessor) { return target; } - public static Connection getConnectionFromSqlObject(final Object obj) { + public static @Nullable Connection getConnectionFromSqlObject(final Object obj) { if (obj == null) { return null; } @@ -532,7 +535,13 @@ public static Connection getConnectionFromSqlObject(final Object obj) { return stmt.getConnection(); } else if (obj instanceof ResultSet) { final ResultSet rs = (ResultSet) obj; - return rs.getStatement() != null ? rs.getStatement().getConnection() : null; + if (rs != null) { + Statement statement = rs.getStatement(); + if (statement != null) { + return statement.getConnection(); + } + } + return null; } } catch (final SQLException | UnsupportedOperationException e) { // Do nothing. The UnsupportedOperationException comes from ResultSets returned by diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 437908b0a..973cffbc1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -36,6 +36,9 @@ import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; +import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.ConnectionPluginManager; @@ -56,20 +59,21 @@ public class ConnectionWrapper implements Connection, CanReleaseResources { private static final Logger LOGGER = Logger.getLogger(ConnectionWrapper.class.getName()); - protected ConnectionPluginManager pluginManager; - protected PluginService pluginService; - protected HostListProviderService hostListProviderService; + protected @NotOnlyInitialized @NonNull ConnectionPluginManager pluginManager; + protected @NotOnlyInitialized @NonNull PluginService pluginService; + protected @NotOnlyInitialized @NonNull HostListProviderService hostListProviderService; - protected PluginManagerService pluginManagerService; + protected @NotOnlyInitialized @NonNull PluginManagerService pluginManagerService; protected String targetDriverProtocol; // TODO: consider moving to PluginService protected String originalUrl; // TODO: consider moving to PluginService protected @Nullable Throwable openConnectionStacktrace; + public ConnectionWrapper( - @NonNull final Properties props, - @NonNull final String url, - @NonNull final ConnectionProvider connectionProvider) + final @NonNull Properties props, + final @NonNull String url, + final @NonNull ConnectionProvider connectionProvider) throws SQLException { if (StringUtils.isNullOrEmpty(url)) { @@ -77,7 +81,7 @@ public ConnectionWrapper( } this.originalUrl = url; - this.targetDriverProtocol = getProtocol(url); + this.targetDriverProtocol = this.getProtocol(url); final ConnectionPluginManager pluginManager = new ConnectionPluginManager(connectionProvider, this); final PluginServiceImpl pluginService = new PluginServiceImpl(pluginManager, props, url, this.targetDriverProtocol); @@ -89,28 +93,33 @@ public ConnectionWrapper( } } + ConnectionWrapper( - @NonNull final Properties props, - @NonNull final String url, - @NonNull final ConnectionPluginManager connectionPluginManager, - @NonNull final PluginService pluginService, - @NonNull final HostListProviderService hostListProviderService, - @NonNull final PluginManagerService pluginManagerService) + final @NonNull Properties props, + final @NonNull String url, + final @UnderInitialization @NonNull ConnectionPluginManager connectionPluginManager, + final @UnderInitialization @NonNull PluginService pluginService, + final @UnderInitialization @NonNull HostListProviderService hostListProviderService, + final @UnderInitialization @NonNull PluginManagerService pluginManagerService) throws SQLException { if (StringUtils.isNullOrEmpty(url)) { throw new IllegalArgumentException("url"); } + this.originalUrl = url; + this.targetDriverProtocol = this.getProtocol(url); + init(props, connectionPluginManager, pluginService, hostListProviderService, pluginManagerService); } - protected void init( - final Properties props, - final ConnectionPluginManager connectionPluginManager, - final PluginService pluginService, - final HostListProviderService hostListProviderService, - final PluginManagerService pluginManagerService) throws SQLException { + @EnsuresNonNull({"pluginManager", "this.pluginService", "this.hostListProviderService", "this.pluginManagerService"}) + protected void init(@UnderInitialization(Object.class) ConnectionWrapper this, + final @NonNull Properties props, + final @UnderInitialization @NonNull ConnectionPluginManager connectionPluginManager, + final @UnderInitialization @NonNull PluginService pluginService, + final @UnderInitialization @NonNull HostListProviderService hostListProviderService, + final @UnderInitialization @NonNull PluginManagerService pluginManagerService) throws SQLException { this.pluginManager = connectionPluginManager; this.pluginService = pluginService; this.hostListProviderService = hostListProviderService; @@ -139,7 +148,7 @@ protected void init( } } - protected String getProtocol(final String url) { + protected String getProtocol(@UnderInitialization(Object.class) ConnectionWrapper this, final @NonNull String url) { final int index = url.indexOf("//"); if (index < 0) { throw new IllegalArgumentException( @@ -158,7 +167,7 @@ public void releaseResources() { } @Override - public void abort(final Executor executor) throws SQLException { + public void abort(final @NonNull Executor executor) throws SQLException { WrapperUtils.runWithPlugins( SQLException.class, this.pluginManager, @@ -210,7 +219,7 @@ public void commit() throws SQLException { } @Override - public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { + public Array createArrayOf(final @NonNull String typeName, final Object[] elements) throws SQLException { return WrapperUtils.executeWithPlugins( Array.class, SQLException.class,