diff --git a/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java index 6c5afcda3..a61c93df9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/DataSourceConnectionProvider.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import javax.sql.DataSource; import org.checkerframework.checker.nullness.qual.NonNull; @@ -36,6 +35,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.WrapperUtils; @@ -59,7 +59,7 @@ public class DataSourceConnectionProvider implements ConnectionProvider { private final @NonNull DataSource dataSource; private final @NonNull String dataSourceClassName; - private final ReentrantLock lock = new ReentrantLock(); + private final ResourceLock lock = new ResourceLock(); private final RdsUtils rdsUtils = new RdsUtils(); @@ -138,12 +138,9 @@ public Connection connect( // Data Source object could be shared between different threads while failover in progress. // That's why it's important to configure Data Source object and get connection atomically. - this.lock.lock(); LOGGER.finest(() -> "Use main DataSource object to create a connection."); - try { + try (ResourceLock ignored = this.lock.obtain()) { conn = this.openConnection(this.dataSource, protocol, targetDriverDialect, hostSpec, copy); - } finally { - this.lock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index b6a5c318c..cd1532624 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -32,7 +32,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -54,6 +53,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -90,7 +90,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected final SessionStateService sessionStateService; - protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); + protected final ResourceLock connectionSwitchLock = new ResourceLock(); public PluginServiceImpl( @NonNull final FullServicesContainer servicesContainer, @@ -281,8 +281,7 @@ public EnumSet setCurrentConnection( @Nullable final ConnectionPlugin skipNotificationForThisPlugin) throws SQLException { - connectionSwitchLock.lock(); - try { + try (ResourceLock ignored = connectionSwitchLock.obtain()) { if (this.currentConnection == null) { // setting up an initial connection @@ -350,8 +349,6 @@ public EnumSet setCurrentConnection( } return changes; } - } finally { - connectionSwitchLock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java b/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java index 205af0574..d897a21a5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java +++ b/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -31,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.storage.CacheMap; @@ -48,7 +48,7 @@ public class RoundRobinHostSelector implements HostSelector { Pattern.compile("((?[^:/?#]*):(?[0-9]*))"); protected static final CacheMap roundRobinCache = new CacheMap<>(); - protected static final ReentrantLock lock = new ReentrantLock(); + protected static final ResourceLock lock = new ResourceLock(); static { PropertyDefinition.registerPluginProperties(RoundRobinHostSelector.class); @@ -76,8 +76,7 @@ public HostSpec getHost( final @NonNull HostRole role, final @Nullable Properties props) throws SQLException { - lock.lock(); - try { + try (ResourceLock ignored = lock.obtain()) { final List eligibleHosts = hosts.stream() .filter(hostSpec -> role.equals(hostSpec.getRole()) && hostSpec.getAvailability().equals(HostAvailability.AVAILABLE)) @@ -125,8 +124,6 @@ public HostSpec getHost( return eligibleHosts.get(targetHostIndex); - } finally { - lock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/WeightedRandomHostSelector.java b/wrapper/src/main/java/software/amazon/jdbc/WeightedRandomHostSelector.java index 8ca372ca2..20ff616e3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/WeightedRandomHostSelector.java +++ b/wrapper/src/main/java/software/amazon/jdbc/WeightedRandomHostSelector.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Properties; import java.util.Random; -import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -31,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ResourceLock; public class WeightedRandomHostSelector implements HostSelector { public static final AwsWrapperProperty WEIGHTED_RANDOM_HOST_WEIGHT_PAIRS = new AwsWrapperProperty( @@ -45,7 +45,7 @@ public class WeightedRandomHostSelector implements HostSelector { private String cachedHostWeightMapString; private Random random; - private final ReentrantLock lock = new ReentrantLock(); + private final ResourceLock lock = new ResourceLock(); public WeightedRandomHostSelector() { this(new Random()); @@ -109,8 +109,7 @@ public HostSpec getHost( } private Map getHostWeightPairMap(final String hostWeightMapString) throws SQLException { - try { - lock.lock(); + try (ResourceLock ignored = lock.obtain()) { if (this.cachedHostWeightMapString != null && this.cachedHostWeightMapString.trim().equals(hostWeightMapString.trim()) && this.cachedHostWeightMap != null @@ -148,8 +147,6 @@ private Map getHostWeightPairMap(final String hostWeightMapStri this.cachedHostWeightMap = hostWeightMap; this.cachedHostWeightMapString = hostWeightMapString; return hostWeightMap; - } finally { - lock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/authentication/AwsCredentialsManager.java b/wrapper/src/main/java/software/amazon/jdbc/authentication/AwsCredentialsManager.java index c562dd48e..f14b27c23 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/authentication/AwsCredentialsManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/authentication/AwsCredentialsManager.java @@ -17,40 +17,33 @@ package software.amazon.jdbc.authentication; import java.util.Properties; -import java.util.concurrent.locks.ReentrantLock; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PropertyDefinition; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.StringUtils; public class AwsCredentialsManager { private static AwsCredentialsProviderHandler handler = null; - private static final ReentrantLock lock = new ReentrantLock(); + private static final ResourceLock lock = new ResourceLock(); public static void setCustomHandler(final AwsCredentialsProviderHandler customHandler) { - lock.lock(); - try { + try (ResourceLock ignored = lock.obtain()) { handler = customHandler; - } finally { - lock.unlock(); } } public static void resetCustomHandler() { - lock.lock(); - try { + try (ResourceLock ignored = lock.obtain()) { handler = null; - } finally { - lock.unlock(); } } public static AwsCredentialsProvider getProvider(final HostSpec hostSpec, final Properties props) { - lock.lock(); - try { + try (ResourceLock ignored = lock.obtain()) { AwsCredentialsProvider provider = handler == null ? null : handler.getAwsCredentialsProvider(hostSpec, props); @@ -60,8 +53,6 @@ public static AwsCredentialsProvider getProvider(final HostSpec hostSpec, final } return provider; - } finally { - lock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index 738eebcc3..7775fe8c2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -35,7 +35,6 @@ import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; @@ -52,6 +51,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; @@ -106,7 +106,7 @@ public class RdsHostListProvider implements DynamicHostListProvider { protected List initialHostList = new ArrayList<>(); protected HostSpec initialHostSpec; - protected final ReentrantLock lock = new ReentrantLock(); + protected final ResourceLock lock = new ResourceLock(); protected String clusterId; protected HostSpec clusterInstanceTemplate; @@ -143,8 +143,7 @@ protected void init() throws SQLException { return; } - lock.lock(); - try { + try (ResourceLock ignored = lock.obtain()) { if (this.isInitialized) { return; } @@ -210,8 +209,6 @@ protected void init() throws SQLException { } this.isInitialized = true; - } finally { - lock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 5800c993b..e5597cc2d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -37,7 +37,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -53,6 +52,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.ServiceUtility; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; @@ -101,7 +101,7 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected final AtomicLong ignoreNewTopologyRequestsEndTimeNano = new AtomicLong(-1); protected final ConcurrentHashMap submittedNodes = new ConcurrentHashMap<>(); protected ExecutorService nodeExecutorService = null; - protected final ReentrantLock nodeExecutorLock = new ReentrantLock(); + protected final ResourceLock nodeExecutorLock = new ResourceLock(); protected final AtomicBoolean nodeThreadsStop = new AtomicBoolean(false); protected final AtomicReference nodeThreadsWriterConnection = new AtomicReference<>(null); protected final AtomicReference nodeThreadsWriterHostSpec = new AtomicReference<>(null); @@ -506,8 +506,7 @@ public void processEvent(Event event) { protected void shutdownNodeExecutorService() { if (this.nodeExecutorService != null) { - this.nodeExecutorLock.lock(); - try { + try (ResourceLock ignored = this.nodeExecutorLock.obtain()) { if (this.nodeExecutorService == null) { return; @@ -526,18 +525,13 @@ protected void shutdownNodeExecutorService() { } this.nodeExecutorService = null; - } finally { - this.nodeExecutorLock.unlock(); } } } protected void createNodeExecutorService() { - this.nodeExecutorLock.lock(); - try { + try (ResourceLock ignored = this.nodeExecutorLock.obtain()) { this.nodeExecutorService = ExecutorFactory.newCachedThreadPool("node"); - } finally { - this.nodeExecutorLock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java index 72696b85e..ffc6a302f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java @@ -31,7 +31,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -59,6 +58,7 @@ import software.amazon.jdbc.util.Pair; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.events.MonitorResetEvent; @@ -114,7 +114,7 @@ public class BlueGreenStatusProvider { protected AtomicBoolean monitorResetOnTopologyCompleted = new AtomicBoolean(false); protected final AtomicBoolean allGreenNodesChangedName = new AtomicBoolean(false); protected long postStatusEndTimeNano = 0; - protected final ReentrantLock processStatusLock = new ReentrantLock(); + protected final ResourceLock processStatusLock = new ResourceLock(); // Status check interval time in millis for each BlueGreenIntervalRate. protected final Map statusCheckIntervalMap = new HashMap<>(); @@ -202,8 +202,7 @@ protected void prepareStatus( final @NonNull BlueGreenRole role, final @NonNull BlueGreenInterimStatus interimStatus) { - this.processStatusLock.lock(); - try { + try (ResourceLock ignored = this.processStatusLock.obtain()) { // Detect changes int statusHash = interimStatus.getCustomHashCode(); @@ -243,8 +242,6 @@ protected void prepareStatus( this.resetContextWhenCompleted(); - } finally { - this.processStatusLock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java index 5244d469e..8d7495e2a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java @@ -20,10 +20,10 @@ import java.sql.SQLException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.telemetry.TelemetryCounter; /** @@ -51,7 +51,7 @@ public class HostMonitorConnectionContext { private long invalidNodeStartTimeNano; // Only accessed by monitor thread private long failureCount; // Only accessed by monitor thread - private final ReentrantLock lock = new ReentrantLock(); + private final ResourceLock lock = new ResourceLock(); /** * Constructor. @@ -246,7 +246,7 @@ void setConnectionValid( new Object[] {hostName})); } - public ReentrantLock getLock() { + public ResourceLock getLock() { return this.lock; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java index 3d605679c..d1c9415a9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java @@ -24,9 +24,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ResourceLock; /** * This singleton class keeps track of all the monitoring threads and handles the creation and clean @@ -38,8 +38,8 @@ public class HostMonitorThreadContainer { private final Map> tasksMap = new ConcurrentHashMap<>(); private final Map monitorMap = new ConcurrentHashMap<>(); private final ExecutorService threadPool; - private static final ReentrantLock LOCK_OBJECT = new ReentrantLock(); - private static final ReentrantLock MONITOR_LOCK_OBJECT = new ReentrantLock(); + private static final ResourceLock LOCK_OBJECT = new ResourceLock(); + private static final ResourceLock MONITOR_LOCK_OBJECT = new ResourceLock(); /** * Create an instance of the {@link HostMonitorThreadContainer}. @@ -57,14 +57,11 @@ static HostMonitorThreadContainer getInstance(final ExecutorServiceInitializer e return singletonToReturn; } - LOCK_OBJECT.lock(); - try { + try (ResourceLock ignored = LOCK_OBJECT.obtain()) { if (singleton == null) { singleton = new HostMonitorThreadContainer(executorServiceInitializer); } singletonToReturn = singleton; - } finally { - LOCK_OBJECT.unlock(); } return singletonToReturn; } @@ -77,14 +74,11 @@ public static void releaseInstance() { if (singleton == null) { return; } - LOCK_OBJECT.lock(); - try { + try (ResourceLock ignored = LOCK_OBJECT.obtain()) { if (singleton != null) { singleton.releaseResources(); singleton = null; } - } finally { - LOCK_OBJECT.unlock(); } } @@ -118,8 +112,7 @@ HostMonitor getOrCreateMonitor(final Set nodeKeys, final Supplier nodeKeys, final Supplier monitorList = Collections.singletonList(monitor); - MONITOR_LOCK_OBJECT.lock(); - try { + try (ResourceLock ignored = MONITOR_LOCK_OBJECT.obtain()) { monitorMap.values().removeAll(monitorList); tasksMap.computeIfPresent( monitor, @@ -180,14 +170,11 @@ public void releaseResource(final HostMonitor monitor) { v.cancel(true); return null; }); - } finally { - MONITOR_LOCK_OBJECT.unlock(); } } public void releaseResources() { - MONITOR_LOCK_OBJECT.lock(); - try { + try (ResourceLock ignored = MONITOR_LOCK_OBJECT.obtain()) { monitorMap.clear(); tasksMap.values().stream() .filter(val -> !val.isDone() && !val.isCancelled()) @@ -196,8 +183,6 @@ public void releaseResources() { if (threadPool != null) { threadPool.shutdownNow(); } - } finally { - MONITOR_LOCK_OBJECT.unlock(); } } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java index ae4e7b026..214de6081 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java @@ -25,7 +25,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.AwsWrapperProperty; @@ -39,6 +38,7 @@ import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.HostSelectorUtils; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.monitoring.MonitorErrorResponse; @@ -50,7 +50,7 @@ public class LimitlessRouterServiceImpl implements LimitlessRouterService { "limitlessTransactionRouterMonitorDisposalTimeMs", "600000", // 10min "Interval in milliseconds for an Limitless router monitor to be considered inactive and to be disposed."); - protected static final Map forceGetLimitlessRoutersLockMap = new ConcurrentHashMap<>(); + protected static final Map forceGetLimitlessRoutersLockMap = new ConcurrentHashMap<>(); protected static final Set monitorErrorResponses = new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); protected final FullServicesContainer servicesContainer; @@ -278,12 +278,11 @@ protected void synchronouslyGetLimitlessRoutersWithRetry(final LimitlessConnecti protected void synchronouslyGetLimitlessRouters(final LimitlessConnectionContext context) throws SQLException { - final ReentrantLock lock = forceGetLimitlessRoutersLockMap.computeIfAbsent( + final ResourceLock lock = forceGetLimitlessRoutersLockMap.computeIfAbsent( this.pluginService.getHostListProvider().getClusterId(), - key -> new ReentrantLock() + key -> new ResourceLock() ); - lock.lock(); - try { + try (ResourceLock ignored = lock.obtain()) { final List limitlessRouters = getLimitlessRouters(this.pluginService.getHostListProvider().getClusterId()); if (!Utils.isNullOrEmpty(limitlessRouters)) { @@ -306,8 +305,6 @@ protected void synchronouslyGetLimitlessRouters(final LimitlessConnectionContext } else { throw new SQLException(Messages.get("LimitlessRouterServiceImpl.fetchedEmptyRouterList")); } - } finally { - lock.unlock(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/profile/ConfigurationProfile.java b/wrapper/src/main/java/software/amazon/jdbc/profile/ConfigurationProfile.java index c8bf2af67..a6a1d4daa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/profile/ConfigurationProfile.java +++ b/wrapper/src/main/java/software/amazon/jdbc/profile/ConfigurationProfile.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Properties; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -28,6 +27,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.exceptions.ExceptionHandler; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ResourceLock; public class ConfigurationProfile { @@ -46,7 +46,7 @@ public class ConfigurationProfile { private @Nullable AwsCredentialsProviderHandler awsCredentialsProviderHandler; private @Nullable ConnectionProvider connectionProvider; - private final ReentrantLock lock = new ReentrantLock(); + private final ResourceLock lock = new ResourceLock(); ConfigurationProfile(final @NonNull String name, @Nullable List> pluginFactories, @@ -106,12 +106,9 @@ public class ConfigurationProfile { return null; } - this.lock.lock(); - try { + try (ResourceLock ignored = this.lock.obtain()) { this.dialect = this.dialectSupplier.get(); return this.dialect; - } finally { - this.lock.unlock(); } } @@ -122,15 +119,12 @@ public class ConfigurationProfile { if (this.targetDriverDialectSupplier == null) { return null; } - try { - this.lock.lock(); + try (ResourceLock ignored = this.lock.obtain()) { if (this.targetDriverDialect != null) { return this.targetDriverDialect; } this.targetDriverDialect = this.targetDriverDialectSupplier.get(); return this.targetDriverDialect; - } finally { - this.lock.unlock(); } } @@ -141,15 +135,12 @@ public class ConfigurationProfile { if (this.exceptionHandlerSupplier == null) { return null; } - try { - this.lock.lock(); + try (ResourceLock ignored = this.lock.obtain()) { if (this.exceptionHandler != null) { return this.exceptionHandler; } this.exceptionHandler = this.exceptionHandlerSupplier.get(); return this.exceptionHandler; - } finally { - this.lock.unlock(); } } @@ -160,15 +151,12 @@ public class ConfigurationProfile { if (this.connectionProviderSupplier == null) { return null; } - try { - this.lock.lock(); + try (ResourceLock ignored = this.lock.obtain()) { if (this.connectionProvider != null) { return this.connectionProvider; } this.connectionProvider = this.connectionProviderSupplier.get(); return this.connectionProvider; - } finally { - this.lock.unlock(); } } @@ -179,15 +167,12 @@ public class ConfigurationProfile { if (this.awsCredentialsProviderHandlerSupplier == null) { return null; } - try { - this.lock.lock(); + try (ResourceLock ignored = this.lock.obtain()) { if (this.awsCredentialsProviderHandler != null) { return this.awsCredentialsProviderHandler; } this.awsCredentialsProviderHandler = this.awsCredentialsProviderHandlerSupplier.get(); return this.awsCredentialsProviderHandler; - } finally { - this.lock.unlock(); } } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ResourceLock.java b/wrapper/src/main/java/software/amazon/jdbc/util/ResourceLock.java new file mode 100644 index 000000000..e6168a18e --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ResourceLock.java @@ -0,0 +1,38 @@ +/* + * 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.util; + +import java.util.concurrent.locks.ReentrantLock; + +public class ResourceLock extends ReentrantLock implements AutoCloseable { + + /** + * Obtain a lock and return the ResourceLock for use in try-with-resources block. + */ + public software.amazon.jdbc.util.ResourceLock obtain() { + lock(); + return this; + } + + /** + * Unlock on exit of try-with-resources block. + */ + @Override + public void close() { + this.unlock(); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java index 36c0fbfaa..960355614 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java @@ -18,9 +18,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import software.amazon.jdbc.util.ExecutorFactory; +import software.amazon.jdbc.util.ResourceLock; public class SlidingExpirationCacheWithCleanupThread extends SlidingExpirationCache { @@ -29,7 +29,7 @@ public class SlidingExpirationCacheWithCleanupThread extends SlidingExpira protected final ExecutorService cleanupThreadPool = ExecutorFactory.newFixedThreadPool(1, "threadPool"); - protected final ReentrantLock initLock = new ReentrantLock(); + protected final ResourceLock initLock = new ResourceLock(); protected boolean isInitialized = false; public SlidingExpirationCacheWithCleanupThread() { @@ -54,8 +54,7 @@ public SlidingExpirationCacheWithCleanupThread( protected void initCleanupThread() { if (!isInitialized) { - initLock.lock(); - try { + try (ResourceLock ignored = initLock.obtain()) { if (!isInitialized) { cleanupThreadPool.submit(() -> { while (true) { @@ -75,8 +74,6 @@ protected void initCleanupThread() { cleanupThreadPool.shutdown(); isInitialized = true; } - } finally { - initLock.unlock(); } } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java index 989db9877..240b3e7f4 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java @@ -41,7 +41,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.RepeatedTest; @@ -50,6 +49,7 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.ResourceLock; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -71,7 +71,7 @@ class HostMonitorImplTest { @Mock TelemetryFactory telemetryFactory; @Mock TelemetryContext telemetryContext; @Mock TelemetryCounter telemetryCounter; - @Mock ReentrantLock mockReentrantLock; + @Mock ResourceLock mockResourceLock; private static final long SHORT_INTERVAL_MILLIS = 30; private static final long SHORT_INTERVAL_SECONDS = TimeUnit.MILLISECONDS.toSeconds(SHORT_INTERVAL_MILLIS); @@ -90,7 +90,7 @@ void init() throws SQLException { when(contextWithLongInterval.getFailureDetectionIntervalMillis()) .thenReturn(LONG_INTERVAL_MILLIS); when(contextWithShortInterval.getLock()) - .thenReturn(mockReentrantLock); + .thenReturn(mockResourceLock); when(booleanProperty.getStringValue()).thenReturn(Boolean.TRUE.toString()); when(longProperty.getValue()).thenReturn(SHORT_INTERVAL_MILLIS); when(pluginService.forceConnect(any(HostSpec.class), any(Properties.class))).thenReturn(connection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java index 63320e95d..f272e6166 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java @@ -41,7 +41,6 @@ import java.util.HashSet; import java.util.Properties; import java.util.Set; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; @@ -66,6 +65,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ResourceLock; class HostMonitoringConnectionPluginTest { @@ -88,7 +88,7 @@ class HostMonitoringConnectionPluginTest { @Mock Supplier supplier; @Mock RdsUtils rdsUtils; @Mock HostMonitorConnectionContext context; - @Mock ReentrantLock mockReentrantLock; + @Mock ResourceLock mockResourceLock; @Mock HostMonitorService monitorService; @Mock JdbcCallable sqlFunction; @Mock TargetDriverDialect targetDriverDialect; @@ -136,7 +136,7 @@ void initDefaultMockReturns() throws Exception { anyInt(), anyInt())) .thenReturn(context); - when(context.getLock()).thenReturn(mockReentrantLock); + when(context.getLock()).thenReturn(mockResourceLock); when(pluginService.getCurrentConnection()).thenReturn(connection); when(pluginService.getCurrentHostSpec()).thenReturn(hostSpec); diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java index afcae7530..7550fc46d 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/WrapperUtilsTest.java @@ -59,13 +59,13 @@ public class WrapperUtilsTest { @Mock TelemetryFactory mockTelemetryFactory; @Mock TelemetryContext mockTelemetryContext; @Mock Object object; - ReentrantLock testLock; + ResourceLock testLock; private AutoCloseable closeable; @BeforeEach @SuppressWarnings("unchecked") void init() { - testLock = new ReentrantLock(); + testLock = new ResourceLock(); closeable = MockitoAnnotations.openMocks(this); mockExecuteReturnValue(1); @@ -77,13 +77,13 @@ void init() { private void mockExecuteReturnValue(Object returnValue) { doAnswer(invocation -> { - boolean lockIsFree = testLock.tryLock(); - if (!lockIsFree) { - fail("Lock is in use, should not be attempting to fetch it right now"); + try (ResourceLock lockIsFree = testLock.obtain()) { + if (lockIsFree == null) { + fail("Lock is in use, should not be attempting to fetch it right now"); + } + Thread.sleep(3000); + return returnValue; } - Thread.sleep(3000); - testLock.unlock(); - return returnValue; }).when(mockPluginManager).execute( any(Class.class), any(Class.class),