Reputation: 353
I use ScheduledThreadPoolExecutor
for tasks which have different execution times. For example, one task scheduled for 9:30 PM tonight and the other for 8:00 AM tomorrow. But if the server is shut down for some reason, the tasks disappear.
I am thinking about task persistence in a database so that I can re-trigger the failed tasks at a later time—but I guess that's not a perfect solution.
Are there any good frameworks or any other suggestions for this?
Upvotes: 3
Views: 2649
Reputation: 1
If you wants to customize your own version instead of using Quartz or elastic-job, all you need is a place to persist the list of jobs and an execution lock for workers to acquire. Job contains job detail and cron expression. You can use Redis, Zookeeper or ETCD for both the distributed lock and the job list persistence.
Here is an example, https://medium.com/@ywang412/write-your-own-distributed-job-scheduling-framework-using-etcd-and-spring-boot-7ed51442eada
Upvotes: 0
Reputation: 10763
You can use Redis based task scheduling framework like Redisson. It also supports quartz cron experssions. Here is documentation about it
Upvotes: 3
Reputation: 666
You can use quartz scheduler special java based library for scheduling tasks.
Here are the links for that sites
This library can be used in any java development environment like IntelliJ IDEA,Netbeans or in Eclipse.
Upvotes: 1
Reputation: 1650
Below is the code of a proof-of-concept app that achieves what you specify in your question. It does so using Quartz as the scheduler and H2 as the underlying database. I assume Maven standard file/folder structure.
It is a command-line application that accepts lines from stdin each specifying how many seconds to wait prior to firing and what to print then (so you enter "15 hello, world!" and press enter and it will schedule to print "hello, world!" in 15 seconds, you can schedule several tasks without waiting for any of them to finish). If you kill the app prior to the tasks being fired and then run the app again, they will run either at the specified time if it hasn't passed yet or immediately if it has.
1) src/main/java/App.java
:
import java.io.*;
import java.sql.*;
import java.util.*;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.util.IOUtils;
import org.quartz.*;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.impl.StdSchedulerFactory;
public class App {
public static class PrintJob implements Job {
public void execute(JobExecutionContext ctx)
throws JobExecutionException {
System.out.println(ctx.getJobDetail().getJobDataMap()
.get("message"));
}
}
public static void main(String[] args) throws SchedulerException,
ClassNotFoundException, SQLException, IOException {
setUpDb();
try (Scanner scanner = new Scanner(System.in)) {
SchedulerFactory schedFactory = new StdSchedulerFactory();
Scheduler sched = schedFactory.getScheduler();
sched.start();
while (true) {
int dt = scanner.nextInt();
String s = scanner.nextLine().trim();
JobDetail jd =
JobBuilder.newJob(PrintJob.class)
.withIdentity(UUID.randomUUID().toString())
.usingJobData("message", s).build();
Trigger trigger =
TriggerBuilder
.newTrigger()
.startAt(
DateBuilder.futureDate(dt, IntervalUnit.SECOND))
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow())
.build();
sched.scheduleJob(jd, trigger);
}
}
}
private static void setUpDb() throws ClassNotFoundException, IOException,
SQLException, UnsupportedEncodingException {
Class.forName("org.h2.Driver");
JdbcDataSource ds = new JdbcDataSource();
ds.setURL("jdbc:h2:~/my_db");
ds.setUser("sa");
ds.setPassword("");
InputStream in =
App.class.getClassLoader().getResourceAsStream("tables_h2.sql");
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copyAndClose(in, out);
try (Connection conn = ds.getConnection()) {
try {
conn.createStatement().executeQuery(
"select count(*) from QRTZ_JOB_DETAILS");
} catch (Exception e) {
conn.createStatement().executeUpdate(
new String(out.toByteArray(), "utf-8"));
}
}
}
}
2) src/main/resources/quartz.properties
:
org.quartz.threadPool.threadCount=3
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.tablePrefix=qrtz_
org.quartz.dataSource.myDS.driver=org.h2.Driver
org.quartz.dataSource.myDS.URL=jdbc:h2:~/my_db;MVCC=true
org.quartz.dataSource.myDS.user=sa
org.quartz.dataSource.myDS.password=
org.quartz.dataSource.myDS.maxConnections=3
3) src/main/resources/tables_h2.sql
(taken from quartz 2.2.3 distribution, folder docs/dbTables
):
-- Thanks to Amir Kibbar and Peter Rietzler for contributing the schema for H2 database,
-- and verifying that it works with Quartz's StdJDBCDelegate
--
-- Note, Quartz depends on row-level locking which means you must use the MVCC=TRUE
-- setting on your H2 database, or you will experience dead-locks
--
--
-- In your Quartz properties file, you'll need to set
-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
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;
4) pom.xml
:
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.ra</groupId>
<artifactId>so_qrtz</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>so_qrtz</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.187</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
</project>
Upvotes: 1