mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2025-12-30 22:23:15 +09:00
@@ -108,11 +108,11 @@
|
||||
"defaultProject": "angular-spring-starter",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"prefix": "qrzmng",
|
||||
"style": "css"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app"
|
||||
"prefix": "qrzmng"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,64 @@
|
||||
<mat-card-header>
|
||||
<mat-card-title><b>SCHEDULER CONFIG</b></mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
|
||||
<!-- ADD BUTTON -->
|
||||
<mat-card-content *ngIf="!existsATriggerInProgress() && !enabledTriggerForm">
|
||||
<button mat-fab color="primary">
|
||||
<mat-icon (click)="enableTriggerForm()">add</mat-icon>
|
||||
</button>
|
||||
</mat-card-content>
|
||||
|
||||
<!-- TRIGGER DETAILS -->
|
||||
<mat-card-content *ngIf="existsATriggerInProgress() || enabledTriggerForm">
|
||||
<div fxLayout="column">
|
||||
<form name="configForm" fxFlex="1 1 100%" #configForm="ngForm">
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Freq [Num per day]" [(ngModel)]="config.triggerPerDay" name="triggerPerDay" type="number">
|
||||
<mat-form-field [appearance]="enabledTriggerForm ? 'fill': 'none'">
|
||||
<mat-label>Freq [Num per day]</mat-label>
|
||||
<input [readonly]="!enabledTriggerForm"
|
||||
matInput placeholder="Freq [Num per day]" name="triggerPerDay" type="number"
|
||||
[(ngModel)]="config.triggerPerDay"
|
||||
>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Max Occurrences" [(ngModel)]="config.maxCount" name="maxCount" type="number">
|
||||
<mat-form-field [appearance]="enabledTriggerForm ? 'fill': 'none'">
|
||||
<mat-label>Max Occurrences</mat-label>
|
||||
<input [readonly]="!enabledTriggerForm"
|
||||
matInput placeholder="Max Occurrences" name="maxCount" type="number"
|
||||
[(ngModel)]="config.maxCount"
|
||||
>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
<div>
|
||||
<h5>Misfire Policy</h5>
|
||||
<div>RESCHEDULE NEXT WITH REMAINING COUNT</div>
|
||||
<div class="small">
|
||||
In case of misfire event, the trigger is re-scheduled to the next scheduled time after 'now' with the repeat count set to what it would be, if it had not missed any firings.
|
||||
In case of misfire event, the trigger is re-scheduled to the next scheduled time after 'now' with the repeat count set to what it would be, if it had not missed any firings.
|
||||
<br/>
|
||||
<strong>Warning:</strong> This policy could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed.
|
||||
<strong>Warning:</strong> This policy could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
<button mat-raised-button
|
||||
type="button"
|
||||
*ngIf="enabledTriggerForm"
|
||||
(click)="cancelConfigForm()">
|
||||
Cancel
|
||||
</button>
|
||||
<button mat-raised-button
|
||||
type="button"
|
||||
type="button" color="primary"
|
||||
*ngIf="enabledTriggerForm"
|
||||
(click)="submitConfig()">
|
||||
Submit
|
||||
</button>
|
||||
<button mat-raised-button type="button"
|
||||
*ngIf="!enabledTriggerForm"
|
||||
(click)="enabledTriggerForm = true">
|
||||
Reschedule
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
|
||||
@@ -1,40 +1,69 @@
|
||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { SchedulerService } from '../../services';
|
||||
import { SchedulerConfig } from '../../model/schedulerConfig.model'
|
||||
import {Scheduler} from '../../model/scheduler.model';
|
||||
|
||||
@Component({
|
||||
selector: 'scheduler-config',
|
||||
selector: 'qrzmng-scheduler-config',
|
||||
templateUrl: './scheduler-config.component.html',
|
||||
styleUrls: ['./scheduler-config.component.scss']
|
||||
})
|
||||
export class SchedulerConfigComponent implements OnInit {
|
||||
|
||||
config: SchedulerConfig = new SchedulerConfig()
|
||||
configBackup: SchedulerConfig = new SchedulerConfig()
|
||||
scheduler: Scheduler;
|
||||
|
||||
triggerLoading = true;
|
||||
enabledTriggerForm = false;
|
||||
private fetchedTriggers = false;
|
||||
private triggerInProgress = false;
|
||||
|
||||
constructor(
|
||||
private schedulerService: SchedulerService
|
||||
) { }
|
||||
|
||||
config : SchedulerConfig = new SchedulerConfig()
|
||||
configBackup : SchedulerConfig = new SchedulerConfig()
|
||||
|
||||
ngOnInit() {
|
||||
this.retrieveConfig()
|
||||
this.triggerLoading = true;
|
||||
this._getScheduler();
|
||||
this.retrieveConfig();
|
||||
}
|
||||
|
||||
retrieveConfig = () => {
|
||||
this.schedulerService.getConfig()
|
||||
.subscribe(res => {
|
||||
this.config = new SchedulerConfig(res.triggerPerDay, res.maxCount)
|
||||
this.config = new SchedulerConfig(res.triggerPerDay, res.maxCount, res.timesTriggered)
|
||||
this.configBackup = res
|
||||
this.triggerLoading = false;
|
||||
this.triggerInProgress = res.timesTriggered < res.maxCount;
|
||||
})
|
||||
}
|
||||
|
||||
private _getScheduler() {
|
||||
this.schedulerService.getScheduler()
|
||||
.subscribe( res => {
|
||||
this.scheduler = <Scheduler>res;
|
||||
this.fetchedTriggers = this.scheduler.triggerKeys.length > 0
|
||||
})
|
||||
}
|
||||
|
||||
existsATriggerInProgress = (): boolean => this.fetchedTriggers && this.triggerInProgress;
|
||||
|
||||
cancelConfigForm = () => this.enabledTriggerForm = false;
|
||||
|
||||
submitConfig = () => {
|
||||
this.schedulerService.updateConfig(this.config)
|
||||
const schedulerServiceCall = this.existsATriggerInProgress() ? this.schedulerService.updateConfig : this.schedulerService.saveConfig;
|
||||
|
||||
schedulerServiceCall(this.config)
|
||||
.subscribe(res => {
|
||||
this.configBackup = this.config;
|
||||
this.enabledTriggerForm = false;
|
||||
this.fetchedTriggers = true;
|
||||
this.triggerInProgress = true;
|
||||
}, error => {
|
||||
this.config = this.configBackup;
|
||||
});
|
||||
};
|
||||
|
||||
enableTriggerForm = () => this.enabledTriggerForm = true;
|
||||
}
|
||||
|
||||
13
quartz-manager-frontend/src/app/model/scheduler.model.ts
Normal file
13
quartz-manager-frontend/src/app/model/scheduler.model.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {TriggerKey} from './triggerKey.model';
|
||||
|
||||
export class Scheduler {
|
||||
name: string;
|
||||
instanceId: string;
|
||||
triggerKeys: TriggerKey[];
|
||||
|
||||
constructor(name: string, instanceId: string, triggerKeys: TriggerKey[]) {
|
||||
this.name = name;
|
||||
this.instanceId = instanceId;
|
||||
this.triggerKeys = triggerKeys;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
export class SchedulerConfig {
|
||||
|
||||
triggerPerDay : number = 0
|
||||
maxCount : number = 0
|
||||
triggerPerDay = 0;
|
||||
maxCount = 0;
|
||||
timesTriggered = 0;
|
||||
|
||||
constructor(triggerPerDay = 0, maxCount = 0) {
|
||||
this.triggerPerDay = triggerPerDay
|
||||
this.maxCount = maxCount
|
||||
constructor(triggerPerDay = 0, maxCount = 0, timesTriggered = 0) {
|
||||
this.triggerPerDay = triggerPerDay;
|
||||
this.maxCount = maxCount;
|
||||
this.timesTriggered = timesTriggered;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export class TriggerKey {
|
||||
name: string;
|
||||
group: string;
|
||||
|
||||
constructor(name: string, group: string) {
|
||||
this.name = name;
|
||||
this.group = group;
|
||||
}
|
||||
}
|
||||
@@ -12,20 +12,24 @@ export class SchedulerService {
|
||||
startScheduler = () => {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/run')
|
||||
}
|
||||
|
||||
|
||||
stopScheduler = () => {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/stop')
|
||||
}
|
||||
|
||||
|
||||
pauseScheduler = () => {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/pause')
|
||||
}
|
||||
|
||||
|
||||
resumeScheduler = () => {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/resume')
|
||||
}
|
||||
|
||||
getStatus = () => {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/status')
|
||||
}
|
||||
|
||||
getScheduler = () => {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler')
|
||||
}
|
||||
|
||||
@@ -33,7 +37,13 @@ export class SchedulerService {
|
||||
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/config')
|
||||
}
|
||||
|
||||
updateConfig = (config: Object) => {
|
||||
return this.apiService.post(getBaseUrl() + '/quartz-manager/scheduler/config', config)
|
||||
saveConfig = (config: Object) => {
|
||||
return this.apiService.post(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config)
|
||||
}
|
||||
|
||||
updateConfig = (config: Object) => {
|
||||
return this.apiService.put(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
<div fxLayout="column">
|
||||
<scheduler-control></scheduler-control>
|
||||
<br/>
|
||||
<scheduler-config></scheduler-config>
|
||||
<qrzmng-scheduler-config></qrzmng-scheduler-config>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div fxFlex="1 1 70%" style="margin-left: 20px">
|
||||
<div fxLayout="column">
|
||||
<div fxFlex="1 1 100%"><progress-panel></progress-panel></div>
|
||||
<br/>
|
||||
<div fxFlex="1 1 100%"><logs-panel></logs-panel></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
1
quartz-manager-parent/.gitignore
vendored
1
quartz-manager-parent/.gitignore
vendored
@@ -3,3 +3,4 @@
|
||||
/**/target
|
||||
.classpath
|
||||
.project
|
||||
.idea
|
||||
|
||||
@@ -44,10 +44,17 @@
|
||||
<module>quartz-manager-starter-ui</module>
|
||||
<module>quartz-manager-starter-security</module>
|
||||
<module>quartz-manager-web-showcase</module>
|
||||
<module>quartz-manager-starter-persistence</module>
|
||||
<module>quartz-manager-common</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-common</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
@@ -58,6 +65,11 @@
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-persistence</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
|
||||
23
quartz-manager-parent/quartz-manager-common/pom.xml
Normal file
23
quartz-manager-parent/quartz-manager-common/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>quartz-manager-common</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package it.fabioformosa.quartzmanager.common.properties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@Data
|
||||
public class QuartzModuleProperties{
|
||||
|
||||
private Properties properties = new Properties();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package it.fabioformosa.quartzmanager.common.utils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateUtils {
|
||||
|
||||
static public Date fromLocaleDateTimeToDate(LocalDateTime localDateTime){
|
||||
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
static public Date getHoursFromNow(long hours){
|
||||
return DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now().plus(Duration.ofHours(hours)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package it.fabioformosa.quartzmanager.common.utils;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Try<R> {
|
||||
|
||||
private final Throwable failure;
|
||||
private final R success;
|
||||
|
||||
public Try(Throwable failure, R success) {
|
||||
this.failure = failure;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public R getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public static <R> Try success(R r){
|
||||
return new Try<>(null, r);
|
||||
}
|
||||
|
||||
public static <ExceptionType> Try failure(Throwable e){
|
||||
return new Try<>(e, null);
|
||||
}
|
||||
|
||||
public static <T, R> Function<T, Try<R>> with(CheckedFunction<T, R> checkedFunction){
|
||||
return t -> {
|
||||
try {
|
||||
return Try.success(checkedFunction.apply(t));
|
||||
} catch (java.lang.Exception e) {
|
||||
return Try.failure(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T, R> Function<T, R> sneakyThrow(CheckedFunction<T, R> checkedFunction){
|
||||
return t -> Try.with(checkedFunction).apply(t).getSuccess();
|
||||
}
|
||||
|
||||
public boolean isSuccess(){
|
||||
return this.failure == null;
|
||||
}
|
||||
|
||||
public boolean isFailure(){
|
||||
return this.failure != null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface CheckedFunction<T, R> {
|
||||
R apply(T t) throws java.lang.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,142 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
|
||||
<name>Quartz Manager Starter API</name>
|
||||
<description>REST API layer for your scheduler and triggered jobs, to be included in your spring webapp</description>
|
||||
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<springfox.version>2.9.2</springfox.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- SPRING -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MISC -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- QUARTZ -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Reactor -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-net</artifactId>
|
||||
<version>2.0.8.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.spring</groupId>
|
||||
<artifactId>reactor-spring-context</artifactId>
|
||||
<version>2.0.7.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SWAGGER -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${springfox.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${springfox.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
|
||||
<name>Quartz Manager Starter API</name>
|
||||
<description>REST API layer for your scheduler and triggered jobs, to be included in your spring webapp</description>
|
||||
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<springfox.version>2.9.2</springfox.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SPRING -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MISC -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa</groupId>
|
||||
<artifactId>metamorphosis-core</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- QUARTZ -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Reactor -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-net</artifactId>
|
||||
<version>2.0.8.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.spring</groupId>
|
||||
<artifactId>reactor-spring-context</artifactId>
|
||||
<version>2.0.7.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SWAGGER -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${springfox.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${springfox.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TEST -->
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package it.fabioformosa.quartzmanager.aspects;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerStatus;
|
||||
import it.fabioformosa.quartzmanager.services.SchedulerService;
|
||||
import org.quartz.DailyTimeIntervalTrigger;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SimpleTrigger;
|
||||
import org.quartz.Trigger;
|
||||
@@ -11,8 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.simp.SimpMessageSendingOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerStatus;
|
||||
import it.fabioformosa.quartzmanager.scheduler.TriggerMonitor;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -28,11 +26,14 @@ public class WebSocketProgressNotifier implements ProgressNotifier {
|
||||
@Autowired
|
||||
private SimpMessageSendingOperations messagingTemplate;
|
||||
|
||||
@Resource
|
||||
private Scheduler scheduler;
|
||||
// @Resource
|
||||
// private Scheduler scheduler;
|
||||
|
||||
@Resource
|
||||
private TriggerMonitor triggerMonitor;
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
// @Resource
|
||||
// private TriggerMonitor triggerMonitor;
|
||||
|
||||
//@AfterReturning("execution(* logAndSend(..))")
|
||||
// @Override
|
||||
@@ -44,7 +45,7 @@ public class WebSocketProgressNotifier implements ProgressNotifier {
|
||||
public void send() throws SchedulerException {
|
||||
TriggerStatus currTriggerStatus = new TriggerStatus();
|
||||
|
||||
Trigger trigger = scheduler.getTrigger(triggerMonitor.getTrigger().getKey());
|
||||
Trigger trigger = schedulerService.getOneSimpleTrigger().get();
|
||||
currTriggerStatus.setFinalFireTime(trigger.getFinalFireTime());
|
||||
currTriggerStatus.setNextFireTime(trigger.getNextFireTime());
|
||||
currTriggerStatus.setPreviousFireTime(trigger.getPreviousFireTime());
|
||||
@@ -62,7 +63,7 @@ public class WebSocketProgressNotifier implements ProgressNotifier {
|
||||
repeatCount = dailyTrigger.getRepeatCount();
|
||||
}
|
||||
|
||||
Trigger jobTrigger = triggerMonitor.getTrigger();
|
||||
Trigger jobTrigger = schedulerService.getOneSimpleTrigger().get();
|
||||
if (jobTrigger != null && jobTrigger.getJobKey() != null) {
|
||||
currTriggerStatus.setJobKey(jobTrigger.getJobKey().getName());
|
||||
currTriggerStatus.setJobClass(jobTrigger.getClass().getSimpleName());
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package it.fabioformosa.quartzmanager.configuration;
|
||||
|
||||
import it.fabioformosa.metamorphosis.core.EnableMetamorphosisConversions;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableMetamorphosisConversions(basePackages = { "it.fabioformosa.quartzmanager" })
|
||||
public class ConversionConfig {
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
package it.fabioformosa.quartzmanager.configuration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import it.fabioformosa.quartzmanager.common.properties.QuartzModuleProperties;
|
||||
import it.fabioformosa.quartzmanager.scheduler.AutowiringSpringBeanJobFactory;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.SimpleTrigger;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.spi.JobFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -19,19 +15,15 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
|
||||
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
|
||||
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
|
||||
|
||||
import it.fabioformosa.quartzmanager.scheduler.AutowiringSpringBeanJobFactory;
|
||||
import it.fabioformosa.quartzmanager.scheduler.TriggerMonitor;
|
||||
import it.fabioformosa.quartzmanager.scheduler.TriggerMonitorImpl;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
@ComponentScan(basePackages = {"it.fabioformosa.quartzmanager.controllers"})
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "quartz.enabled")
|
||||
public class SchedulerConfig {
|
||||
|
||||
private static final int DEFAULT_MISFIRE_INSTRUCTION = SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;
|
||||
|
||||
private static JobDetailFactoryBean createJobDetail(Class<? extends Job> jobClass) {
|
||||
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
|
||||
factoryBean.setJobClass(jobClass);
|
||||
@@ -39,30 +31,13 @@ public class SchedulerConfig {
|
||||
return factoryBean;
|
||||
}
|
||||
|
||||
private static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs,
|
||||
int repeatCount) {
|
||||
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
|
||||
factoryBean.setJobDetail(jobDetail);
|
||||
factoryBean.setStartDelay(3000L);
|
||||
factoryBean.setRepeatInterval(pollFrequencyMs);
|
||||
factoryBean.setRepeatCount(repeatCount);
|
||||
factoryBean
|
||||
.setMisfireInstruction(DEFAULT_MISFIRE_INSTRUCTION);// in case of misfire, ignore all missed triggers and continue
|
||||
return factoryBean;
|
||||
}
|
||||
|
||||
@Value("${quartz-manager.jobClass}")
|
||||
private String jobClassname;
|
||||
|
||||
@Bean(name = "triggerMonitor")
|
||||
public TriggerMonitor createTriggerMonitor(@Qualifier("jobTrigger") Trigger trigger) {
|
||||
TriggerMonitor triggerMonitor = new TriggerMonitorImpl();
|
||||
triggerMonitor.setTrigger(trigger);
|
||||
return triggerMonitor;
|
||||
}
|
||||
@Autowired(required = false)
|
||||
private QuartzModuleProperties quartzModuleProperties;
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
public JobDetailFactoryBean jobDetail() throws ClassNotFoundException {
|
||||
Class<? extends Job> JobClass = (Class<? extends Job>) Class.forName(jobClassname);
|
||||
return createJobDetail(JobClass);
|
||||
@@ -83,20 +58,16 @@ public class SchedulerConfig {
|
||||
return propertiesFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Bean(name = "jobTrigger")
|
||||
public SimpleTriggerFactoryBean sampleJobTrigger(@Qualifier("jobDetail") JobDetail jobDetail,
|
||||
@Value("${job.frequency}") long frequency, @Value("${job.repeatCount}") int repeatCount) {
|
||||
return createTrigger(jobDetail, frequency, repeatCount);
|
||||
}
|
||||
|
||||
@Bean(name = "scheduler")
|
||||
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory,
|
||||
@Qualifier("jobTrigger") Trigger sampleJobTrigger) throws IOException {
|
||||
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws IOException {
|
||||
SchedulerFactoryBean factory = new SchedulerFactoryBean();
|
||||
factory.setJobFactory(jobFactory);
|
||||
factory.setQuartzProperties(quartzProperties());
|
||||
factory.setTriggers(sampleJobTrigger);
|
||||
Properties mergedProperties = new Properties();
|
||||
if(quartzModuleProperties != null)
|
||||
mergedProperties.putAll(quartzModuleProperties.getProperties());
|
||||
mergedProperties.putAll(quartzProperties());
|
||||
factory.setQuartzProperties(mergedProperties);
|
||||
factory.setAutoStartup(false);
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,161 +1,127 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SimpleScheduleBuilder;
|
||||
import org.quartz.SimpleTrigger;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerBuilder;
|
||||
import org.quartz.impl.triggers.SimpleTriggerImpl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerStatus;
|
||||
import it.fabioformosa.quartzmanager.enums.SchedulerStates;
|
||||
import it.fabioformosa.quartzmanager.scheduler.TriggerMonitor;
|
||||
import it.fabioformosa.quartzmanager.services.SchedulerService;
|
||||
import org.quartz.*;
|
||||
import org.quartz.impl.triggers.SimpleTriggerImpl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This controller provides scheduler info about config and status. It provides
|
||||
* also methods to set new config and start/stop/resume the scheduler.
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/quartz-manager/scheduler")
|
||||
@Api(value = "scheduler")
|
||||
public class SchedulerController {
|
||||
|
||||
private static final int MILLS_IN_A_DAY = 1000 * 60 * 60 * 24;
|
||||
private static final int SEC_IN_A_DAY = 60 * 60 * 24;
|
||||
private final Logger log = LoggerFactory.getLogger(SchedulerController.class);
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(SchedulerController.class);
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@Resource
|
||||
private Scheduler scheduler;
|
||||
public SchedulerController(SchedulerService schedulerService, ConversionService conversionService) {
|
||||
this.schedulerService = schedulerService;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Resource
|
||||
private TriggerMonitor triggerMonitor;
|
||||
@Resource
|
||||
private ConversionService conversionService;
|
||||
|
||||
private long fromMillsIntervalToTriggerPerDay(long repeatIntervalInMills) {
|
||||
return (int) Math.ceil(MILLS_IN_A_DAY / repeatIntervalInMills);
|
||||
@GetMapping("/config")
|
||||
public SchedulerConfigParam getConfig() throws SchedulerException {
|
||||
log.debug("SCHEDULER - GET CONFIG params");
|
||||
SchedulerConfigParam schedulerConfigParam = schedulerService.getOneSimpleTrigger()
|
||||
.map(SchedulerController::fromSimpleTriggerToSchedulerConfigParam)
|
||||
.orElse(new SchedulerConfigParam(0, 0, 0));
|
||||
return schedulerConfigParam;
|
||||
}
|
||||
|
||||
public static SchedulerConfigParam fromSimpleTriggerToSchedulerConfigParam(SimpleTrigger simpleTrigger){
|
||||
int timesTriggered = simpleTrigger.getTimesTriggered();
|
||||
int maxCount = simpleTrigger.getRepeatCount() + 1;
|
||||
long triggersPerDay = SchedulerService.fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval());
|
||||
return new SchedulerConfigParam(triggersPerDay, maxCount, timesTriggered);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public SchedulerDTO getScheduler() {
|
||||
log.debug("SCHEDULER - GET Scheduler...");
|
||||
SchedulerDTO schedulerDTO = conversionService.convert(schedulerService.getScheduler(), SchedulerDTO.class);
|
||||
return schedulerDTO;
|
||||
}
|
||||
|
||||
@GetMapping("/progress")
|
||||
public TriggerStatus getProgressInfo() throws SchedulerException {
|
||||
log.trace("SCHEDULER - GET PROGRESS INFO");
|
||||
TriggerStatus progress = new TriggerStatus();
|
||||
|
||||
SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) schedulerService.getOneSimpleTrigger().get();
|
||||
if (jobTrigger != null && jobTrigger.getJobKey() != null) {
|
||||
progress.setJobKey(jobTrigger.getJobKey().getName());
|
||||
progress.setJobClass(jobTrigger.getClass().getSimpleName());
|
||||
progress.setTimesTriggered(jobTrigger.getTimesTriggered());
|
||||
progress.setRepeatCount(jobTrigger.getRepeatCount());
|
||||
progress.setFinalFireTime(jobTrigger.getFinalFireTime());
|
||||
progress.setNextFireTime(jobTrigger.getNextFireTime());
|
||||
progress.setPreviousFireTime(jobTrigger.getPreviousFireTime());
|
||||
}
|
||||
|
||||
private int fromTriggerPerDayToMillsInterval(long triggerPerDay) {
|
||||
return (int) Math.ceil(Long.valueOf(MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private int fromTriggerPerDayToSecInterval(long triggerPerDay) {
|
||||
return (int) Math.ceil(Long.valueOf(SEC_IN_A_DAY) / triggerPerDay);
|
||||
}
|
||||
@GetMapping(value = "/status", produces = "application/json")
|
||||
public Map<String, String> getStatus() throws SchedulerException {
|
||||
log.trace("SCHEDULER - GET STATUS");
|
||||
String schedulerState = "";
|
||||
if (schedulerService.getScheduler().isShutdown() || !schedulerService.getScheduler().isStarted())
|
||||
schedulerState = SchedulerStates.STOPPED.toString();
|
||||
else if (schedulerService.getScheduler().isStarted() && schedulerService.getScheduler().isInStandbyMode())
|
||||
schedulerState = SchedulerStates.PAUSED.toString();
|
||||
else
|
||||
schedulerState = SchedulerStates.RUNNING.toString();
|
||||
return Collections.singletonMap("data", schedulerState.toLowerCase());
|
||||
}
|
||||
|
||||
@GetMapping("/config")
|
||||
public SchedulerConfigParam getConfig() throws SchedulerException {
|
||||
log.debug("SCHEDULER - GET CONFIG params");
|
||||
@GetMapping("/pause")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void pause() throws SchedulerException {
|
||||
log.info("SCHEDULER - PAUSE COMMAND");
|
||||
schedulerService.getScheduler().standby();
|
||||
}
|
||||
|
||||
SimpleTrigger jobTrigger = (SimpleTrigger) scheduler.getTrigger(triggerMonitor.getTrigger().getKey());
|
||||
int maxCount = jobTrigger.getRepeatCount() + 1;
|
||||
long triggersPerDay = fromMillsIntervalToTriggerPerDay(jobTrigger.getRepeatInterval());
|
||||
@GetMapping("/resume")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void resume() throws SchedulerException {
|
||||
log.info("SCHEDULER - RESUME COMMAND");
|
||||
schedulerService.getScheduler().start();
|
||||
}
|
||||
|
||||
return new SchedulerConfigParam(triggersPerDay, maxCount);
|
||||
}
|
||||
@GetMapping("/run")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void run() throws SchedulerException {
|
||||
log.info("SCHEDULER - START COMMAND");
|
||||
schedulerService.getScheduler().start();
|
||||
}
|
||||
|
||||
@GetMapping("/progress")
|
||||
public TriggerStatus getProgressInfo() throws SchedulerException {
|
||||
log.trace("SCHEDULER - GET PROGRESS INFO");
|
||||
TriggerStatus progress = new TriggerStatus();
|
||||
|
||||
SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) scheduler.getTrigger(triggerMonitor.getTrigger().getKey());
|
||||
if (jobTrigger != null && jobTrigger.getJobKey() != null) {
|
||||
progress.setJobKey(jobTrigger.getJobKey().getName());
|
||||
progress.setJobClass(jobTrigger.getClass().getSimpleName());
|
||||
progress.setTimesTriggered(jobTrigger.getTimesTriggered());
|
||||
progress.setRepeatCount(jobTrigger.getRepeatCount());
|
||||
progress.setFinalFireTime(jobTrigger.getFinalFireTime());
|
||||
progress.setNextFireTime(jobTrigger.getNextFireTime());
|
||||
progress.setPreviousFireTime(jobTrigger.getPreviousFireTime());
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
@GetMapping(produces = "application/json")
|
||||
public Map<String, String> getStatus() throws SchedulerException {
|
||||
log.trace("SCHEDULER - GET STATUS");
|
||||
String schedulerState = "";
|
||||
if (scheduler.isShutdown() || !scheduler.isStarted())
|
||||
schedulerState = SchedulerStates.STOPPED.toString();
|
||||
else if (scheduler.isStarted() && scheduler.isInStandbyMode())
|
||||
schedulerState = SchedulerStates.PAUSED.toString();
|
||||
else
|
||||
schedulerState = SchedulerStates.RUNNING.toString();
|
||||
return Collections.singletonMap("data", schedulerState.toLowerCase());
|
||||
}
|
||||
|
||||
@GetMapping("/pause")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void pause() throws SchedulerException {
|
||||
log.info("SCHEDULER - PAUSE COMMAND");
|
||||
scheduler.standby();
|
||||
}
|
||||
|
||||
@PostMapping("/config")
|
||||
public SchedulerConfigParam postConfig(@RequestBody SchedulerConfigParam config) throws SchedulerException {
|
||||
log.info("SCHEDULER - NEW CONFIG {}", config);
|
||||
SimpleTrigger trigger = (SimpleTrigger) triggerMonitor.getTrigger();
|
||||
|
||||
TriggerBuilder<SimpleTrigger> triggerBuilder = trigger.getTriggerBuilder();
|
||||
|
||||
int intervalInMills = fromTriggerPerDayToMillsInterval(config.getTriggerPerDay());
|
||||
|
||||
Trigger newTrigger = triggerBuilder
|
||||
.withSchedule(
|
||||
SimpleScheduleBuilder.simpleSchedule()
|
||||
.withIntervalInMilliseconds(intervalInMills)
|
||||
.withRepeatCount(config.getMaxCount() - 1)
|
||||
.withMisfireHandlingInstructionNextWithRemainingCount()
|
||||
)
|
||||
.build();
|
||||
|
||||
scheduler.rescheduleJob(triggerMonitor.getTrigger().getKey(), newTrigger);
|
||||
triggerMonitor.setTrigger(newTrigger);
|
||||
return config;
|
||||
}
|
||||
|
||||
@GetMapping("/resume")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void resume() throws SchedulerException {
|
||||
log.info("SCHEDULER - RESUME COMMAND");
|
||||
scheduler.start();
|
||||
}
|
||||
|
||||
@GetMapping("/run")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void run() throws SchedulerException {
|
||||
log.info("SCHEDULER - START COMMAND");
|
||||
scheduler.start();
|
||||
}
|
||||
|
||||
@GetMapping("/stop")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void stop() throws SchedulerException {
|
||||
log.info("SCHEDULER - STOP COMMAND");
|
||||
scheduler.shutdown(true);
|
||||
}
|
||||
@GetMapping("/stop")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void stop() throws SchedulerException {
|
||||
log.info("SCHEDULER - STOP COMMAND");
|
||||
schedulerService.getScheduler().shutdown(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import it.fabioformosa.quartzmanager.services.SchedulerService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Slf4j
|
||||
@RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL)
|
||||
@RestController
|
||||
@Api(value = "triggers")
|
||||
public class TriggerController {
|
||||
|
||||
static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers";
|
||||
|
||||
@Value("${quartz-manager.jobClass}")
|
||||
private String jobClassname;
|
||||
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
public TriggerController(SchedulerService schedulerService) {
|
||||
this.schedulerService = schedulerService;
|
||||
}
|
||||
|
||||
@GetMapping("/{name}")
|
||||
public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException {
|
||||
return schedulerService.getTriggerByName(name);
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@PostMapping("/{name}")
|
||||
public TriggerDTO postTrigger(@PathVariable String name, @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException {
|
||||
log.info("TRIGGER - CREATING a trigger {} {}", name, config);
|
||||
TriggerDTO newTriggerDTO = schedulerService.scheduleNewTrigger(name, jobClassname, config);
|
||||
log.info("TRIGGER - CREATED a trigger {}", newTriggerDTO);
|
||||
return newTriggerDTO;
|
||||
}
|
||||
|
||||
@PutMapping("/{name}")
|
||||
public TriggerDTO rescheduleTrigger(@PathVariable String name, @RequestBody SchedulerConfigParam config) throws SchedulerException {
|
||||
log.info("TRIGGER - RESCHEDULING the trigger {} {}", name, config);
|
||||
TriggerDTO triggerDTO = schedulerService.rescheduleTrigger(name, config);
|
||||
log.info("TRIGGER - RESCHEDULED the trigger {}", triggerDTO);
|
||||
return triggerDTO;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping(value = "/quartz-manager/api", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class UserController {
|
||||
|
||||
|
||||
@GetMapping("/whoami")
|
||||
public @ResponseBody Object user() {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
@@ -21,12 +20,12 @@ public class UserController {
|
||||
return "\"NO_AUTH\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT Temporary disabled
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
// /**
|
||||
// * JWT Temporary disabled
|
||||
// *
|
||||
// * @author Fabio.Formosa
|
||||
// *
|
||||
// */
|
||||
|
||||
// @Autowired
|
||||
// private UserService userService;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package it.fabioformosa.quartzmanager.converters;
|
||||
|
||||
import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.JobKeyDTO;
|
||||
import org.quartz.JobKey;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class JobKeyToJobKeyDTO extends AbstractBaseConverterToDTO<JobKey, JobKeyDTO> {
|
||||
@Override
|
||||
protected void convert(JobKey source, JobKeyDTO target) {
|
||||
target.setName(source.getName());
|
||||
target.setGroup(source.getGroup());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package it.fabioformosa.quartzmanager.converters;
|
||||
|
||||
import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerDTO;
|
||||
import lombok.SneakyThrows;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.impl.matchers.GroupMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SchedulerToSchedulerDTO extends AbstractBaseConverterToDTO<Scheduler, SchedulerDTO> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
protected void convert(Scheduler source, SchedulerDTO target) {
|
||||
target.setName(source.getSchedulerName());
|
||||
target.setInstanceId(source.getSchedulerInstanceId());
|
||||
target.setTriggerKeys(source.getTriggerKeys(GroupMatcher.anyTriggerGroup()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package it.fabioformosa.quartzmanager.converters;
|
||||
|
||||
import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TriggerKeyToTriggerKeyDTO extends AbstractBaseConverterToDTO<TriggerKey, TriggerKeyDTO> {
|
||||
|
||||
@Override
|
||||
protected void convert(TriggerKey source, TriggerKeyDTO target) {
|
||||
target.setName(source.getName());
|
||||
target.setGroup(source.getGroup());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package it.fabioformosa.quartzmanager.converters;
|
||||
|
||||
import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.JobKeyDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TriggerToTriggerDTO extends AbstractBaseConverterToDTO<Trigger, TriggerDTO> {
|
||||
|
||||
@Override
|
||||
protected void convert(Trigger source, TriggerDTO target) {
|
||||
TriggerKey triggerKey = source.getKey();
|
||||
TriggerKeyDTO triggerKeyDTO = conversionService.convert(triggerKey, TriggerKeyDTO.class);
|
||||
target.setTriggerKeyDTO(triggerKeyDTO);
|
||||
|
||||
target.setStartTime(source.getStartTime());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setEndTime(source.getEndTime());
|
||||
target.setFinalFireTime(source.getFinalFireTime());
|
||||
target.setMisfireInstruction(source.getMisfireInstruction());
|
||||
target.setNextFireTime(source.getNextFireTime());
|
||||
target.setPriority(source.getPriority());
|
||||
target.setMayFireAgain(source.mayFireAgain());
|
||||
|
||||
JobKey jobKey = source.getJobKey();
|
||||
JobKeyDTO jobKeyDTO = conversionService.convert(jobKey, JobKeyDTO.class);
|
||||
target.setJobKeyDTO(jobKeyDTO);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class JobKeyDTO {
|
||||
private String name;
|
||||
private String group;
|
||||
}
|
||||
@@ -1,40 +1,16 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
public class SchedulerConfigParam {
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Data
|
||||
public class SchedulerConfigParam {
|
||||
public long triggerPerDay;
|
||||
public int maxCount;
|
||||
|
||||
public SchedulerConfigParam() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SchedulerConfigParam(long triggerPerDay, int maxCount) {
|
||||
super();
|
||||
this.triggerPerDay = triggerPerDay;
|
||||
this.maxCount = maxCount;
|
||||
}
|
||||
|
||||
public int getMaxCount() {
|
||||
return maxCount;
|
||||
}
|
||||
|
||||
public long getTriggerPerDay() {
|
||||
return triggerPerDay;
|
||||
}
|
||||
|
||||
public void setMaxCount(int maxCount) {
|
||||
this.maxCount = maxCount;
|
||||
}
|
||||
|
||||
public void setTriggerPerDay(long triggerPerDay) {
|
||||
this.triggerPerDay = triggerPerDay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SchedulerConfigParam [triggerPerDay=" + triggerPerDay
|
||||
+ ", maxCount=" + maxCount + "]";
|
||||
}
|
||||
|
||||
public int timesTriggered;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
import org.quartz.TriggerKey;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class SchedulerDTO {
|
||||
private String name;
|
||||
private String instanceId;
|
||||
private Set<TriggerKey> triggerKeys;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setInstanceId(String instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public String getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setTriggerKeys(Set<TriggerKey> triggerKeys) {
|
||||
this.triggerKeys = triggerKeys;
|
||||
}
|
||||
|
||||
public Set<TriggerKey> getTriggerKeys() {
|
||||
return triggerKeys;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class TriggerDTO {
|
||||
private TriggerKeyDTO triggerKeyDTO;
|
||||
private int priority;
|
||||
private Date startTime;
|
||||
private String description;
|
||||
private Date endTime;
|
||||
private Date finalFireTime;
|
||||
private int misfireInstruction;
|
||||
private Date nextFireTime;
|
||||
private JobKeyDTO jobKeyDTO;
|
||||
private boolean mayFireAgain;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class TriggerKeyDTO {
|
||||
private String name;
|
||||
private String group;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package it.fabioformosa.quartzmanager.services;
|
||||
|
||||
import it.fabioformosa.quartzmanager.common.utils.Try;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import org.quartz.*;
|
||||
import org.quartz.impl.matchers.GroupMatcher;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class SchedulerService {
|
||||
|
||||
public static final int MILLS_IN_A_DAY = 1000 * 60 * 60 * 24;
|
||||
public static final int SEC_IN_A_DAY = 60 * 60 * 24;
|
||||
|
||||
private Scheduler scheduler;
|
||||
private ConversionService conversionService;
|
||||
|
||||
public SchedulerService(Scheduler scheduler, ConversionService conversionService) {
|
||||
this.scheduler = scheduler;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public static int fromTriggerPerDayToMillsInterval(long triggerPerDay) {
|
||||
return (int) Math.ceil(Long.valueOf(SchedulerService.MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value
|
||||
}
|
||||
|
||||
public static int fromTriggerPerDayToSecInterval(long triggerPerDay) {
|
||||
return (int) Math.ceil(Long.valueOf(SchedulerService.SEC_IN_A_DAY) / triggerPerDay);
|
||||
}
|
||||
|
||||
public static long fromMillsIntervalToTriggerPerDay(long repeatIntervalInMills) {
|
||||
return (int) Math.ceil(MILLS_IN_A_DAY / repeatIntervalInMills);
|
||||
}
|
||||
|
||||
public Scheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public Optional<TriggerKey> getTriggerByKey(String triggerKeyName) throws SchedulerException {
|
||||
return scheduler.getTriggerKeys(GroupMatcher.anyGroup()).stream()
|
||||
.filter(triggerKey -> triggerKey.getName().equals(triggerKeyName))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public Optional<SimpleTrigger> getOneSimpleTrigger() throws SchedulerException {
|
||||
return getOneTriggerKey()
|
||||
.map(Try.with(triggerKey -> scheduler.getTrigger(triggerKey)))
|
||||
.filter(Try::isSuccess).map(Try::getSuccess)
|
||||
.filter(trigger -> trigger instanceof SimpleTrigger)
|
||||
.map(trigger -> (SimpleTrigger) trigger);
|
||||
}
|
||||
|
||||
public Optional<TriggerKey> getOneTriggerKey() throws SchedulerException {
|
||||
return scheduler.getTriggerKeys(GroupMatcher.anyGroup()).stream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public TriggerDTO getTriggerByName(String name) throws SchedulerException {
|
||||
Trigger trigger = scheduler.getTrigger(new TriggerKey(name));
|
||||
return conversionService.convert(trigger, TriggerDTO.class);
|
||||
}
|
||||
|
||||
public TriggerDTO scheduleNewTrigger(String name, String jobClassname, SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException {
|
||||
Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(jobClassname);
|
||||
JobDetail jobDetail = JobBuilder.newJob()
|
||||
.ofType(jobClass)
|
||||
.storeDurably(false)
|
||||
.build();
|
||||
|
||||
int intervalInMills = SchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay());
|
||||
|
||||
Trigger newTrigger = TriggerBuilder.newTrigger()
|
||||
.withSchedule(
|
||||
SimpleScheduleBuilder.simpleSchedule()
|
||||
.withIntervalInMilliseconds(intervalInMills)
|
||||
.withRepeatCount(config.getMaxCount() - 1)
|
||||
.withMisfireHandlingInstructionNextWithRemainingCount()
|
||||
)
|
||||
.withIdentity(name)
|
||||
.build();
|
||||
|
||||
scheduler.scheduleJob(jobDetail, newTrigger);
|
||||
|
||||
return conversionService.convert(newTrigger, TriggerDTO.class);
|
||||
}
|
||||
|
||||
public TriggerDTO rescheduleTrigger(String name, SchedulerConfigParam config) throws SchedulerException {
|
||||
int intervalInMills = SchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay());
|
||||
|
||||
Optional<TriggerKey> optionalTriggerKey = getTriggerByKey(name);
|
||||
TriggerKey triggerKey = optionalTriggerKey.orElse(TriggerKey.triggerKey(name));
|
||||
Trigger trigger = scheduler.getTrigger(triggerKey);
|
||||
|
||||
Trigger newTrigger = TriggerBuilder.newTrigger()
|
||||
.withSchedule(
|
||||
SimpleScheduleBuilder.simpleSchedule()
|
||||
.withIntervalInMilliseconds(intervalInMills)
|
||||
.withRepeatCount(config.getMaxCount() - 1)
|
||||
.withMisfireHandlingInstructionNextWithRemainingCount()
|
||||
)
|
||||
.forJob(trigger.getJobKey().getName())
|
||||
.withIdentity(name)
|
||||
.build();
|
||||
|
||||
scheduler.rescheduleJob(triggerKey, newTrigger);
|
||||
|
||||
return conversionService.convert(newTrigger, TriggerDTO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package it.fabioformosa.quartzmanager;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
public class QuartManagerApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import it.fabioformosa.quartzmanager.controllers.utils.TestUtils;
|
||||
import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import it.fabioformosa.quartzmanager.services.SchedulerService;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
|
||||
@Disabled
|
||||
@WebMvcTest(controllers = TriggerController.class, properties = {
|
||||
"quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob"
|
||||
})
|
||||
class TriggerControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@AfterEach
|
||||
void cleanUp(){
|
||||
Mockito.reset(schedulerService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenASchedulerConfigParam_whenPosted_thenANewTriggerIsCreated() throws Exception {
|
||||
TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance();
|
||||
Mockito.when(schedulerService.scheduleNewTrigger(any(), any(), any())).thenReturn(expectedTriggerDTO);
|
||||
|
||||
SchedulerConfigParam configParamToPost = SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build();
|
||||
mockMvc.perform(
|
||||
post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "mytrigger")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(TestUtils.toJson(configParamToPost))
|
||||
)
|
||||
.andExpect(MockMvcResultMatchers.status().isCreated())
|
||||
.andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO)))
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package it.fabioformosa.quartzmanager.controllers.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
static public ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@SneakyThrows
|
||||
static public String toJson(Object object){
|
||||
return objectMapper.writeValueAsString(object);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package it.fabioformosa.quartzmanager.controllers.utils;
|
||||
|
||||
import it.fabioformosa.quartzmanager.common.utils.DateUtils;
|
||||
import it.fabioformosa.quartzmanager.dto.JobKeyDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class TriggerUtils {
|
||||
|
||||
static public TriggerDTO getTriggerInstance(){
|
||||
return TriggerDTO.builder()
|
||||
.description("sample trigger")
|
||||
.endTime(DateUtils.getHoursFromNow(2L))
|
||||
.finalFireTime(DateUtils.getHoursFromNow(2L))
|
||||
.jobKeyDTO(JobKeyDTO.builder()
|
||||
.group("defaultJobGroup")
|
||||
.name("sampleJob")
|
||||
.build())
|
||||
.mayFireAgain(true)
|
||||
.triggerKeyDTO(TriggerKeyDTO.builder()
|
||||
.group("defaultTriggerGroup")
|
||||
.name("sampleTrigger")
|
||||
.build())
|
||||
.misfireInstruction(1)
|
||||
.nextFireTime(DateUtils.getHoursFromNow(1L))
|
||||
.priority(1)
|
||||
.startTime(DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now()))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-starter-persistence</artifactId>
|
||||
|
||||
<name>Quartz Manager Starter Security</name>
|
||||
<description>Persist quartz jobs into a database</description>
|
||||
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,69 @@
|
||||
package it.fabioformosa.quartzmanager.persistence;
|
||||
|
||||
import it.fabioformosa.quartzmanager.common.properties.QuartzModuleProperties;
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.*;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
@PropertySource("classpath:quartz-persistence.properties")
|
||||
public class PersistenceConfig {
|
||||
|
||||
@Value("${quartz-manager.persistence.quartz.datasource.url}")
|
||||
private String quartzDatasourceUrl;
|
||||
|
||||
@Value("${quartz-manager.persistence.quartz.datasource.user}")
|
||||
private String quartzDatasourceUser;
|
||||
|
||||
@Value("${quartz-manager.persistence.quartz.datasource.password}")
|
||||
private String quartzDatasourcePassword;
|
||||
|
||||
@Data
|
||||
public class PersistenceDatasourceProps {
|
||||
private String changeLog;
|
||||
private String contexts;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringLiquibase liquibase(PersistenceDatasourceProps persistenceDatasourceProps, DataSource quartzManagerDatasource) {
|
||||
SpringLiquibase liquibase = new SpringLiquibase();
|
||||
liquibase.setContexts(persistenceDatasourceProps.getContexts());
|
||||
liquibase.setChangeLog(persistenceDatasourceProps.getChangeLog());
|
||||
liquibase.setDataSource(quartzManagerDatasource);
|
||||
liquibase.setDropFirst(false);
|
||||
return liquibase;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.liquibase")
|
||||
public PersistenceDatasourceProps persistenceDatasourceProps() {
|
||||
return new PersistenceDatasourceProps();
|
||||
}
|
||||
|
||||
@Bean("quartzPersistenceProperties")
|
||||
public QuartzModuleProperties persistenceQuartzProps(QuartzPersistencePropConfig quartzPersistencePropConfig) {
|
||||
QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties();
|
||||
quartzModuleProperties.setProperties(quartzPersistencePropConfig.getProperties());
|
||||
quartzModuleProperties.getProperties().setProperty("org.quartz.dataSource.quartzDataSource.URL", quartzDatasourceUrl);
|
||||
quartzModuleProperties.getProperties().setProperty("org.quartz.dataSource.quartzDataSource.user", quartzDatasourceUser);
|
||||
quartzModuleProperties.getProperties().setProperty("org.quartz.dataSource.quartzDataSource.password", quartzDatasourcePassword);
|
||||
return quartzModuleProperties;
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public DataSource quartzManagerDatasource(PersistenceDatasourceProps persistenceDatasourceProps) {
|
||||
return DataSourceBuilder.create()
|
||||
.url(quartzDatasourceUrl)
|
||||
.driverClassName("org.postgresql.Driver")
|
||||
.username(quartzDatasourceUser)
|
||||
.password(quartzDatasourcePassword)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package it.fabioformosa.quartzmanager.persistence;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@Configuration
|
||||
@PropertySource("classpath:quartz-persistence.properties")
|
||||
@ConfigurationProperties(prefix = "spring.quartz")
|
||||
@Getter @Setter
|
||||
public class QuartzPersistencePropConfig {
|
||||
private Properties properties;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
it.fabioformosa.quartzmanager.persistence.PersistenceConfig
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog
|
||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
|
||||
|
||||
<includeAll path="./migrations" relativeToChangelogFile="true"/>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,218 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
/* https://github.com/quartz-scheduler/quartz/blob/master/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore/tables_postgres.sql */
|
||||
|
||||
--changeset V202102190034_01 (dbms:postgresql)
|
||||
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
|
||||
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
|
||||
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
|
||||
DROP TABLE IF EXISTS QRTZ_LOCKS;
|
||||
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
|
||||
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
|
||||
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
|
||||
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
|
||||
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
|
||||
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
|
||||
DROP TABLE IF EXISTS QRTZ_CALENDARS;
|
||||
|
||||
--changeset V202102190034_02 (dbms:postgresql)
|
||||
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 BOOL NOT NULL,
|
||||
IS_NONCONCURRENT BOOL NOT NULL,
|
||||
IS_UPDATE_DATA BOOL NOT NULL,
|
||||
REQUESTS_RECOVERY BOOL NOT NULL,
|
||||
JOB_DATA BYTEA NULL,
|
||||
PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_03 (dbms:postgresql)
|
||||
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 BYTEA NULL,
|
||||
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
|
||||
FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
|
||||
REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_04 (dbms:postgresql)
|
||||
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,
|
||||
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
|
||||
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_05 (dbms:postgresql)
|
||||
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),
|
||||
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
|
||||
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_06 (dbms:postgresql)
|
||||
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 INT NULL,
|
||||
INT_PROP_2 INT 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 BOOL NULL,
|
||||
BOOL_PROP_2 BOOL NULL,
|
||||
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
|
||||
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_07 (dbms:postgresql)
|
||||
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 BYTEA NULL,
|
||||
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
|
||||
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_08 (dbms:postgresql)
|
||||
CREATE TABLE QRTZ_CALENDARS
|
||||
(
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
CALENDAR_NAME VARCHAR(200) NOT NULL,
|
||||
CALENDAR BYTEA NOT NULL,
|
||||
PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)
|
||||
);
|
||||
|
||||
--changeset V202102190034_09 (dbms:postgresql)
|
||||
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
|
||||
(
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
--changeset V202102190034_10 (dbms:postgresql)
|
||||
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 BOOL NULL,
|
||||
REQUESTS_RECOVERY BOOL NULL,
|
||||
PRIMARY KEY (SCHED_NAME, ENTRY_ID)
|
||||
);
|
||||
|
||||
--changeset V202102190034_11 (dbms:postgresql)
|
||||
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,
|
||||
PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)
|
||||
);
|
||||
|
||||
--changeset V202102190034_12 (dbms:postgresql)
|
||||
CREATE TABLE QRTZ_LOCKS
|
||||
(
|
||||
SCHED_NAME VARCHAR(120) NOT NULL,
|
||||
LOCK_NAME VARCHAR(40) NOT NULL,
|
||||
PRIMARY KEY (SCHED_NAME, LOCK_NAME)
|
||||
);
|
||||
|
||||
--changeset V202102190034_13 (dbms:postgresql)
|
||||
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY
|
||||
ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY);
|
||||
CREATE INDEX IDX_QRTZ_J_GRP
|
||||
ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP);
|
||||
|
||||
CREATE INDEX IDX_QRTZ_T_J
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
|
||||
CREATE INDEX IDX_QRTZ_T_JG
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP);
|
||||
CREATE INDEX IDX_QRTZ_T_C
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME);
|
||||
CREATE INDEX IDX_QRTZ_T_G
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
|
||||
CREATE INDEX IDX_QRTZ_T_STATE
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE);
|
||||
CREATE INDEX IDX_QRTZ_T_N_STATE
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);
|
||||
CREATE INDEX IDX_QRTZ_T_N_G_STATE
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);
|
||||
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME);
|
||||
CREATE INDEX IDX_QRTZ_T_NFT_ST
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);
|
||||
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);
|
||||
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);
|
||||
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP
|
||||
ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);
|
||||
|
||||
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME
|
||||
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME);
|
||||
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY
|
||||
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);
|
||||
CREATE INDEX IDX_QRTZ_FT_J_G
|
||||
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
|
||||
CREATE INDEX IDX_QRTZ_FT_JG
|
||||
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP);
|
||||
CREATE INDEX IDX_QRTZ_FT_T_G
|
||||
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);
|
||||
CREATE INDEX IDX_QRTZ_FT_TG
|
||||
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
|
||||
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,13 @@
|
||||
spring.liquibase.change-log=classpath:db/quartz-scheduler/liquibase-changelog-master.xml
|
||||
spring.liquibase.contexts=default
|
||||
spring.quartz.job-store-type=jdbc
|
||||
spring.quartz.initialize-schema=never
|
||||
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
|
||||
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.driver=org.postgresql.Driver
|
||||
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.maxConnections=5
|
||||
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
|
||||
spring.quartz.properties.org.quartz.jobStore.dataSource=quartzDataSource
|
||||
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.provider=hikaricp
|
||||
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=1000
|
||||
# org.quartz.jobStore.isClustered=true
|
||||
# org.quartz.scheduler.instanceId=AUTO
|
||||
@@ -1,144 +1,144 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-web-showcase</artifactId>
|
||||
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>Quartz Manager Web Showcase</name>
|
||||
<description>A webapp that imports Quartz Manager API lib and the frontend webjar</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<springfox.version>2.9.2</springfox.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SPRING -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MISC -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>spring-mock-mvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-web-showcase</artifactId>
|
||||
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>Quartz Manager Web Showcase</name>
|
||||
<description>A webapp that imports Quartz Manager API lib and the frontend webjar</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<springfox.version>2.9.2</springfox.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-persistence</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SPRING -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MISC -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>spring-mock-mvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
|
||||
@@ -8,10 +8,9 @@ import it.fabioformosa.quartzmanager.jobs.entities.LogRecord.LogType;
|
||||
|
||||
|
||||
public class SampleJob extends AbstractLoggingJob {
|
||||
|
||||
@Override
|
||||
public LogRecord doIt(JobExecutionContext jobExecutionContext) {
|
||||
return new LogRecord(LogType.INFO, "Hello!");
|
||||
return new LogRecord(LogType.INFO, "Hello!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,8 +26,15 @@ logging:
|
||||
org.springframework.security: INFO
|
||||
org.springframework.boot.autoconfigure.security: INFO
|
||||
it.fabioformosa: DEBUG
|
||||
org.quartz: INFO
|
||||
|
||||
quartz-manager:
|
||||
persistence:
|
||||
quartz:
|
||||
datasource:
|
||||
url: "jdbc:postgresql://localhost:5432/quartzmanager"
|
||||
user: "quartzmanager"
|
||||
password: "quartzmanager"
|
||||
security:
|
||||
login-model:
|
||||
form-login-enabled: true
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<logger name="it.fabioformosa" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
<logger name="it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenAuthenticationFilter" level="WARN" additivity="false">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
<logger name="org.springframework" level="WARN" additivity="false">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
@@ -19,8 +22,8 @@
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
<root level="WARN">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
org.quartz.scheduler.instanceName=example
|
||||
org.quartz.scheduler.instanceId=AUTO
|
||||
org.quartz.threadPool.threadCount=1
|
||||
org.quartz.threadPool.threadCount=1
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
import it.fabioformosa.quartzmanager.controllers.utils.TestUtils;
|
||||
import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils;
|
||||
import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import it.fabioformosa.quartzmanager.services.SchedulerService;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
|
||||
@WebMvcTest(controllers = TriggerController.class, properties = {
|
||||
"quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob"
|
||||
})
|
||||
class TriggerControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@AfterEach
|
||||
void cleanUp(){
|
||||
Mockito.reset(schedulerService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenASchedulerConfigParam_whenPosted_thenANewTriggerIsCreated() throws Exception {
|
||||
TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance();
|
||||
Mockito.when(schedulerService.scheduleNewTrigger(any(), any(), any())).thenReturn(expectedTriggerDTO);
|
||||
|
||||
SchedulerConfigParam configParamToPost = SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build();
|
||||
mockMvc.perform(
|
||||
post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "mytrigger")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(TestUtils.toJson(configParamToPost))
|
||||
)
|
||||
.andExpect(MockMvcResultMatchers.status().isCreated())
|
||||
.andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO)))
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package it.fabioformosa.quartzmanager.controllers.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
static public ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@SneakyThrows
|
||||
static public String toJson(Object object){
|
||||
return objectMapper.writeValueAsString(object);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package it.fabioformosa.quartzmanager.controllers.utils;
|
||||
|
||||
import it.fabioformosa.quartzmanager.common.utils.DateUtils;
|
||||
import it.fabioformosa.quartzmanager.dto.JobKeyDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
|
||||
import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class TriggerUtils {
|
||||
|
||||
static public TriggerDTO getTriggerInstance(){
|
||||
return TriggerDTO.builder()
|
||||
.description("sample trigger")
|
||||
.endTime(DateUtils.getHoursFromNow(2L))
|
||||
.finalFireTime(DateUtils.getHoursFromNow(2L))
|
||||
.jobKeyDTO(JobKeyDTO.builder()
|
||||
.group("defaultJobGroup")
|
||||
.name("sampleJob")
|
||||
.build())
|
||||
.mayFireAgain(true)
|
||||
.triggerKeyDTO(TriggerKeyDTO.builder()
|
||||
.group("defaultTriggerGroup")
|
||||
.name("sampleTrigger")
|
||||
.build())
|
||||
.misfireInstruction(1)
|
||||
.nextFireTime(DateUtils.getHoursFromNow(1L))
|
||||
.priority(1)
|
||||
.startTime(DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now()))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user