mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2026-05-15 06:10:29 +09:00
Compare commits
40 Commits
quartz-man
...
3.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69e2ab3977 | ||
|
|
27fab8acca | ||
|
|
734bb1b087 | ||
|
|
81d9e92450 | ||
|
|
5d794536e3 | ||
|
|
4f3efc50fc | ||
|
|
37ad22090c | ||
|
|
95fa102720 | ||
|
|
f37ad1ae58 | ||
|
|
fb2d8da53d | ||
|
|
87ee4bebb3 | ||
|
|
9bf0871ff6 | ||
|
|
70827393b7 | ||
|
|
c4e8eb94d6 | ||
|
|
e50a48bd4c | ||
|
|
10df1116bd | ||
|
|
03f45346a7 | ||
|
|
6170e8f1ae | ||
|
|
0a718e897b | ||
|
|
d243c3a8e8 | ||
|
|
49020d9bd0 | ||
|
|
1457f3fdea | ||
|
|
42c63b963f | ||
|
|
aecc1cab68 | ||
|
|
4e63e0a833 | ||
|
|
4540165157 | ||
|
|
a56f5284d1 | ||
|
|
eb675f59a3 | ||
|
|
35f8b6b52a | ||
|
|
6c76f0a067 | ||
|
|
48b626c08e | ||
|
|
b2f796cf73 | ||
|
|
b01f4b99e4 | ||
|
|
d11089c451 | ||
|
|
8ecdb4f5a6 | ||
|
|
86c2e86a12 | ||
|
|
2b6dadd5f1 | ||
|
|
bce1be698c | ||
|
|
b7d152a42a | ||
|
|
25a5e808f2 |
3
.travis.yml
Normal file
3
.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
language: java
|
||||
jdk: openjdk8
|
||||
before_script: cd quartz-manager-parent
|
||||
8
CHANGELOG.md
Normal file
8
CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## **v3.0.1**
|
||||
|
||||
Quartz-Manager is now publicly available into the maven central repo into 3 different packages.
|
||||
You can import:
|
||||
|
||||
* `quartz-manager-starter-api` to have a REST API layer to control your scheduler
|
||||
* `quartz-manager-starter-ui` to import the UI also, in your spring webapp.
|
||||
* `quartz-manager-starter-security` if you want to give access to the quartz-manager UI and API only to authenticated users
|
||||
177
README.md
177
README.md
@@ -1,21 +1,154 @@
|
||||
[](https://travis-ci.org/fabioformosa/quartz-manager)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api)
|
||||
[](https://gitter.im/quartz-manager/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
# QUARTZ MANAGER
|
||||
UI Manager for Quartz Scheduler.
|
||||
Quartz Manager is a library you can import in your spring webapp to easily enable the [Quartz Scheduler](http://www.quartz-scheduler.org/) and to control it by REST APIs or by a UI Manager Panel (angular-based).
|
||||
|
||||
Through this webapp you can launch and control your scheduled job. The UI Console is composed by a management panel to set trigger, start/stop scheduler and a log panel with a progress bar to display the job output.
|
||||
Your Spring Webapp should provide the java class of the job you want schedule. Importing the Quartz Manager your project will have the REST API and (optionally) the UI to launch and control the job.
|
||||
The UI Dashboard is composed by a management panel to set the quartz trigger, to start/stop the scheduler and a log panel with a progress bar to display the job output.
|
||||
|
||||

|
||||

|
||||
|
||||
## HOW IT WORKS
|
||||
* Set up the trigger into the left sidebar in terms of: daily frequency and and max occurrences.
|
||||
* Press the start button
|
||||
* The GUI manager updates the progress bar and reports all logs of your quartz job.
|
||||
* The GUI manager updates in real time the progress bar and reports all logs of your quartz job.
|
||||
|
||||
## ROADMAP
|
||||
Open the [Project Roadmap](https://github.com/fabioformosa/quartz-manager/projects) to take a look at the plan of Quartz Manager.
|
||||
Currently this project might be useful to look how to import Quartz Library in a spring boot application. For this purpose, browse the folder `quartz-manager-parent/quartz-manager-api`.
|
||||
We're just working to create a library, from project `quartz-manager-parent/quartz-manager-api`, to be imported in your spring boot where you have your job to be scheduled.
|
||||
## QUICK START
|
||||
|
||||
* **Requirements**
|
||||
Java 8+
|
||||
|
||||
* **add the dependency**
|
||||
|
||||
MAVEN
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OPTIONALLY -->
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
GRADLE
|
||||
|
||||
```
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-api', version: '3.0.1'
|
||||
|
||||
//optionally
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-ui', version: '3.0.1'
|
||||
|
||||
```
|
||||
Import `quartz-manager-starter-ui` as well, if you want to use the Quartz Manager API by the angular frontend.
|
||||
|
||||
* **add a `quartz.properties` file in the classpath (`src/main/resources`)**
|
||||
|
||||
```
|
||||
org.quartz.scheduler.instanceName=example
|
||||
org.quartz.scheduler.instanceId=AUTO
|
||||
org.quartz.threadPool.threadCount=1
|
||||
```
|
||||
`quartz.properties` is a configuration file required by [Quartz Scheduler](http://www.quartz-scheduler.org/). For further details, click [here](http://www.quartz-scheduler.org/documentation/quartz-2.3.0/configuration/).
|
||||
|
||||
* **Create the job class that you want to schedule**
|
||||
|
||||
```
|
||||
public class SampleJob extends AbstractLoggingJob {
|
||||
|
||||
@Override
|
||||
public LogRecord doIt(JobExecutionContext jobExecutionContext) {
|
||||
return new LogRecord(LogType.INFO, "Hello from QuartManagerDemo!");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Extend the super-class `AbstractLoggingJob`
|
||||
|
||||
|
||||
* **Enable quartz-manager adding into the application.yml**
|
||||
|
||||
```
|
||||
quartz:
|
||||
enabled: true
|
||||
|
||||
job:
|
||||
frequency: 4000
|
||||
repeatCount: 19
|
||||
|
||||
quartz-manager:
|
||||
jobClass: <QUALIFIED NAME OF THE YOUR JOB CLASS>
|
||||
```
|
||||
|
||||
* **REST API**
|
||||
You can access the REST API, through the swagger-ui. Open the URL:
|
||||
[http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html)
|
||||
|
||||
(Change the port and the contextPath accordingly with the setup of your webapp)
|
||||
|
||||
* **Frontend**
|
||||
If you've imported the `quartz-manager-starter-ui` you can open the UI at URL:
|
||||
[http://localhost:8080/quartz-manager-ui/index.html](http://localhost:8080/quartz-manager-ui/index.html)
|
||||
|
||||
(Change the port and the contextPath accordingly with the setup of your webapp)
|
||||
|
||||
* **Security**
|
||||
If you want enable a security layer and allow the access to the REST API and to the UI only to authenticated users, add the dependency:
|
||||
|
||||
MAVEN
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
GRADLE
|
||||
|
||||
```
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-security', version: '3.0.1'
|
||||
```
|
||||
|
||||
and in your application.yml:
|
||||
|
||||
```
|
||||
quartz-manager:
|
||||
security:
|
||||
login-model:
|
||||
form-login-enabled: true
|
||||
userpwd-filter-enabled : false
|
||||
jwt:
|
||||
enabled: true
|
||||
secret: "PLEASE_TYPE_HERE_A_SECRET"
|
||||
expiration-in-sec: 28800 # 8 hours
|
||||
header-strategy:
|
||||
enabled: false
|
||||
header: "Authorization"
|
||||
cookie-strategy:
|
||||
enabled: true
|
||||
cookie: AUTH-TOKEN
|
||||
accounts:
|
||||
in-memory:
|
||||
enabled: true
|
||||
users:
|
||||
- name: admin
|
||||
password: admin
|
||||
roles:
|
||||
- ADMIN
|
||||
|
||||
```
|
||||
|
||||
* **DEMO**
|
||||
|
||||
Take a loot to the project [Quartz-Manager Demo](https://github.com/fabioformosa/quartz-manager-demo), it is an example of how-to:
|
||||
* import the quartz-manager-api library in your webapp
|
||||
@@ -23,8 +156,9 @@ Take a loot to the project [Quartz-Manager Demo](https://github.com/fabioformosa
|
||||
* set properties into the application.yml
|
||||
* add a secure layer to allow the API only to logged users
|
||||
* schedule a custom job (a dummy `hello world`)
|
||||
|
||||
**NB: In few days, we'll release the library jar of quartz-manager into the maven central repo.**
|
||||
|
||||
## ROADMAP
|
||||
Open the [Project Roadmap](https://github.com/fabioformosa/quartz-manager/projects) to take a look at the plan of Quartz Manager.
|
||||
|
||||
Next steps in the roadmap are:
|
||||
* to add a persistent layer to save all job setup.
|
||||
@@ -32,14 +166,17 @@ Next steps in the roadmap are:
|
||||
* to add CI/CD pipeline to ease the deploy pulling a docker container.
|
||||
* Enabling adapters for integrations: kafka, etc.
|
||||
|
||||
## PROJECT STRUCTURE
|
||||
* **quartz-parent/quartz-manager-api** is the library that can be imported in webapp to have the quartz-manager API.
|
||||
* **quartz-parent/quartz-manager-webjar** is a maven module to build and package the angular frontend in a webjar.
|
||||
* **quartz-parent/quartz-manager-security** is ther library that can be imported in a webapp to have a security layer (login) over the quartz-manager API.
|
||||
* **quartz-parent/quartz-manager-web-showcase** is an example of webapp that imports quartz-manager-api. Useful to develop the frontend started locally with the webpack dev server.
|
||||
* **quartz-frontend** is the angular app that interacts with the Quartz Manager API.
|
||||
|
||||
## HOW-TO CONTRIBUTE
|
||||
## HOW-TO CONTRIBUTE
|
||||
|
||||
### PROJECT STRUCTURE
|
||||
* `quartz-parent/quartz-manager-starter-api` is the library that can be imported in webapp to have the quartz-manager API.
|
||||
* `quartz-parent/quartz-manager-starter-ui` is a maven module to build and package the angular frontend in a webjar.
|
||||
* `quartz-parent/quartz-manager-starter-security` is ther library that can be imported in a webapp to have a security layer (login) over the quartz-manager API.
|
||||
* `quartz-parent/quartz-manager-web-showcase` is an example of webapp that imports quartz-manager-api. Useful to develop the frontend started locally with the webpack dev server.
|
||||
* `quartz-frontend` is the angular app that interacts with the Quartz Manager API.
|
||||
|
||||
### PROJECT DETAILS
|
||||
**[requirements]** Make sure you have installed
|
||||
* [Java 8](https://java.com/download/) or greater
|
||||
* [Maven](https://maven.apache.org/)
|
||||
@@ -52,7 +189,9 @@ To build&run quartz-manager in your machine:
|
||||
git clone https://github.com/fabioformosa/quartz-manager.git
|
||||
|
||||
# START QUARTZ-MANAGER-WEB
|
||||
cd quartz-manager/quartz-parent/quartz-manager-web
|
||||
cd quartz-manager/quartz-parent
|
||||
mvn install
|
||||
cd quartz-manager/quartz-parent/quartz-manager-web-showcase
|
||||
mvn spring-boot:run
|
||||
|
||||
# START QUARTZ-MANAGER-FRONTEND
|
||||
@@ -69,7 +208,7 @@ If you are not confident with maven CLI, you can start it by your IDE. For more
|
||||
|
||||
|
||||
## HOW TO RUN YOUR SCHEDULED JOB
|
||||
By default, quartz-manager-web executes the dummy job that logs "hello world!".
|
||||
By default, `quartz-manager-web-showcase` executes the dummy job that logs "hello world!".
|
||||
Replace the dummy job (class: `it.fabioformosa.quartzmanager.jobs.SampleJob`) with yours. Follow these steps:
|
||||
|
||||
1. Extend the super class `it.fabioformosa.quartzmanager.jobs.AbstractLoggingJob`
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@fortawesome/fontawesome-free-regular": "^5.0.8",
|
||||
"@fortawesome/fontawesome-free-solid": "^5.0.8",
|
||||
"@stomp/ng2-stompjs": "^0.6.3",
|
||||
"@types/jest": "^27.0.2",
|
||||
"core-js": "2.5.1",
|
||||
"hammerjs": "2.0.8",
|
||||
"net": "^1.0.2",
|
||||
@@ -65,6 +66,8 @@
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-preset-angular",
|
||||
"setupFilesAfterEnv": ["<rootDir>/jest.setup.ts"]
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/jest.setup.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,36 +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 EXISTING COUNT</div>
|
||||
<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 left unchanged (missed events are definitively lost).
|
||||
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 the end-time of the trigger has arrived,
|
||||
so this misfire instruction doesn't guarantee that the repeat counter reaches your max value, but it guarantees that the end-time doesn't go over the expected final fire time.
|
||||
<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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { TestBed, async, inject } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { NO_AUTH, UserService } from '../services';
|
||||
import { AdminGuard } from './admin.guard';
|
||||
import {jest} from '@jest/globals'
|
||||
|
||||
export class RouterStub {
|
||||
navigate(commands?: any[], extras?: any) {}
|
||||
}
|
||||
|
||||
const RouterSpy = jest.spyOn(RouterStub.prototype, 'navigate');
|
||||
|
||||
const MockUserServiceNoAuth = jest.fn(() => ({currentUser: NO_AUTH}));
|
||||
const MockUserService = jest.fn(() => ({
|
||||
currentUser: {
|
||||
authorities: ['ROLE_ADMIN']
|
||||
}
|
||||
}));
|
||||
const MockUserServiceForbidden = jest.fn(() => ({
|
||||
currentUser: {
|
||||
authorities: ['ROLE_GUEST']
|
||||
}
|
||||
}));
|
||||
|
||||
// describe('AdminGuard NoAuth', () => {
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({
|
||||
// providers: [
|
||||
// AdminGuard,
|
||||
// {
|
||||
// provide: Router,
|
||||
// useClass: RouterStub
|
||||
// },
|
||||
// {
|
||||
// provide: UserService,
|
||||
// useClass: MockUserServiceNoAuth
|
||||
// }
|
||||
// ]
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// test.skip('should run', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
// expect(guard).toBeTruthy();
|
||||
// }));
|
||||
//
|
||||
// test.skip('returns true if user is NO_AUTH', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
// expect(guard.canActivate(null, null)).toBeTruthy();
|
||||
// }));
|
||||
//
|
||||
// });
|
||||
|
||||
// describe('AdminGuard activates the route', () => {
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({
|
||||
// providers: [
|
||||
// AdminGuard,
|
||||
// {
|
||||
// provide: Router,
|
||||
// useClass: RouterStub
|
||||
// },
|
||||
// {
|
||||
// provide: UserService,
|
||||
// useClass: MockUserService
|
||||
// }
|
||||
// ]
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// test.skip('should run', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
// expect(guard).toBeTruthy();
|
||||
// }));
|
||||
//
|
||||
// test.skip('returns true if user has admin role', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
// expect(guard.canActivate(null, null)).toBeTruthy();
|
||||
// }));
|
||||
//
|
||||
// });
|
||||
|
||||
// describe('AdminGuard redirects to 403', () => {
|
||||
// beforeEach(() => {
|
||||
// TestBed.configureTestingModule({
|
||||
// providers: [
|
||||
// AdminGuard,
|
||||
// {
|
||||
// provide: Router,
|
||||
// useClass: RouterStub
|
||||
// },
|
||||
// {
|
||||
// provide: UserService,
|
||||
// useClass: MockUserServiceForbidden
|
||||
// }
|
||||
// ]
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// test.skip('should run', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
// expect(guard).toBeTruthy();
|
||||
// }));
|
||||
//
|
||||
// test.skip('returns false if user is not authorized', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
// expect(guard.canActivate(null, null)).toBeFalsy();
|
||||
// expect(RouterSpy).toHaveBeenCalledTimes(1);
|
||||
// }));
|
||||
//
|
||||
// });
|
||||
@@ -1,105 +0,0 @@
|
||||
import { TestBed, async, inject } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { NO_AUTH, UserService } from '../services';
|
||||
import { AdminGuard } from './admin.guard';
|
||||
import {jest} from '@jest/globals'
|
||||
|
||||
export class RouterStub {
|
||||
navigate(commands?: any[], extras?: any) {}
|
||||
}
|
||||
|
||||
const RouterSpy = jest.spyOn(RouterStub.prototype, 'navigate');
|
||||
|
||||
const MockUserServiceNoAuth = jest.fn(() => ({currentUser: NO_AUTH}));
|
||||
const MockUserService = jest.fn(() => ({
|
||||
currentUser: {
|
||||
authorities: ['ROLE_ADMIN']
|
||||
}
|
||||
}));
|
||||
const MockUserServiceForbidden = jest.fn(() => ({
|
||||
currentUser: {
|
||||
authorities: ['ROLE_GUEST']
|
||||
}
|
||||
}));
|
||||
|
||||
describe('AdminGuard NoAuth', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
AdminGuard,
|
||||
{
|
||||
provide: Router,
|
||||
useClass: RouterStub
|
||||
},
|
||||
{
|
||||
provide: UserService,
|
||||
useClass: MockUserServiceNoAuth
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should run', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
expect(guard).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('returns true if user is NO_AUTH',inject([AdminGuard], (guard: AdminGuard) => {
|
||||
expect(guard.canActivate(null, null)).toBeTruthy();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('AdminGuard activates the route', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
AdminGuard,
|
||||
{
|
||||
provide: Router,
|
||||
useClass: RouterStub
|
||||
},
|
||||
{
|
||||
provide: UserService,
|
||||
useClass: MockUserService
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should run', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
expect(guard).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('returns true if user has admin role',inject([AdminGuard], (guard: AdminGuard) => {
|
||||
expect(guard.canActivate(null, null)).toBeTruthy();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('AdminGuard redirects to 403', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
AdminGuard,
|
||||
{
|
||||
provide: Router,
|
||||
useClass: RouterStub
|
||||
},
|
||||
{
|
||||
provide: UserService,
|
||||
useClass: MockUserServiceForbidden
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should run', inject([AdminGuard], (guard: AdminGuard) => {
|
||||
expect(guard).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('returns false if user is not authorized',inject([AdminGuard], (guard: AdminGuard) => {
|
||||
expect(guard.canActivate(null, null)).toBeFalsy();
|
||||
expect(RouterSpy).toHaveBeenCalledTimes(1);
|
||||
}));
|
||||
|
||||
});
|
||||
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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<connection>scm:git:git://github.com/fabioformosa/quartz-manager.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:fabioformosa/quartz-manager.git</developerConnection>
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
<tag>quartz-manager-parent-3.0.0</tag>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<developers>
|
||||
@@ -40,28 +40,40 @@
|
||||
</developers>
|
||||
|
||||
<modules>
|
||||
<module>quartz-manager-api</module>
|
||||
<module>quartz-manager-ui-webjar</module>
|
||||
<module>quartz-manager-security</module>
|
||||
<module>quartz-manager-starter-api</module>
|
||||
<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>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-api</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<artifactId>quartz-manager-common</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-security</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-ui-webjar</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<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>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -83,7 +95,6 @@
|
||||
<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>
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
<?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.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-api</artifactId>
|
||||
|
||||
<name>Quartz Manager API</name>
|
||||
<description>A library to manage your scheduled job by API</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>
|
||||
@@ -1,154 +0,0 @@
|
||||
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.TriggerStatus;
|
||||
import it.fabioformosa.quartzmanager.enums.SchedulerStates;
|
||||
import it.fabioformosa.quartzmanager.scheduler.TriggerMonitor;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@Resource
|
||||
private Scheduler scheduler;
|
||||
|
||||
@Resource
|
||||
private TriggerMonitor triggerMonitor;
|
||||
|
||||
private long fromMillsIntervalToTriggerPerDay(long repeatIntervalInMills) {
|
||||
return (int) Math.ceil(MILLS_IN_A_DAY / repeatIntervalInMills);
|
||||
}
|
||||
|
||||
private int fromTriggerPerDayToMillsInterval(long triggerPerDay) {
|
||||
return (int) Math.ceil(Long.valueOf(MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private int fromTriggerPerDayToSecInterval(long triggerPerDay) {
|
||||
return (int) Math.ceil(Long.valueOf(SEC_IN_A_DAY) / triggerPerDay);
|
||||
}
|
||||
|
||||
@GetMapping("/config")
|
||||
public SchedulerConfigParam getConfig() {
|
||||
log.debug("SCHEDULER - GET CONFIG params");
|
||||
SimpleTrigger simpleTrigger = (SimpleTrigger) triggerMonitor.getTrigger();
|
||||
|
||||
int maxCount = simpleTrigger.getRepeatCount() + 1;
|
||||
long triggersPerDay = fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval());
|
||||
|
||||
return new SchedulerConfigParam(triggersPerDay, maxCount);
|
||||
}
|
||||
|
||||
@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)).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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
142
quartz-manager-parent/quartz-manager-starter-api/pom.xml
Normal file
142
quartz-manager-parent/quartz-manager-starter-api/pom.xml
Normal file
@@ -0,0 +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>
|
||||
<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,11 +15,9 @@ 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
|
||||
@@ -37,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(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT);// 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);
|
||||
@@ -81,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package it.fabioformosa.quartzmanager.controllers;
|
||||
|
||||
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.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 final Logger log = LoggerFactory.getLogger(SchedulerController.class);
|
||||
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
public SchedulerController(SchedulerService schedulerService, ConversionService conversionService) {
|
||||
this.schedulerService = schedulerService;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Resource
|
||||
private ConversionService conversionService;
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
@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("/pause")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void pause() throws SchedulerException {
|
||||
log.info("SCHEDULER - PAUSE COMMAND");
|
||||
schedulerService.getScheduler().standby();
|
||||
}
|
||||
|
||||
@GetMapping("/resume")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void resume() throws SchedulerException {
|
||||
log.info("SCHEDULER - RESUME COMMAND");
|
||||
schedulerService.getScheduler().start();
|
||||
}
|
||||
|
||||
@GetMapping("/run")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void run() throws SchedulerException {
|
||||
log.info("SCHEDULER - START COMMAND");
|
||||
schedulerService.getScheduler().start();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package it.fabioformosa.quartzmanager.dto;
|
||||
|
||||
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 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
|
||||
@@ -3,13 +3,13 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-security</artifactId>
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
|
||||
<name>Quartz Manager Security</name>
|
||||
<description>Security Layer for Quartz Manager</description>
|
||||
<name>Quartz Manager Starter Security</name>
|
||||
<description>Security Layer for Quartz Manager. Import it in your spring webapp</description>
|
||||
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
<properties>
|
||||
@@ -4,13 +4,13 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-ui-webjar</artifactId>
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
|
||||
<name>Quartz Manager UI webjar</name>
|
||||
<description>webjar builder for quartz-manager frontend</description>
|
||||
<description>Webjar to import the quartz-manager frontend in your spring webapp</description>
|
||||
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
<properties>
|
||||
@@ -1,144 +1,151 @@
|
||||
<?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.0</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-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-ui-webjar</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-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>
|
||||
|
||||
<!-- TEST -->
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</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
|
||||
|
||||
Reference in New Issue
Block a user