From 7ea41c4d95e2964a10f881b8f67db23d8662bb50 Mon Sep 17 00:00:00 2001 From: damien Date: Mon, 4 Mar 2024 21:50:09 +0100 Subject: [PATCH] rewrite the jdbc configuration validation --- pom.xml | 2 +- .../resources/META-INF/quarkus-extension.yaml | 1 + .../JdbcSchedulerLockProcessor.java | 74 +++++++++++++------ ...LockableServiceUsingUnknownDataSource.java | 12 +++ ...ailWhenUsedDataSourceIsNotPresentTest.java | 2 +- .../jdbc/runtime/DataSourceName.java | 5 ++ .../jdbc/runtime/DataSourceNameRecorder.java | 12 +++ .../runtime/JdbcLockProviderInitializer.java | 60 +++++++++------ .../resources/META-INF/quarkus-extension.yaml | 1 + .../resources/META-INF/quarkus-extension.yaml | 1 + 10 files changed, 121 insertions(+), 49 deletions(-) create mode 100644 providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/LockableServiceUsingUnknownDataSource.java create mode 100644 providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceName.java create mode 100644 providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceNameRecorder.java diff --git a/pom.xml b/pom.xml index eaf90a5..7b47c5a 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 17 UTF-8 UTF-8 - 3.7.4 + 3.8.1 5.11.0 diff --git a/providers/inmemory/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/providers/inmemory/runtime/src/main/resources/META-INF/quarkus-extension.yaml index d02dd55..ec6f439 100644 --- a/providers/inmemory/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/providers/inmemory/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -3,6 +3,7 @@ description: "Distributed lock for your scheduled tasks" metadata: keywords: - "shedlock" + - "provider" - "inmemory" # guide: https://quarkiverse.github.io/quarkiverse-docs/shedlock/dev/ # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension categories: diff --git a/providers/jdbc/deployment/src/main/java/io/quarkiverse/shedlock/providers/jdbc/deployment/JdbcSchedulerLockProcessor.java b/providers/jdbc/deployment/src/main/java/io/quarkiverse/shedlock/providers/jdbc/deployment/JdbcSchedulerLockProcessor.java index 3539464..2dd5956 100644 --- a/providers/jdbc/deployment/src/main/java/io/quarkiverse/shedlock/providers/jdbc/deployment/JdbcSchedulerLockProcessor.java +++ b/providers/jdbc/deployment/src/main/java/io/quarkiverse/shedlock/providers/jdbc/deployment/JdbcSchedulerLockProcessor.java @@ -1,18 +1,22 @@ package io.quarkiverse.shedlock.providers.jdbc.deployment; import java.util.List; +import java.util.stream.Stream; + +import jakarta.inject.Singleton; import org.jboss.jandex.DotName; -import io.quarkiverse.shedlock.providers.jdbc.runtime.JdbcConfig; -import io.quarkiverse.shedlock.providers.jdbc.runtime.JdbcLockProviderInitializer; -import io.quarkiverse.shedlock.providers.jdbc.runtime.JdbcSchedulerLock; -import io.quarkiverse.shedlock.providers.jdbc.runtime.JdbcSchedulerLockInterceptor; +import io.quarkiverse.shedlock.providers.jdbc.runtime.*; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem; +import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ApplicationIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; @@ -37,26 +41,50 @@ void produceBeans(final BuildProducer additionalBeanBui @BuildStep List validateDataSourcesDefinitionsWhenJdbcShedLockIsUsed( final ApplicationIndexBuildItem applicationIndexBuildItem, - final JdbcConfig jdbcConfig, final List jdbcDataSourceBuildItems) { + final List dataSourceNames = getDataSourcesNameFromJdbcSchedulerLocks(applicationIndexBuildItem); + return dataSourceNames + .stream() + .filter(shedLockDataSourceName -> jdbcDataSourceBuildItems.stream().map(JdbcDataSourceBuildItem::getName) + .noneMatch(jdbcDataSourceName -> jdbcDataSourceName.equals(shedLockDataSourceName))) + .map(missingDataSource -> new ValidationErrorBuildItem( + new IllegalStateException(String.format( + "A missing datasource '%s' has been defined for ShedLock. Please fixe the ShedLock configuration or add the datasource", + missingDataSource)))) + .toList(); + } + + @BuildStep + @Record(ExecutionTime.STATIC_INIT) + List registerJdbcLockProviderInitializer( + final ApplicationIndexBuildItem applicationIndexBuildItem, + final DataSourceNameRecorder dataSourceNameRecorder) { + final List dataSourceNames = getDataSourcesNameFromJdbcSchedulerLocks(applicationIndexBuildItem); + List list = dataSourceNames.stream() + .map(dataSourceName -> SyntheticBeanBuildItem.configure(DataSourceName.class) + .scope(Singleton.class) + .identifier(dataSourceName) + .supplier(dataSourceNameRecorder.dataSourceNameSupplier(dataSourceName)) + .unremovable() + .done()) + .toList(); + return list; + } + + private List getDataSourcesNameFromJdbcSchedulerLocks(ApplicationIndexBuildItem applicationIndexBuildItem) { final DotName jdbcSchedulerLock = DotName.createSimple(JdbcSchedulerLock.class); - final boolean shouldCheckDataSourcesBecauseLockIsUsed = applicationIndexBuildItem.getIndex() - .getKnownClasses().stream() - .anyMatch(classInfo -> classInfo.hasAnnotation(jdbcSchedulerLock) || classInfo.methods().stream() - .anyMatch(methodInfo -> methodInfo.hasAnnotation(jdbcSchedulerLock))); - if (shouldCheckDataSourcesBecauseLockIsUsed) { - return jdbcConfig.dataSources() - .keySet() - .stream() - .filter(shedLockDataSourceName -> jdbcDataSourceBuildItems.stream().map(JdbcDataSourceBuildItem::getName) - .noneMatch(jdbcDataSourceName -> jdbcDataSourceName.equals(shedLockDataSourceName))) - .map(missingDataSource -> new ValidationErrorBuildItem( - new IllegalStateException(String.format( - "A missing datasource '%s' has been defined for ShedLock. Please fixe the ShedLock configuration or add the datasource", - missingDataSource)))) - .toList(); - } else { - return List.of(); - } + return Stream.concat( + applicationIndexBuildItem.getIndex().getKnownClasses() + .stream().filter(classInfo -> classInfo.hasAnnotation(jdbcSchedulerLock)) + .map(classInfo -> classInfo.annotation(jdbcSchedulerLock)), + applicationIndexBuildItem.getIndex().getKnownClasses().stream() + .flatMap(classInfo -> classInfo.methods().stream()) + .filter(methodInfo -> methodInfo.hasAnnotation(jdbcSchedulerLock)) + .map(methodInfo -> methodInfo.annotation(jdbcSchedulerLock))) + .map(annotationInstance -> annotationInstance.value("dataSourceName")) + .map(dataSourceName -> dataSourceName != null ? dataSourceName.asString() + : DataSourceUtil.DEFAULT_DATASOURCE_NAME) + .distinct() + .toList(); } } diff --git a/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/LockableServiceUsingUnknownDataSource.java b/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/LockableServiceUsingUnknownDataSource.java new file mode 100644 index 0000000..28d3fe8 --- /dev/null +++ b/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/LockableServiceUsingUnknownDataSource.java @@ -0,0 +1,12 @@ +package io.quarkiverse.shedlock.providers.jdbc.deployment; + +import jakarta.enterprise.context.ApplicationScoped; + +import io.quarkiverse.shedlock.providers.jdbc.runtime.JdbcSchedulerLock; + +@ApplicationScoped +public class LockableServiceUsingUnknownDataSource { + @JdbcSchedulerLock(dataSourceName = "unknownDataSource") + void execute() { + } +} diff --git a/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/ShouldFailWhenUsedDataSourceIsNotPresentTest.java b/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/ShouldFailWhenUsedDataSourceIsNotPresentTest.java index 6287c76..c3208ce 100644 --- a/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/ShouldFailWhenUsedDataSourceIsNotPresentTest.java +++ b/providers/jdbc/deployment/src/test/java/io/quarkiverse/shedlock/providers/jdbc/deployment/ShouldFailWhenUsedDataSourceIsNotPresentTest.java @@ -19,7 +19,7 @@ public class ShouldFailWhenUsedDataSourceIsNotPresentTest { @RegisterExtension static final QuarkusUnitTest unitTest = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClasses(LockableService.class) + .addClasses(LockableServiceUsingUnknownDataSource.class) .addAsResource(new StringAsset("quarkus.shedlock.defaults-lock-at-most-for=PT30S\n" + "quarkus.shedlock.jdbc.unknownDataSource.table-name=myShedLockTableName"), "application.properties")) diff --git a/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceName.java b/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceName.java new file mode 100644 index 0000000..b492522 --- /dev/null +++ b/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceName.java @@ -0,0 +1,5 @@ +package io.quarkiverse.shedlock.providers.jdbc.runtime; + +public interface DataSourceName { + String name(); +} diff --git a/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceNameRecorder.java b/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceNameRecorder.java new file mode 100644 index 0000000..75eaf37 --- /dev/null +++ b/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/DataSourceNameRecorder.java @@ -0,0 +1,12 @@ +package io.quarkiverse.shedlock.providers.jdbc.runtime; + +import java.util.function.Supplier; + +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class DataSourceNameRecorder { + public Supplier dataSourceNameSupplier(final String dataSourceName) { + return () -> (DataSourceName) () -> dataSourceName; + } +} diff --git a/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/JdbcLockProviderInitializer.java b/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/JdbcLockProviderInitializer.java index 1d620f4..e863c5b 100644 --- a/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/JdbcLockProviderInitializer.java +++ b/providers/jdbc/runtime/src/main/java/io/quarkiverse/shedlock/providers/jdbc/runtime/JdbcLockProviderInitializer.java @@ -3,13 +3,17 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.List; import java.util.Objects; +import java.util.Optional; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.Default; +import jakarta.enterprise.inject.Instance; import io.agroal.api.AgroalDataSource; +import io.quarkiverse.shedlock.common.runtime.SchedulerLockInterceptorBase; import io.quarkus.agroal.DataSource; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -18,34 +22,42 @@ @ApplicationScoped public class JdbcLockProviderInitializer { private final JdbcConfig jdbcConfig; + private final List dataSourcesName; - public JdbcLockProviderInitializer(final JdbcConfig jdbcConfig) { + public JdbcLockProviderInitializer(final JdbcConfig jdbcConfig, + final Instance dataSourcesName) { this.jdbcConfig = Objects.requireNonNull(jdbcConfig); + this.dataSourcesName = Objects.requireNonNull(dataSourcesName).stream().toList(); } void createTable(@Observes StartupEvent startupEvent) { - jdbcConfig.dataSources().forEach((dataSourceName, dataSourceConfig) -> { - final AgroalDataSource agroalDataSource = Arc.container() - .select(AgroalDataSource.class, - DataSourceUtil.DEFAULT_DATASOURCE_NAME.equals(dataSourceName) ? new Default.Literal() - : new DataSource.DataSourceLiteral(dataSourceName)) - .get(); - final String databaseCreationSql = """ - CREATE TABLE IF NOT EXISTS %s ( - name VARCHAR(255), - lock_until TIMESTAMP(3) NULL, - locked_at TIMESTAMP(3) NULL, - locked_by VARCHAR(255), - PRIMARY KEY (name) - ) - """; - try (final Connection connection = agroalDataSource.getConnection()) { - final PreparedStatement preparedStatement = connection - .prepareStatement(String.format(databaseCreationSql, dataSourceConfig.tableName())); - preparedStatement.execute(); - } catch (SQLException e) { - throw new RuntimeException(e); - } - }); + dataSourcesName + .stream().map(DataSourceName::name) + .forEach(dataSourceName -> { + final AgroalDataSource agroalDataSource = Arc.container() + .select(AgroalDataSource.class, + DataSourceUtil.DEFAULT_DATASOURCE_NAME.equals(dataSourceName) ? new Default.Literal() + : new DataSource.DataSourceLiteral(dataSourceName)) + .get(); + final String databaseCreationSql = """ + CREATE TABLE IF NOT EXISTS %s ( + name VARCHAR(255), + lock_until TIMESTAMP(3) NULL, + locked_at TIMESTAMP(3) NULL, + locked_by VARCHAR(255), + PRIMARY KEY (name) + ) + """; + try (final Connection connection = agroalDataSource.getConnection()) { + final String tableName = Optional.ofNullable(jdbcConfig.dataSources().get(dataSourceName)) + .map(JdbcConfig.DataSourceConfig::tableName) + .orElse(SchedulerLockInterceptorBase.SHED_LOCK); + final PreparedStatement preparedStatement = connection + .prepareStatement(String.format(databaseCreationSql, tableName)); + preparedStatement.execute(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }); } } diff --git a/providers/jdbc/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/providers/jdbc/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 4a6e9db..fc77dcb 100644 --- a/providers/jdbc/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/providers/jdbc/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -3,6 +3,7 @@ description: "Distributed lock for your scheduled tasks" metadata: keywords: - "shedlock" + - "provider" - "jdbc" # guide: https://quarkiverse.github.io/quarkiverse-docs/shedlock/dev/ # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension categories: diff --git a/providers/mongo/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/providers/mongo/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 2011252..136144a 100644 --- a/providers/mongo/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/providers/mongo/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -3,6 +3,7 @@ description: "Distributed lock for your scheduled tasks" metadata: keywords: - "shedlock" + - "provider" - "mongo" # guide: https://quarkiverse.github.io/quarkiverse-docs/shedlock/dev/ # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension categories: