Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cron-support using shaded com.cronutils library. #56

Merged
merged 3 commits into from
Mar 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
target/
.classpath
.project
.settings/
.settings/
dependency-reduced-pom.xml
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This software includes third party software subject to the following licenses:
Apache Commons Compress under Apache License, Version 2.0
Apache Commons IO under Apache License, Version 2.0
Apache Commons Lang under Apache License, Version 2.0
cron-utils under Apache 2.0
Db-scheduler under The Apache Software License, Version 2.0
Guava: Google Core Libraries for Java under The Apache Software License, Version 2.0
Hamcrest Core under New BSD License
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ See also [why not Quartz?](#why-db-scheduler-when-there-is-quartz)

2. Create the `scheduled_tasks` table in your database-schema. See table definition for [postgresql](https://github.com/kagkarlsson/db-scheduler/blob/master/src/test/resources/postgresql_tables.sql), [oracle](https://github.com/kagkarlsson/db-scheduler/blob/master/src/test/resources/oracle_tables.sql), [mssql](https://github.com/kagkarlsson/db-scheduler/blob/master/src/test/resources/mssql_tables.sql) or [mysql](https://github.com/kagkarlsson/db-scheduler/blob/master/src/test/resources/mysql_tables.sql).

Note: `scheduled_tasks` is the default table name, but it is [customizable](#scheduler-configuration).

3. Instantiate and start the scheduler, which then will start any defined recurring tasks.

```java
Expand All @@ -55,7 +53,7 @@ For more examples, continue reading. For details on the inner workings, see [How

### Recurring task

Define a _recurring_ task and schedule the task's first execution on start-up using the `startTasks` builder-method. Upon completion, the task will be re-scheduled according to the defined schedule.
Define a _recurring_ task and schedule the task's first execution on start-up using the `startTasks` builder-method. Upon completion, the task will be re-scheduled according to the defined schedule (see [pre-defined schedule-types](#schedules)).

```java
RecurringTask<Void> hourlyTask = Tasks.recurring("my-hourly-task", FixedDelay.ofHours(1))
Expand Down Expand Up @@ -153,6 +151,17 @@ Tasks are created using one of the builder-classes in `Tasks`. The builders have
| `.initialData(T initialData)` | `null` | The data to use the first time a _recurring task_ is scheduled. |


### Schedules

The library contains a number of Schedule-implementations for recurring tasks. See class `Schedules`.

| Schedule | Description |
| ------------- | ------------- |
| `.daily(LocalTime ...)` | Runs every day at specified times. |
| `.fixedDelay(Duration)` | Next execution-time is `Duration` after last completed execution. |
| `.cron(String)` | Spring-style cron-expression. |



## How it works

Expand Down
35 changes: 35 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>com.cronutils</groupId>
<artifactId>cron-utils</artifactId>
<version>8.0.0</version>
</dependency>

<!-- Test -->
<dependency>
Expand Down Expand Up @@ -205,6 +210,7 @@
</excludes>
<includes>
<include>com.github.kagkarlsson:micro-jdbc</include>
<include>com.cronutils:cron-utils:jar</include>
<include>org.slf4j:slf4j-api</include>
<include>org.slf4j:slf4j-simple</include>
<!-- Tests -->
Expand Down Expand Up @@ -263,6 +269,32 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<artifactSet>
<includes>
<include>com.cronutils:*</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>com.cronutils</pattern>
<shadedPattern>com.github.kagkarlsson.shaded.cronutils</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- Release plugins -->
<plugin>
Expand Down Expand Up @@ -351,6 +383,9 @@
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
</plugins>

</build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright (C) Gustav Karlsson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.kagkarlsson.scheduler.task.schedule;

import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import com.github.kagkarlsson.scheduler.task.ExecutionComplete;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

/**
* Spring-style cron-pattern schedule
*/
public class CronSchedule implements Schedule {

private static final Logger LOG = LoggerFactory.getLogger(CronSchedule.class);
private final ExecutionTime cronExecutionTime;

public CronSchedule(String pattern) {
CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING));
Cron cron = parser.parse(pattern);
this.cronExecutionTime = ExecutionTime.forCron(cron);
}

@Override
public Instant getNextExecutionTime(ExecutionComplete executionComplete) {
Optional<ZonedDateTime> nextTime = cronExecutionTime.nextExecution(ZonedDateTime.ofInstant(executionComplete.getTimeDone(), ZoneId.systemDefault()));
if (!nextTime.isPresent()) {
LOG.error("Cron-pattern did not return any further execution-times. This behavior is currently not supported by the scheduler. Setting next execution-time to far-future.");
return Instant.now().plus(1000, ChronoUnit.YEARS);
}
return nextTime.get().toInstant();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public static Schedule fixedDelay(Duration delay) {
return FixedDelay.of(delay);
}

public static Schedule cron(String cronPattern) { return new CronSchedule(cronPattern); }

/**
* Currently supports Daily- and FixedDelay-schedule on the formats:
* <pre>DAILY|hh:mm,hh:mm,...,hh:mm</pre><br/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.github.kagkarlsson.scheduler.example;

import com.github.kagkarlsson.scheduler.HsqlTestDatabaseRule;
import com.github.kagkarlsson.scheduler.Scheduler;
import com.github.kagkarlsson.scheduler.task.*;
import com.github.kagkarlsson.scheduler.task.helper.OneTimeTask;
import com.github.kagkarlsson.scheduler.task.helper.RecurringTask;
import com.github.kagkarlsson.scheduler.task.helper.Tasks;
import com.github.kagkarlsson.scheduler.task.schedule.Schedules;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalField;

public class CronMain {
private static final Logger LOG = LoggerFactory.getLogger(CronMain.class);

private static void example(DataSource dataSource) {

RecurringTask<Void> cronTask = Tasks.recurring("cron-task", Schedules.cron("*/3 * * * * ?"))
.execute((taskInstance, executionContext) -> {
System.out.println(Instant.now().getEpochSecond() + "s - Cron-schedule!");
});

final Scheduler scheduler = Scheduler
.create(dataSource)
.startTasks(cronTask)
.pollingInterval(Duration.ofSeconds(1))
.build();

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOG.info("Received shutdown signal.");
scheduler.stop();
}));

scheduler.start();
}

public static void main(String[] args) throws Throwable {
try {
final HsqlTestDatabaseRule hsqlRule = new HsqlTestDatabaseRule();
hsqlRule.before();
final DataSource dataSource = hsqlRule.getDataSource();

example(dataSource);
} catch (Exception e) {
LOG.error("Error", e);
}

}

}
47 changes: 47 additions & 0 deletions src/test/java/com/github/kagkarlsson/scheduler/task/CronTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.github.kagkarlsson.scheduler.task;

import com.github.kagkarlsson.scheduler.task.schedule.CronSchedule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class CronTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Test
public void should_validate_pattern() {
expectedException.expect(IllegalArgumentException.class);
new CronSchedule("asdf asdf asdf");
}

@Test
public void should_generate_next_date_correctly() {
ZonedDateTime timeDone = ZonedDateTime.of(2000, 1, 1, 12, 0, 0, 0, ZoneId.systemDefault());

assertNextExecutionTime(timeDone, "0 * * * * ?", timeDone.plusMinutes(1));
assertNextExecutionTime(timeDone, "0 0 * * * ?", timeDone.plusHours(1));
assertNextExecutionTime(timeDone, "0 0 12 * * ?", timeDone.plusDays(1));
assertNextExecutionTime(timeDone, "0 0 12 1 * ?", timeDone.plusMonths(1));
assertNextExecutionTime(timeDone, "0 0 12 1 1 ?", timeDone.plusYears(1));
}

private void assertNextExecutionTime(ZonedDateTime timeDone, String cronPattern, ZonedDateTime expectedTime) {
CronSchedule schedule = new CronSchedule(cronPattern);
Instant nextExecutionTime = schedule.getNextExecutionTime(complete(timeDone.toInstant()));

assertThat(nextExecutionTime, is(expectedTime.toInstant()));
}

private ExecutionComplete complete(Instant timeDone) {
return ExecutionComplete.success(null, timeDone, timeDone);
}
}