Skip to content

Commit

Permalink
Merge pull request #10967 from marcelorubim/master
Browse files Browse the repository at this point in the history
Added support to Quartz Plugins And Listeners
  • Loading branch information
gsmet authored Jul 29, 2020
2 parents 1551b68 + d647c1a commit 2a5b0a0
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 0 deletions.
25 changes: 25 additions & 0 deletions docs/src/main/asciidoc/quartz.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,31 @@ After a few seconds, in another terminal, run `curl localhost:8080/tasks` to ver

You can also generate the native executable with `./mvnw clean package -Pnative`.

[[quartz-register-plugin-listeners]]
== Registering Plugin and Listeners

You can register a plugin, jobListener or triggerListener through Quarkus configuration.

The example bellow registers the plugin `org.quartz.plugins.history.LoggingJobHistoryPlugin` named as `jobHistory` with the property `jobSuccessMessage` defined as `Job [{1}.{0}] execution complete and reports: {8}`

[source,conf]
----
quarkus.quartz.plugin.jobHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
quarkus.quartz.plugin.jobHistory.jobSuccessMessage=Job [{1}.{0}] execution complete and reports: {8}
----

You can also register a listener programmatically with an injected `org.quartz.Scheduler`

[source,java]
---
public class MyListenerManager {
void onStart(@Observes StartupEvent event, org.quartz.Scheduler scheduler) throws SchedulerException {
scheduler.getListenerManager().addJobListener(new MyJogListener());
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener());
}
}
---

[[quartz-configuration-reference]]
== Quartz Configuration Reference

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.inject.Singleton;

import org.quartz.JobListener;
import org.quartz.TriggerListener;
import org.quartz.core.QuartzSchedulerThread;
import org.quartz.core.SchedulerSignalerImpl;
import org.quartz.impl.StdSchedulerFactory;
Expand All @@ -23,6 +26,7 @@
import org.quartz.simpl.CascadingClassLoadHelper;
import org.quartz.simpl.SimpleInstanceIdGenerator;
import org.quartz.simpl.SimpleThreadPool;
import org.quartz.spi.SchedulerPlugin;

import io.quarkus.agroal.deployment.JdbcDataSourceBuildItem;
import io.quarkus.agroal.deployment.JdbcDataSourceSchemaReadyBuildItem;
Expand All @@ -40,6 +44,7 @@
import io.quarkus.deployment.configuration.ConfigurationError;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.quartz.runtime.QuarkusQuartzConnectionPoolProvider;
import io.quarkus.quartz.runtime.QuartzAdditionalPropsConfig;
import io.quarkus.quartz.runtime.QuartzBuildTimeConfig;
import io.quarkus.quartz.runtime.QuartzRecorder;
import io.quarkus.quartz.runtime.QuartzRuntimeConfig;
Expand Down Expand Up @@ -136,6 +141,26 @@ List<ReflectiveClassBuildItem> reflectiveClasses(QuartzBuildTimeConfig config,
.add(new ReflectiveClassBuildItem(true, false, QuarkusQuartzConnectionPoolProvider.class.getName()));
}

reflectiveClasses.addAll(getAdditionalConfigurationReflectiveClasses(config.triggerListeners, TriggerListener.class));
reflectiveClasses.addAll(getAdditionalConfigurationReflectiveClasses(config.jobListeners, JobListener.class));
reflectiveClasses.addAll(getAdditionalConfigurationReflectiveClasses(config.plugins, SchedulerPlugin.class));

return reflectiveClasses;
}

private List<ReflectiveClassBuildItem> getAdditionalConfigurationReflectiveClasses(
Map<String, QuartzAdditionalPropsConfig> config, Class<?> clazz) {
List<ReflectiveClassBuildItem> reflectiveClasses = new ArrayList<>();
for (QuartzAdditionalPropsConfig props : config.values()) {
try {
if (!clazz.isAssignableFrom(Class.forName(props.clazz))) {
throw new IllegalArgumentException(String.format("%s does not implements %s", props.clazz, clazz));
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
reflectiveClasses.add(new ReflectiveClassBuildItem(true, false, props.clazz));
}
return reflectiveClasses;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.test.QuarkusUnitTest;

public class InvalidTriggerListenerConfigurationTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setExpectedException(IllegalArgumentException.class)
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(SimpleJobs.class)
.addAsResource(new StringAsset(
"quarkus.quartz.triggerListener.jobHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin\n"
+ "quarkus.quartz.triggerListener.jobHistory.jobSuccessMessage=Job [{1}.{0}] execution complete and reports: {8}"),
"application.properties"));

@Test
public void shouldFailWhenInvalidTriggerListenerConfiguration() {
Assertions.fail();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.quartz.test;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import javax.inject.Inject;

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.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.quartz.SchedulerException;

import io.quarkus.quartz.test.listeners.HelloJobListener;
import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class RegisterJobListenerTest {

@Inject
org.quartz.Scheduler quartzScheduler;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Jobs.class)
.addClass(HelloJobListener.class)
.addAsResource(new StringAsset(
"quarkus.quartz.jobListener.testJobListener.class=io.quarkus.quartz.test.listeners.HelloJobListener"),
"application.properties"));

@Test
public void testJobListenerRegistered() throws InterruptedException, SchedulerException {
assertNotNull(quartzScheduler.getListenerManager().getJobListener("testJobListener"));
}

static class Jobs {
@Scheduled(every = "1s")
void checkEverySecond() {
// no op
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.quarkus.quartz.test;

import static org.junit.jupiter.api.Assertions.assertTrue;

import javax.inject.Inject;

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.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.scheduler.Scheduler;
import io.quarkus.test.QuarkusUnitTest;

public class RegisterLoggingJobHistoryPluginTest {

@Inject
Scheduler quartzScheduler;

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Jobs.class)
.addAsResource(new StringAsset(
"quarkus.quartz.plugin.jobHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin\n"
+ "quarkus.quartz.plugin.jobHistory.jobSuccessMessage=Job [{1}.{0}] execution complete and reports: {8}"),
"application.properties"));

@Test
public void testSchedulerStarted() throws InterruptedException {
assertTrue(quartzScheduler.isRunning());
}

static class Jobs {
@Scheduled(every = "1s")
void checkEverySecond() {
// no op
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.quarkus.quartz.test.listeners;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;

public class HelloJobListener implements JobListener {

@Override
public String getName() {
return "testJobListener";
}

@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {

}

@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {

}

@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.quartz.runtime;

import java.util.Map;

import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigDocSection;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

@ConfigGroup
public class QuartzAdditionalPropsConfig {
/**
* Class name for the configuration.
*/
@ConfigItem(name = "class")
public String clazz;

/**
* The props name and the values for the props.
*/
@ConfigDocSection
@ConfigDocMapKey("propsAndValue")
@ConfigItem(name = ConfigItem.PARENT)
public Map<String, String> propsValue;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.quarkus.quartz.runtime;

import java.util.Map;
import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
Expand Down Expand Up @@ -38,4 +40,23 @@ public class QuartzBuildTimeConfig {
*/
@ConfigItem(name = "datasource")
public Optional<String> dataSourceName;

/**
* The named trigged listeners list
*/
@ConfigItem(name = "triggerListener")
@ConfigDocMapKey("namedTriggerListener")
public Map<String, QuartzAdditionalPropsConfig> triggerListeners;
/**
* The named job listeners list
*/
@ConfigItem(name = "jobListener")
@ConfigDocMapKey("namedJobListener")
public Map<String, QuartzAdditionalPropsConfig> jobListeners;
/**
* The named plugins list
*/
@ConfigItem(name = "plugin")
@ConfigDocMapKey("namedPlugin")
public Map<String, QuartzAdditionalPropsConfig> plugins;
}
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,27 @@ private Properties getSchedulerConfigurationProperties(QuartzSupport quartzSuppo
props.put(StdSchedulerFactory.PROP_JOB_STORE_PREFIX + ".nonManagedTXDataSource", dataSource);
}
}
props.putAll(getAdditionalConfigurationProperties(StdSchedulerFactory.PROP_PLUGIN_PREFIX, buildTimeConfig.plugins));
props.putAll(getAdditionalConfigurationProperties(StdSchedulerFactory.PROP_JOB_LISTENER_PREFIX,
buildTimeConfig.jobListeners));
props.putAll(getAdditionalConfigurationProperties(StdSchedulerFactory.PROP_TRIGGER_LISTENER_PREFIX,
buildTimeConfig.triggerListeners));

return props;
}

private Properties getAdditionalConfigurationProperties(String prefix, Map<String, QuartzAdditionalPropsConfig> config) {
Properties props = new Properties();
for (String key : config.keySet()) {
props.put(String.format("%s.%s.class", prefix, key), config.get(key).clazz);
for (String propsName : config.get(key).propsValue.keySet()) {
props.put(String.format("%s.%s.%s", prefix, key, propsName),
config.get(key).propsValue.get(propsName));
}
}
return props;
}

static class InvokerJob implements Job {

final Map<String, ScheduledInvoker> invokers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ quarkus.flyway.migrate-at-start=true
quarkus.flyway.baseline-on-migrate=true
quarkus.flyway.baseline-version=1.0
quarkus.flyway.baseline-description=Quartz

# Register de LoggingJobHistoryPlugin for testing
quarkus.quartz.plugin.jobHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
quarkus.quartz.plugin.jobHistory.jobSuccessMessage=Job [{1}.{0}] execution complete and reports: {8}

0 comments on commit 2a5b0a0

Please sign in to comment.