mirror of
https://github.com/jlengrand/quarkus.git
synced 2026-03-10 08:41:22 +00:00
@@ -106,6 +106,11 @@
|
||||
<artifactId>quarkus-agroal-deployment</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-agroal-spi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-artemis-core</artifactId>
|
||||
|
||||
@@ -348,6 +348,7 @@ stages:
|
||||
modules:
|
||||
- kogito
|
||||
- kubernetes-client
|
||||
- quartz
|
||||
name: misc_3
|
||||
|
||||
- template: native-build-steps.yaml
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-agroal</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-agroal-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-narayana-jta-deployment</artifactId>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<name>Quarkus - Agroal</name>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>spi</module>
|
||||
<module>deployment</module>
|
||||
<module>runtime</module>
|
||||
</modules>
|
||||
|
||||
23
extensions/agroal/spi/pom.xml
Normal file
23
extensions/agroal/spi/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>quarkus-agroal-parent</artifactId>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>quarkus-agroal-spi</artifactId>
|
||||
<name>Quarkus - Agroal - SPI</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-core-deployment</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,58 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>quarkus-quartz-parent</artifactId>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>quarkus-quartz-parent</artifactId>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>quarkus-quartz-deployment</artifactId>
|
||||
<name>Quarkus - Scheduler Quartz - Deployment</name>
|
||||
<artifactId>quarkus-quartz-deployment</artifactId>
|
||||
<name>Quarkus - Quartz - Deployment</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-core-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-arc-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-scheduler-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-quartz</artifactId>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-core-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-arc-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-scheduler-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-agroal-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-quartz</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5-internal</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5-internal</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.quarkus.quartz.deployment;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.quarkus.builder.item.SimpleBuildItem;
|
||||
|
||||
/**
|
||||
* Holds the SQL driver dialect {@link org.quartz.impl.jdbcjobstore.StdJDBCDelegate driver delegate} to use.
|
||||
*/
|
||||
final class QuartzJDBCDriverDialectBuildItem extends SimpleBuildItem {
|
||||
private final Optional<String> driver;
|
||||
|
||||
public QuartzJDBCDriverDialectBuildItem(Optional<String> driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public Optional<String> getDriver() {
|
||||
return driver;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,27 @@ package io.quarkus.quartz.deployment;
|
||||
|
||||
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.quartz.core.QuartzSchedulerThread;
|
||||
import org.quartz.core.SchedulerSignalerImpl;
|
||||
import org.quartz.impl.StdSchedulerFactory;
|
||||
import org.quartz.impl.jdbcjobstore.AttributeRestoringConnectionInvocationHandler;
|
||||
import org.quartz.impl.jdbcjobstore.HSQLDBDelegate;
|
||||
import org.quartz.impl.jdbcjobstore.JobStoreSupport;
|
||||
import org.quartz.impl.jdbcjobstore.MSSQLDelegate;
|
||||
import org.quartz.impl.jdbcjobstore.PostgreSQLDelegate;
|
||||
import org.quartz.impl.jdbcjobstore.StdJDBCDelegate;
|
||||
import org.quartz.impl.triggers.AbstractTrigger;
|
||||
import org.quartz.impl.triggers.SimpleTriggerImpl;
|
||||
import org.quartz.simpl.CascadingClassLoadHelper;
|
||||
import org.quartz.simpl.RAMJobStore;
|
||||
import org.quartz.simpl.SimpleInstanceIdGenerator;
|
||||
import org.quartz.simpl.SimpleThreadPool;
|
||||
|
||||
import io.quarkus.agroal.deployment.DataSourceDriverBuildItem;
|
||||
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
|
||||
import io.quarkus.arc.deployment.BeanContainerBuildItem;
|
||||
import io.quarkus.deployment.Capabilities;
|
||||
@@ -17,18 +31,22 @@ import io.quarkus.deployment.annotations.BuildStep;
|
||||
import io.quarkus.deployment.annotations.Record;
|
||||
import io.quarkus.deployment.builditem.CapabilityBuildItem;
|
||||
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
|
||||
import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem;
|
||||
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
|
||||
import io.quarkus.deployment.configuration.ConfigurationError;
|
||||
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
|
||||
import io.quarkus.quartz.runtime.QuarkusQuartzConnectionPoolProvider;
|
||||
import io.quarkus.quartz.runtime.QuartzBuildTimeConfig;
|
||||
import io.quarkus.quartz.runtime.QuartzRecorder;
|
||||
import io.quarkus.quartz.runtime.QuartzRuntimeConfig;
|
||||
import io.quarkus.quartz.runtime.QuartzScheduler;
|
||||
import io.quarkus.quartz.runtime.QuartzSupport;
|
||||
import io.quarkus.quartz.runtime.StoreType;
|
||||
|
||||
/**
|
||||
* @author Martin Kouba
|
||||
*/
|
||||
public class QuartzProcessor {
|
||||
|
||||
@BuildStep
|
||||
CapabilityBuildItem capability() {
|
||||
return new CapabilityBuildItem(Capabilities.QUARTZ);
|
||||
@@ -40,41 +58,118 @@ public class QuartzProcessor {
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
List<ReflectiveClassBuildItem> reflectiveClasses() {
|
||||
NativeImageProxyDefinitionBuildItem connectionProxy(QuartzBuildTimeConfig config) {
|
||||
if (config.storeType.equals(StoreType.DB)) {
|
||||
return new NativeImageProxyDefinitionBuildItem(Connection.class.getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
QuartzJDBCDriverDialectBuildItem driver(Optional<DataSourceDriverBuildItem> dataSourceDriver,
|
||||
QuartzBuildTimeConfig config) {
|
||||
if (config.storeType == StoreType.RAM) {
|
||||
if (config.clustered) {
|
||||
throw new ConfigurationError("Clustered jobs configured with unsupported job store option");
|
||||
}
|
||||
|
||||
return new QuartzJDBCDriverDialectBuildItem(Optional.empty());
|
||||
}
|
||||
|
||||
if (!dataSourceDriver.isPresent()) {
|
||||
String message = String.format(
|
||||
"JDBC Store configured but '%s' datasource is not configured properly. You can configure your datasource by following the guide available at: https://quarkus.io/guides/datasource-guide",
|
||||
config.dataSourceName.isPresent() ? config.dataSourceName.get() : "default");
|
||||
throw new ConfigurationError(message);
|
||||
}
|
||||
|
||||
return new QuartzJDBCDriverDialectBuildItem(Optional.of(guessDriver(dataSourceDriver)));
|
||||
}
|
||||
|
||||
private String guessDriver(Optional<DataSourceDriverBuildItem> dataSourceDriver) {
|
||||
if (!dataSourceDriver.isPresent()) {
|
||||
return StdJDBCDelegate.class.getName();
|
||||
}
|
||||
|
||||
String resolvedDriver = dataSourceDriver.get().getDriver();
|
||||
if (resolvedDriver.contains("postgresql")) {
|
||||
return PostgreSQLDelegate.class.getName();
|
||||
}
|
||||
if (resolvedDriver.contains("org.h2.Driver")) {
|
||||
return HSQLDBDelegate.class.getName();
|
||||
}
|
||||
|
||||
if (resolvedDriver.contains("com.microsoft.sqlserver.jdbc.SQLServerResource")) {
|
||||
return MSSQLDelegate.class.getName();
|
||||
}
|
||||
|
||||
return StdJDBCDelegate.class.getName();
|
||||
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
List<ReflectiveClassBuildItem> reflectiveClasses(QuartzBuildTimeConfig config,
|
||||
QuartzJDBCDriverDialectBuildItem driverDialect) {
|
||||
List<ReflectiveClassBuildItem> reflectiveClasses = new ArrayList<>();
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(false, false, CascadingClassLoadHelper.class.getName()));
|
||||
StoreType storeType = config.storeType;
|
||||
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, SimpleThreadPool.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, RAMJobStore.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, SimpleInstanceIdGenerator.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(false, false, CascadingClassLoadHelper.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, true, storeType.clazz));
|
||||
|
||||
if (storeType.equals(StoreType.DB)) {
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, JobStoreSupport.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, true, Connection.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, AbstractTrigger.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, SimpleTriggerImpl.class.getName()));
|
||||
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, driverDialect.getDriver().get()));
|
||||
reflectiveClasses
|
||||
.add(new ReflectiveClassBuildItem(true, true, "io.quarkus.quartz.runtime.QuartzScheduler$InvokerJob"));
|
||||
reflectiveClasses
|
||||
.add(new ReflectiveClassBuildItem(true, false, QuarkusQuartzConnectionPoolProvider.class.getName()));
|
||||
}
|
||||
|
||||
return reflectiveClasses;
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
public void logCleanup(BuildProducer<LogCleanupFilterBuildItem> logCleanupFilter) {
|
||||
logCleanupFilter.produce(new LogCleanupFilterBuildItem("org.quartz.impl.StdSchedulerFactory",
|
||||
public List<LogCleanupFilterBuildItem> logCleanup(QuartzBuildTimeConfig config) {
|
||||
StoreType storeType = config.storeType;
|
||||
List<LogCleanupFilterBuildItem> logCleanUps = new ArrayList<>();
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(StdSchedulerFactory.class.getName(),
|
||||
"Quartz scheduler version:",
|
||||
"Using default implementation for",
|
||||
"Quartz scheduler 'QuarkusQuartzScheduler'"));
|
||||
|
||||
logCleanupFilter.produce(new LogCleanupFilterBuildItem("org.quartz.core.QuartzScheduler",
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(org.quartz.core.QuartzScheduler.class.getName(),
|
||||
"Quartz Scheduler v",
|
||||
"JobFactory set to:",
|
||||
"Scheduler meta-data:",
|
||||
"Scheduler QuarkusQuartzScheduler"));
|
||||
|
||||
logCleanupFilter.produce(new LogCleanupFilterBuildItem("org.quartz.simpl.RAMJobStore",
|
||||
"RAMJobStore initialized."));
|
||||
|
||||
logCleanupFilter.produce(new LogCleanupFilterBuildItem("org.quartz.core.SchedulerSignalerImpl",
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(storeType.clazz, storeType.name + " initialized.", "Handling",
|
||||
"Using db table-based data access locking", "JDBCJobStore threads will inherit ContextClassLoader of thread",
|
||||
"Couldn't rollback jdbc connection", "Database connection shutdown unsuccessful"));
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(SchedulerSignalerImpl.class.getName(),
|
||||
"Initialized Scheduler Signaller of type"));
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(QuartzSchedulerThread.class.getName(),
|
||||
"QuartzSchedulerThread Inheriting ContextClassLoader"));
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(SimpleThreadPool.class.getName(),
|
||||
"Job execution threads will use class loader of thread"));
|
||||
|
||||
logCleanUps.add(new LogCleanupFilterBuildItem(AttributeRestoringConnectionInvocationHandler.class.getName(),
|
||||
"Failed restore connection's original"));
|
||||
return logCleanUps;
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
@Record(RUNTIME_INIT)
|
||||
public void build(QuartzRuntimeConfig runtimeConfig, QuartzRecorder recorder, BeanContainerBuildItem beanContainer,
|
||||
BuildProducer<ServiceStartBuildItem> serviceStart) {
|
||||
recorder.initialize(runtimeConfig, beanContainer.getValue());
|
||||
public void build(QuartzRuntimeConfig runtimeConfig, QuartzBuildTimeConfig buildTimeConfig, QuartzRecorder recorder,
|
||||
BeanContainerBuildItem beanContainer,
|
||||
BuildProducer<ServiceStartBuildItem> serviceStart, QuartzJDBCDriverDialectBuildItem driverDialect) {
|
||||
recorder.initialize(runtimeConfig, buildTimeConfig, beanContainer.getValue(), driverDialect.getDriver());
|
||||
// Make sure that StartupEvent is fired after the init
|
||||
serviceStart.produce(new ServiceStartBuildItem("quartz"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.quarkus.quartz.test;
|
||||
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.asset.StringAsset;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import io.quarkus.deployment.configuration.ConfigurationError;
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
|
||||
public class MissingDataSourceTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setExpectedException(ConfigurationError.class)
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addClasses(SimpleJobs.class)
|
||||
.addAsResource(new StringAsset("quarkus.quartz.store-type=db"), "application.properties"));
|
||||
|
||||
@Test
|
||||
public void shouldFailAndNotReachHere() {
|
||||
Assertions.fail();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.quarkus.quartz.test;
|
||||
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.asset.StringAsset;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import io.quarkus.deployment.configuration.ConfigurationError;
|
||||
import io.quarkus.test.QuarkusUnitTest;
|
||||
|
||||
public class UnsupportedClusteredJobConfigurationTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final QuarkusUnitTest test = new QuarkusUnitTest()
|
||||
.setExpectedException(ConfigurationError.class)
|
||||
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
|
||||
.addClasses(SimpleJobs.class)
|
||||
.addAsResource(new StringAsset(
|
||||
"quarkus.quartz.store-type=ram\nquarkus.quartz.clustered=true"),
|
||||
"application.properties"));
|
||||
|
||||
@Test
|
||||
public void shouldFailWhenConfiguringClusteredJobWithRamStore() {
|
||||
Assertions.fail();
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>quarkus-quartz-parent</artifactId>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>quarkus-quartz</artifactId>
|
||||
<name>Quarkus - Scheduler Quartz - Runtime</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-scheduler</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle.substratevm</groupId>
|
||||
<artifactId>svm</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP-java6</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.mchange</groupId>
|
||||
<artifactId>c3p0</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.transaction</groupId>
|
||||
<artifactId>jakarta.transaction-api</artifactId>
|
||||
<!--
|
||||
TODO: Make this an optional dependency?
|
||||
Graal compiler not happy with broken classpaths
|
||||
<optional>true</optional>
|
||||
-->
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>quarkus-quartz-parent</artifactId>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-bootstrap-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>quarkus-quartz</artifactId>
|
||||
<name>Quarkus - Quartz - Runtime</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-agroal</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-scheduler</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle.substratevm</groupId>
|
||||
<artifactId>svm</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP-java6</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.mchange</groupId>
|
||||
<artifactId>c3p0</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.transaction</groupId>
|
||||
<artifactId>jakarta.transaction-api</artifactId>
|
||||
<!--
|
||||
TODO: Make this an optional dependency?
|
||||
Graal compiler not happy with broken classpaths
|
||||
<optional>true</optional>
|
||||
-->
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-bootstrap-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package io.quarkus.quartz.runtime;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.enterprise.util.AnnotationLiteral;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.quartz.utils.PoolingConnectionProvider;
|
||||
|
||||
import io.agroal.api.AgroalDataSource;
|
||||
import io.quarkus.arc.Arc;
|
||||
import io.quarkus.arc.ArcContainer;
|
||||
import io.quarkus.arc.InstanceHandle;
|
||||
|
||||
public class QuarkusQuartzConnectionPoolProvider implements PoolingConnectionProvider {
|
||||
private AgroalDataSource dataSource;
|
||||
private static String dataSourceName;
|
||||
|
||||
public QuarkusQuartzConnectionPoolProvider() {
|
||||
final ArcContainer container = Arc.container();
|
||||
final InstanceHandle<AgroalDataSource> instanceHandle;
|
||||
final boolean useDefaultDataSource = "QUARKUS_QUARTZ_DEFAULT_DATASOURCE".equals(dataSourceName);
|
||||
if (useDefaultDataSource) {
|
||||
instanceHandle = container.instance(AgroalDataSource.class);
|
||||
} else {
|
||||
instanceHandle = container.instance(AgroalDataSource.class, new DataSourceLiteral(dataSourceName));
|
||||
}
|
||||
if (instanceHandle.isAvailable()) {
|
||||
this.dataSource = instanceHandle.get();
|
||||
} else {
|
||||
String message = String.format(
|
||||
"JDBC Store configured but '%s' datasource is missing. You can configure your datasource by following the guide available at: https://quarkus.io/guides/datasource",
|
||||
useDefaultDataSource ? "default" : dataSourceName);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// Do nothing as the connection will be closed inside the Agroal extension
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
|
||||
}
|
||||
|
||||
static void setDataSourceName(String dataSourceName) {
|
||||
QuarkusQuartzConnectionPoolProvider.dataSourceName = dataSourceName;
|
||||
}
|
||||
|
||||
private static class DataSourceLiteral extends AnnotationLiteral<io.quarkus.agroal.DataSource>
|
||||
implements io.quarkus.agroal.DataSource {
|
||||
|
||||
private String name;
|
||||
|
||||
public DataSourceLiteral(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.quarkus.quartz.runtime;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.quarkus.runtime.annotations.ConfigItem;
|
||||
import io.quarkus.runtime.annotations.ConfigPhase;
|
||||
import io.quarkus.runtime.annotations.ConfigRoot;
|
||||
|
||||
@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
|
||||
public class QuartzBuildTimeConfig {
|
||||
/**
|
||||
* Enable cluster mode or not.
|
||||
* <p>
|
||||
* If enabled make sure to set the appropriate cluster properties.
|
||||
*/
|
||||
@ConfigItem
|
||||
public boolean clustered;
|
||||
|
||||
/**
|
||||
* The type of store to use.
|
||||
* <p>
|
||||
* When using the `db` store type configuration value make sure that you have the datasource configured.
|
||||
* See <a href="https://quarkus.io/guides/datasource"> Configuring your datasource</a> for more information.
|
||||
* <p>
|
||||
* To create Quartz tables, you can perform a schema migration via the <a href="https://quarkus.io/guides/flyway"> Flyway
|
||||
* extension</a> using a SQL script matching your database picked from <a href=
|
||||
* "https://github.com/quartz-scheduler/quartz/blob/master/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore">Quartz
|
||||
* repository</a>.
|
||||
*/
|
||||
@ConfigItem(defaultValue = "ram")
|
||||
public StoreType storeType;
|
||||
|
||||
/**
|
||||
* The name of the datasource to use.
|
||||
* <p>
|
||||
* Optionally needed when using the `db` store type.
|
||||
* If not specified, defaults to using the default datasource.
|
||||
*/
|
||||
@ConfigItem(name = "datasource")
|
||||
public Optional<String> dataSourceName;
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package io.quarkus.quartz.runtime;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.quarkus.arc.runtime.BeanContainer;
|
||||
import io.quarkus.runtime.annotations.Recorder;
|
||||
|
||||
@Recorder
|
||||
public class QuartzRecorder {
|
||||
|
||||
public void initialize(QuartzRuntimeConfig runtimeConfig, BeanContainer container) {
|
||||
public void initialize(QuartzRuntimeConfig runTimeConfig, QuartzBuildTimeConfig buildTimeConfig, BeanContainer container,
|
||||
Optional<String> driverDialect) {
|
||||
QuartzSupport support = container.instance(QuartzSupport.class);
|
||||
support.initialize(runtimeConfig);
|
||||
support.initialize(runTimeConfig, buildTimeConfig, driverDialect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.context.BeforeDestroyed;
|
||||
import javax.enterprise.event.Observes;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -17,8 +19,8 @@ import org.jboss.logging.Logger;
|
||||
import org.quartz.CronScheduleBuilder;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobBuilder;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.ScheduleBuilder;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SchedulerFactory;
|
||||
@@ -55,7 +57,6 @@ public class QuartzScheduler implements Scheduler {
|
||||
private final Map<String, ScheduledInvoker> invokers;
|
||||
|
||||
public QuartzScheduler(SchedulerSupport schedulerSupport, QuartzSupport quartzSupport, Config config) {
|
||||
|
||||
if (schedulerSupport.getScheduledMethods().isEmpty()) {
|
||||
this.triggerNameSequence = null;
|
||||
this.scheduler = null;
|
||||
@@ -66,21 +67,7 @@ public class QuartzScheduler implements Scheduler {
|
||||
this.invokers = new HashMap<>();
|
||||
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, "QuarkusQuartzScheduler");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, "QuarkusQuartzScheduler");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_WRAP_JOB_IN_USER_TX, false);
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD,
|
||||
true);
|
||||
props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool");
|
||||
props.put(StdSchedulerFactory.PROP_THREAD_POOL_PREFIX + ".threadCount",
|
||||
"" + quartzSupport.getRuntimeConfig().threadCount);
|
||||
props.put(StdSchedulerFactory.PROP_THREAD_POOL_PREFIX + ".threadPriority",
|
||||
"" + quartzSupport.getRuntimeConfig().threadPriority);
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".misfireThreshold", "60000");
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, "org.quartz.simpl.RAMJobStore");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_RMI_EXPORT, false);
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_RMI_PROXY, false);
|
||||
Properties props = getSchedulerConfigurationProperties(quartzSupport);
|
||||
|
||||
SchedulerFactory schedulerFactory = new StdSchedulerFactory(props);
|
||||
scheduler = schedulerFactory.getScheduler();
|
||||
@@ -109,8 +96,9 @@ public class QuartzScheduler implements Scheduler {
|
||||
for (Scheduled scheduled : method.getSchedules()) {
|
||||
String name = triggerNameSequence.getAndIncrement() + "_" + method.getInvokerClassName();
|
||||
JobBuilder jobBuilder = JobBuilder.newJob(InvokerJob.class)
|
||||
.withIdentity(name, Scheduler.class.getName()).usingJobData(INVOKER_KEY,
|
||||
method.getInvokerClassName());
|
||||
.withIdentity(name, Scheduler.class.getName())
|
||||
.usingJobData(INVOKER_KEY, method.getInvokerClassName())
|
||||
.requestRecovery();
|
||||
ScheduleBuilder<?> scheduleBuilder;
|
||||
|
||||
String cron = scheduled.cron().trim();
|
||||
@@ -161,8 +149,13 @@ public class QuartzScheduler implements Scheduler {
|
||||
.plusMillis(scheduled.delayUnit().toMillis(scheduled.delay())).toEpochMilli()));
|
||||
}
|
||||
|
||||
scheduler.scheduleJob(jobBuilder.build(), triggerBuilder.build());
|
||||
LOGGER.debugf("Scheduled business method %s with config %s", method.getMethodDescription(), scheduled);
|
||||
JobDetail job = jobBuilder.build();
|
||||
if (scheduler.checkExists(job.getKey())) {
|
||||
scheduler.deleteJob(job.getKey());
|
||||
}
|
||||
scheduler.scheduleJob(job, triggerBuilder.build());
|
||||
LOGGER.debugf("Scheduled business method %s with config %s", method.getMethodDescription(),
|
||||
scheduled);
|
||||
}
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
@@ -204,21 +197,76 @@ public class QuartzScheduler implements Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Need to gracefully shutdown the scheduler making sure that all triggers have been
|
||||
* released before datasource shutdown.
|
||||
*
|
||||
* @param event ignored
|
||||
*/
|
||||
void destroy(@BeforeDestroyed(ApplicationScoped.class) Object event) { //
|
||||
if (scheduler != null) {
|
||||
try {
|
||||
scheduler.shutdown(true); // gracefully shutdown
|
||||
} catch (SchedulerException e) {
|
||||
LOGGER.warnf("Unable to gracefully shutdown the scheduler", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void destroy() {
|
||||
if (scheduler != null) {
|
||||
try {
|
||||
scheduler.shutdown();
|
||||
if (!scheduler.isShutdown()) {
|
||||
scheduler.shutdown(false); // force shutdown
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
LOGGER.warnf("Unable to shutdown scheduler", e);
|
||||
LOGGER.warnf("Unable to shutdown the scheduler", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Properties getSchedulerConfigurationProperties(QuartzSupport quartzSupport) {
|
||||
Properties props = new Properties();
|
||||
QuartzBuildTimeConfig buildTimeConfig = quartzSupport.getBuildTimeConfig();
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, "AUTO");
|
||||
props.put("org.quartz.scheduler.skipUpdateCheck", "true");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, "QuarkusQuartzScheduler");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_WRAP_JOB_IN_USER_TX, "false");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD, "true");
|
||||
props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool");
|
||||
props.put(StdSchedulerFactory.PROP_THREAD_POOL_PREFIX + ".threadCount",
|
||||
"" + quartzSupport.getRuntimeConfig().threadCount);
|
||||
props.put(StdSchedulerFactory.PROP_THREAD_POOL_PREFIX + ".threadPriority",
|
||||
"" + quartzSupport.getRuntimeConfig().threadPriority);
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_RMI_EXPORT, "false");
|
||||
props.put(StdSchedulerFactory.PROP_SCHED_RMI_PROXY, "false");
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, buildTimeConfig.storeType.clazz);
|
||||
|
||||
if (buildTimeConfig.storeType == StoreType.DB) {
|
||||
String dataSource = buildTimeConfig.dataSourceName.orElse("QUARKUS_QUARTZ_DEFAULT_DATASOURCE");
|
||||
QuarkusQuartzConnectionPoolProvider.setDataSourceName(dataSource);
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".useProperties", "true");
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".misfireThreshold", "60000");
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".tablePrefix", "QRTZ_");
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".dataSource", dataSource);
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".driverDelegateClass",
|
||||
quartzSupport.getDriverDialect().get());
|
||||
props.put(StdSchedulerFactory.PROP_DATASOURCE_PREFIX + "." + dataSource + ".connectionProvider.class",
|
||||
QuarkusQuartzConnectionPoolProvider.class.getName());
|
||||
if (buildTimeConfig.clustered) {
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".isClustered", "true");
|
||||
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".clusterCheckinInterval", "20000"); // 20 seconds
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
class InvokerJob implements Job {
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
public void execute(JobExecutionContext context) {
|
||||
Trigger trigger = new Trigger() {
|
||||
|
||||
@Override
|
||||
@@ -239,23 +287,25 @@ public class QuartzScheduler implements Scheduler {
|
||||
}
|
||||
};
|
||||
String invokerClass = context.getJobDetail().getJobDataMap().getString(INVOKER_KEY);
|
||||
invokers.get(invokerClass).invoke(new ScheduledExecution() {
|
||||
ScheduledInvoker scheduledInvoker = invokers.get(invokerClass);
|
||||
if (scheduledInvoker != null) { // could be null from previous runs
|
||||
scheduledInvoker.invoke(new ScheduledExecution() {
|
||||
@Override
|
||||
public Trigger getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
@Override
|
||||
public Instant getScheduledFireTime() {
|
||||
return context.getScheduledFireTime().toInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getScheduledFireTime() {
|
||||
return context.getScheduledFireTime().toInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getFireTime() {
|
||||
return context.getFireTime().toInstant();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public Instant getFireTime() {
|
||||
return context.getFireTime().toInstant();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
package io.quarkus.quartz.runtime;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class QuartzSupport {
|
||||
|
||||
private QuartzRuntimeConfig runtimeConfig;
|
||||
private QuartzBuildTimeConfig buildTimeConfig;
|
||||
private Optional<String> driverDialect;
|
||||
|
||||
void initialize(QuartzRuntimeConfig runtimeConfig) {
|
||||
this.runtimeConfig = runtimeConfig;
|
||||
void initialize(QuartzRuntimeConfig runTimeConfig, QuartzBuildTimeConfig buildTimeConfig, Optional<String> driverDialect) {
|
||||
this.runtimeConfig = runTimeConfig;
|
||||
this.buildTimeConfig = buildTimeConfig;
|
||||
this.driverDialect = driverDialect;
|
||||
}
|
||||
|
||||
public QuartzRuntimeConfig getRuntimeConfig() {
|
||||
return runtimeConfig;
|
||||
}
|
||||
|
||||
public QuartzBuildTimeConfig getBuildTimeConfig() {
|
||||
return buildTimeConfig;
|
||||
}
|
||||
|
||||
public Optional<String> getDriverDialect() {
|
||||
return driverDialect;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.quarkus.quartz.runtime;
|
||||
|
||||
import org.quartz.impl.jdbcjobstore.JobStoreTX;
|
||||
import org.quartz.simpl.RAMJobStore;
|
||||
|
||||
public enum StoreType {
|
||||
RAM(RAMJobStore.class.getName(), RAMJobStore.class.getSimpleName()),
|
||||
DB(JobStoreTX.class.getName(), JobStoreTX.class.getSimpleName());
|
||||
|
||||
public String name;
|
||||
public String clazz;
|
||||
|
||||
StoreType(String clazz, String name) {
|
||||
this.clazz = clazz;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package io.quarkus.quartz.runtime.graal;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.rmi.RemoteException;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
import org.quartz.core.RemotableQuartzScheduler;
|
||||
import org.quartz.impl.jdbcjobstore.StdJDBCDelegate;
|
||||
|
||||
import com.oracle.svm.core.annotate.Substitute;
|
||||
import com.oracle.svm.core.annotate.TargetClass;
|
||||
@@ -37,5 +40,30 @@ final class Target_org_quartz_impl_RemoteScheduler {
|
||||
|
||||
}
|
||||
|
||||
@TargetClass(StdJDBCDelegate.class)
|
||||
final class Target_org_quartz_impl_jdbc_jobstore_StdJDBCDelegate {
|
||||
|
||||
/**
|
||||
* Activate the usage of {@link java.util.Properties} to avoid Object serialization
|
||||
* which is not supported by GraalVM - see https://github.com/oracle/graal/issues/460
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
@Substitute
|
||||
protected boolean canUseProperties() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Substitute
|
||||
protected ByteArrayOutputStream serializeObject(Object obj) {
|
||||
throw new IllegalStateException("Object serialization not supported."); // should not reach here
|
||||
}
|
||||
|
||||
@Substitute
|
||||
protected Object getObjectFromBlob(ResultSet rs, String colName) {
|
||||
throw new IllegalStateException("Object serialization not supported."); // should not reach here
|
||||
}
|
||||
}
|
||||
|
||||
final class QuartzSubstitutions {
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<module>elytron-security-jdbc</module>
|
||||
<module>vertx-graphql</module>
|
||||
<module>jpa-without-entity</module>
|
||||
<module>quartz</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
131
integration-tests/quartz/pom.xml
Normal file
131
integration-tests/quartz/pom.xml
Normal file
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>quarkus-integration-tests-parent</artifactId>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>quarkus-integration-test-quartz</artifactId>
|
||||
<name>Quarkus - Integration Tests - Quartz</name>
|
||||
<description>The Quartz integration test module</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-arc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-resteasy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-quartz</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-agroal</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-flyway</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-jdbc-h2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-test-h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-maven-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>native-image</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>native</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-maven-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>native-image</id>
|
||||
<goals>
|
||||
<goal>native-image</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<cleanupServer>true</cleanupServer>
|
||||
<enableHttpUrlHandler>true</enableHttpUrlHandler>
|
||||
<graalvmHome>${graalvmHome}</graalvmHome>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.quarkus.it.quartz;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/scheduler/count")
|
||||
public class CountResource {
|
||||
|
||||
@Inject
|
||||
Counter counter;
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public Integer getCount() {
|
||||
return counter.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.quarkus.it.quartz;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
|
||||
@ApplicationScoped
|
||||
public class Counter {
|
||||
|
||||
AtomicInteger counter;
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
counter = new AtomicInteger();
|
||||
}
|
||||
|
||||
public int get() {
|
||||
return counter.get();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0/1 * * * * ?")
|
||||
void increment() {
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# datasource configuration
|
||||
quarkus.datasource.url=jdbc:h2:tcp://localhost/mem:test
|
||||
quarkus.datasource.driver=org.h2.Driver
|
||||
quarkus.datasource.max-size=8
|
||||
quarkus.datasource.min-size=2
|
||||
|
||||
# Quartz configuration
|
||||
quarkus.quartz.store-type=db
|
||||
quarkus.quartz.clustered=true
|
||||
|
||||
# flyway to create Quartz tables
|
||||
quarkus.flyway.connect-retries=10
|
||||
quarkus.flyway.table=flyway_quarkus_history
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
quarkus.flyway.baseline-on-migrate=true
|
||||
quarkus.flyway.baseline-version=1.0
|
||||
quarkus.flyway.baseline-description=Quartz
|
||||
@@ -0,0 +1,238 @@
|
||||
CREATE TABLE QRTZ_CALENDARS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
CALENDAR_NAME VARCHAR (200) NOT NULL ,
|
||||
CALENDAR IMAGE NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_CRON_TRIGGERS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_NAME VARCHAR (200) NOT NULL ,
|
||||
TRIGGER_GROUP VARCHAR (200) NOT NULL ,
|
||||
CRON_EXPRESSION VARCHAR (120) NOT NULL ,
|
||||
TIME_ZONE_ID VARCHAR (80)
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_FIRED_TRIGGERS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
ENTRY_ID VARCHAR (95) NOT NULL ,
|
||||
TRIGGER_NAME VARCHAR (200) NOT NULL ,
|
||||
TRIGGER_GROUP VARCHAR (200) NOT NULL ,
|
||||
INSTANCE_NAME VARCHAR (200) NOT NULL ,
|
||||
FIRED_TIME BIGINT NOT NULL ,
|
||||
SCHED_TIME BIGINT NOT NULL ,
|
||||
PRIORITY INTEGER NOT NULL ,
|
||||
STATE VARCHAR (16) NOT NULL,
|
||||
JOB_NAME VARCHAR (200) NULL ,
|
||||
JOB_GROUP VARCHAR (200) NULL ,
|
||||
IS_NONCONCURRENT BOOLEAN NULL ,
|
||||
REQUESTS_RECOVERY BOOLEAN NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR (200) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_SCHEDULER_STATE (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
INSTANCE_NAME VARCHAR (200) NOT NULL ,
|
||||
LAST_CHECKIN_TIME BIGINT NOT NULL ,
|
||||
CHECKIN_INTERVAL BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_LOCKS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
LOCK_NAME VARCHAR (40) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_JOB_DETAILS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
JOB_NAME VARCHAR (200) NOT NULL ,
|
||||
JOB_GROUP VARCHAR (200) NOT NULL ,
|
||||
DESCRIPTION VARCHAR (250) NULL ,
|
||||
JOB_CLASS_NAME VARCHAR (250) NOT NULL ,
|
||||
IS_DURABLE BOOLEAN NOT NULL ,
|
||||
IS_NONCONCURRENT BOOLEAN NOT NULL ,
|
||||
IS_UPDATE_DATA BOOLEAN NOT NULL ,
|
||||
REQUESTS_RECOVERY BOOLEAN NOT NULL ,
|
||||
JOB_DATA IMAGE NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_NAME VARCHAR (200) NOT NULL ,
|
||||
TRIGGER_GROUP VARCHAR (200) NOT NULL ,
|
||||
REPEAT_COUNT BIGINT NOT NULL ,
|
||||
REPEAT_INTERVAL BIGINT NOT NULL ,
|
||||
TIMES_TRIGGERED BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_SIMPROP_TRIGGERS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
STR_PROP_1 VARCHAR(512) NULL,
|
||||
STR_PROP_2 VARCHAR(512) NULL,
|
||||
STR_PROP_3 VARCHAR(512) NULL,
|
||||
INT_PROP_1 INTEGER NULL,
|
||||
INT_PROP_2 INTEGER NULL,
|
||||
LONG_PROP_1 BIGINT NULL,
|
||||
LONG_PROP_2 BIGINT NULL,
|
||||
DEC_PROP_1 NUMERIC(13,4) NULL,
|
||||
DEC_PROP_2 NUMERIC(13,4) NULL,
|
||||
BOOL_PROP_1 BOOLEAN NULL,
|
||||
BOOL_PROP_2 BOOLEAN NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_BLOB_TRIGGERS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_NAME VARCHAR (200) NOT NULL ,
|
||||
TRIGGER_GROUP VARCHAR (200) NOT NULL ,
|
||||
BLOB_DATA IMAGE NULL
|
||||
);
|
||||
|
||||
CREATE TABLE QRTZ_TRIGGERS (
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_NAME VARCHAR (200) NOT NULL ,
|
||||
TRIGGER_GROUP VARCHAR (200) NOT NULL ,
|
||||
JOB_NAME VARCHAR (200) NOT NULL ,
|
||||
JOB_GROUP VARCHAR (200) NOT NULL ,
|
||||
DESCRIPTION VARCHAR (250) NULL ,
|
||||
NEXT_FIRE_TIME BIGINT NULL ,
|
||||
PREV_FIRE_TIME BIGINT NULL ,
|
||||
PRIORITY INTEGER NULL ,
|
||||
TRIGGER_STATE VARCHAR (16) NOT NULL ,
|
||||
TRIGGER_TYPE VARCHAR (8) NOT NULL ,
|
||||
START_TIME BIGINT NOT NULL ,
|
||||
END_TIME BIGINT NULL ,
|
||||
CALENDAR_NAME VARCHAR (200) NULL ,
|
||||
MISFIRE_INSTR SMALLINT NULL ,
|
||||
JOB_DATA IMAGE NULL
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_CALENDARS ADD
|
||||
CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
CALENDAR_NAME
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_CRON_TRIGGERS ADD
|
||||
CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_FIRED_TRIGGERS ADD
|
||||
CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
ENTRY_ID
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD
|
||||
CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_GROUP
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_SCHEDULER_STATE ADD
|
||||
CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
INSTANCE_NAME
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_LOCKS ADD
|
||||
CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
LOCK_NAME
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_JOB_DETAILS ADD
|
||||
CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
JOB_NAME,
|
||||
JOB_GROUP
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD
|
||||
CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD
|
||||
CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_TRIGGERS ADD
|
||||
CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
);
|
||||
|
||||
ALTER TABLE QRTZ_CRON_TRIGGERS ADD
|
||||
CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
) REFERENCES QRTZ_TRIGGERS (
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
) ON DELETE CASCADE;
|
||||
|
||||
|
||||
ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD
|
||||
CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
) REFERENCES QRTZ_TRIGGERS (
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD
|
||||
CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
) REFERENCES QRTZ_TRIGGERS (
|
||||
SCHED_NAME,
|
||||
TRIGGER_NAME,
|
||||
TRIGGER_GROUP
|
||||
) ON DELETE CASCADE;
|
||||
|
||||
|
||||
ALTER TABLE QRTZ_TRIGGERS ADD
|
||||
CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS FOREIGN KEY
|
||||
(
|
||||
SCHED_NAME,
|
||||
JOB_NAME,
|
||||
JOB_GROUP
|
||||
) REFERENCES QRTZ_JOB_DETAILS (
|
||||
SCHED_NAME,
|
||||
JOB_NAME,
|
||||
JOB_GROUP
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.quarkus.it.quartz;
|
||||
|
||||
import io.quarkus.test.junit.NativeImageTest;
|
||||
|
||||
@NativeImageTest
|
||||
public class QuartzITCase extends QuartzTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.quarkus.it.quartz;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.response.Response;
|
||||
|
||||
@QuarkusTest
|
||||
public class QuartzTestCase {
|
||||
|
||||
@Test
|
||||
public void testCount() throws InterruptedException {
|
||||
// Wait at least 1 second
|
||||
Thread.sleep(1000);
|
||||
Response response = given()
|
||||
.when().get("/scheduler/count");
|
||||
String body = response.asString();
|
||||
int count = Integer.valueOf(body);
|
||||
assertTrue(count > 0);
|
||||
response
|
||||
.then()
|
||||
.statusCode(200);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.quarkus.it.quartz;
|
||||
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.h2.H2DatabaseTestResource;
|
||||
|
||||
@QuarkusTestResource(H2DatabaseTestResource.class)
|
||||
public class TestResources {
|
||||
}
|
||||
Reference in New Issue
Block a user