mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2026-05-15 14:20:30 +09:00
Compare commits
47 Commits
| 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 | ||
|
|
54662a1b8a | ||
|
|
6cdee3ba5d | ||
|
|
c6df1e2093 | ||
|
|
663b3f4e09 | ||
|
|
2b6dadd5f1 | ||
|
|
bce1be698c | ||
|
|
c29af8c593 | ||
|
|
a7a86f960a | ||
|
|
56c81caf05 | ||
|
|
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
|
||||
191
README.md
191
README.md
@@ -1,39 +1,182 @@
|
||||
[](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.
|
||||
|
||||
## 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
|
||||
* include the quartz-manager frontend (angular based) through a webjar
|
||||
* 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`)
|
||||
|
||||
## 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.
|
||||
The project `quartz-manager-parent/quartz-manager-web` is an example of how-to:
|
||||
* import the library
|
||||
* set the application.yml
|
||||
* add secure layer
|
||||
* schedule a custom job (a dummy `hello world`)
|
||||
|
||||
## 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-web** is an example of webapp that imports quartz-manager-api. It adds a secure layer and a custom job to be scheduled.
|
||||
* **quartz-frontend** is the angular app that interacts with the Quartz Manager API.
|
||||
|
||||
Next steps in the roadmap are:
|
||||
* to simplify the customization of the job through plugins
|
||||
* to add CI/CD pipeline to ease the deploy pulling a docker container
|
||||
* to add a complete setup UI panel for quartz, in term of cronjobs and multiple jobs
|
||||
* to add a persistent layer to save all job logs.
|
||||
* to add a persistent layer to save all job setup.
|
||||
* to add a complete setup UI panel for quartz, in term of cronjobs and multiple jobs.
|
||||
* to add CI/CD pipeline to ease the deploy pulling a docker container.
|
||||
* Enabling adapters for integrations: kafka, etc.
|
||||
|
||||
## QUICK START
|
||||
|
||||
## 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/)
|
||||
@@ -46,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
|
||||
@@ -63,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
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -11,35 +9,187 @@
|
||||
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>2.2.2-SNAPSHOT</version>
|
||||
<version>3.0.2-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
||||
<name>Quartz Manager</name>
|
||||
<description>API and UI Manager for Quartz Scheduler</description>
|
||||
|
||||
<url>https://github.com/fabioformosa/quartz-manager</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License 2.0</name>
|
||||
<url>https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<scm>
|
||||
<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>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Fabio Formosa</name>
|
||||
<url>https://github.com/fabioformosa</url>
|
||||
</developer>
|
||||
</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>2.2.2-SNAPSHOT</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>2.2.2-SNAPSHOT</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>2.2.2-SNAPSHOT</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>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/
|
||||
</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<configuration>
|
||||
<preparationGoals>clean</preparationGoals>
|
||||
<releaseProfiles>build-webjar</releaseProfiles>
|
||||
<localCheckout>true</localCheckout>
|
||||
<pushChanges>false</pushChanges>
|
||||
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||
<arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.scm</groupId>
|
||||
<artifactId>maven-scm-provider-gitexe</artifactId>
|
||||
<version>1.9.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.7</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<doclint>none</doclint>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<!-- GPG Signature on release -->
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -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>2.2.2-SNAPSHOT</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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.controllers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper;
|
||||
import it.fabioformosa.quartzmanager.security.models.UserTokenState;
|
||||
import it.fabioformosa.quartzmanager.security.services.impl.CustomUserDetailsService;
|
||||
|
||||
/**
|
||||
* JWT Temporary disabled
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
|
||||
//@RestController
|
||||
//@RequestMapping( value = "/api", produces = MediaType.APPLICATION_JSON_VALUE )
|
||||
public class AuthenticationController {
|
||||
|
||||
static class PasswordChanger {
|
||||
public String oldPassword;
|
||||
public String newPassword;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private CustomUserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
JwtTokenHelper tokenHelper;
|
||||
|
||||
@Value("${quartz-manager.security.jwt.expiration-in-sec}")
|
||||
private int EXPIRES_IN_SEC;
|
||||
|
||||
@Value("${quartz-manager.security.jwt.cookie-strategy-cookie}")
|
||||
private String TOKEN_COOKIE;
|
||||
|
||||
@RequestMapping(value = "/changePassword", method = RequestMethod.POST)
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
public ResponseEntity<?> changePassword(@RequestBody PasswordChanger passwordChanger) {
|
||||
userDetailsService.changePassword(passwordChanger.oldPassword, passwordChanger.newPassword);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put( "result", "success" );
|
||||
return ResponseEntity.accepted().body(result);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/refresh", method = RequestMethod.GET)
|
||||
public ResponseEntity<?> refreshAuthenticationToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
String authToken = tokenHelper.retrieveToken( request );
|
||||
if (authToken != null && tokenHelper.canTokenBeRefreshed(authToken)) {
|
||||
// TODO check user password last update
|
||||
String refreshedToken = tokenHelper.refreshToken(authToken);
|
||||
|
||||
Cookie authCookie = new Cookie( TOKEN_COOKIE, refreshedToken );
|
||||
authCookie.setPath( "/quartz-manager" );
|
||||
authCookie.setHttpOnly( true );
|
||||
authCookie.setMaxAge( EXPIRES_IN_SEC );
|
||||
// Add cookie to response
|
||||
response.addCookie( authCookie );
|
||||
|
||||
UserTokenState userTokenState = new UserTokenState(refreshedToken, EXPIRES_IN_SEC);
|
||||
return ResponseEntity.ok(userTokenState);
|
||||
} else {
|
||||
UserTokenState userTokenState = new UserTokenState();
|
||||
return ResponseEntity.accepted().body(userTokenState);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
|
||||
//@Component
|
||||
//@ConditionalOnProperty(prefix = "quartz-manager.security.login-model", name = "form-login-enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
|
||||
|
||||
// @Autowired
|
||||
public AuthenticationSuccessHandler(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
|
||||
super();
|
||||
this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
|
||||
}
|
||||
|
||||
public String cookieMustBeDeletedAtLogout() {
|
||||
return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication ) throws IOException, ServletException {
|
||||
clearAuthenticationAttributes(request);
|
||||
jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ComboEntryPoint extends LoginUrlAuthenticationEntryPoint {
|
||||
|
||||
private static final String LOGIN_FORM_URL = "/login";
|
||||
|
||||
public ComboEntryPoint() {
|
||||
super(LOGIN_FORM_URL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException, ServletException {
|
||||
|
||||
if (RESTRequestMatcher.isRestRequest(request)
|
||||
|| WebsocketRequestMatcher.isWebsocketConnectionRequest(request))
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
else
|
||||
super.commence(request, response, authException);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer;
|
||||
|
||||
/**
|
||||
* It wraps the httpSecurity to provide new function as login and logout
|
||||
*
|
||||
*/
|
||||
public class QuartzManagerHttpSecurity extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
|
||||
|
||||
public static QuartzManagerHttpSecurity from(HttpSecurity httpSecurity){
|
||||
QuartzManagerHttpSecurity newInstance = new QuartzManagerHttpSecurity(httpSecurity);
|
||||
newInstance.setBuilder(httpSecurity);
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
private HttpSecurity httpSecurity;
|
||||
|
||||
private LoginConfigurer loginConfigurer;
|
||||
|
||||
private LogoutSuccess logoutSuccess;
|
||||
|
||||
public QuartzManagerHttpSecurity(HttpSecurity httpSecurity) {
|
||||
this.httpSecurity = httpSecurity;
|
||||
// applicationContext = httpSecurity.getSharedObject(ApplicationContext.class);
|
||||
}
|
||||
|
||||
public QuartzManagerHttpSecurity login(String loginPath, AuthenticationManager authenticationManager) throws Exception {
|
||||
if(loginConfigurer == null || logoutSuccess == null)
|
||||
throw new IllegalStateException("QuartzManagerHttpSecurity requires to be set loginConfigurer and logoutSuccess!");
|
||||
httpSecurity = loginConfigurer.login(loginPath, httpSecurity, authenticationManager);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public LogoutConfigurer<HttpSecurity> logout(String logoutPath) throws Exception {
|
||||
LogoutConfigurer<HttpSecurity> logoutConfigurer = httpSecurity.logout().logoutRequestMatcher(new AntPathRequestMatcher(logoutPath))
|
||||
.logoutSuccessHandler(logoutSuccess);
|
||||
String cookie = loginConfigurer.cookieMustBeDeletedAtLogout();
|
||||
if(cookie != null)
|
||||
logoutConfigurer.deleteCookies(cookie);
|
||||
return logoutConfigurer;
|
||||
}
|
||||
|
||||
public QuartzManagerHttpSecurity withLoginConfigurer(LoginConfigurer loginConfigurer, LogoutSuccess logoutSuccess) {
|
||||
this.loginConfigurer = loginConfigurer;
|
||||
this.logoutSuccess = logoutSuccess;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.web.util.matcher.ELRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
public class RESTRequestMatcher {
|
||||
|
||||
static private final Logger log = LoggerFactory.getLogger(RESTRequestMatcher.class);
|
||||
|
||||
static public RequestMatcher matcherRequestedWith = new ELRequestMatcher(
|
||||
"hasHeader('X-Requested-With','XMLHttpRequest')");
|
||||
static public RequestMatcher matcherAccept = new ELRequestMatcher(
|
||||
"hasHeader('accept','application/json, text/plain, */*')");
|
||||
|
||||
static public boolean isRestRequest(HttpServletRequest request) {
|
||||
log.trace("Detecting if it's an AJAX Request: " + request.getRequestURL() + " accept: "
|
||||
+ request.getHeader("accept") + " " + " X-Requested-With: "
|
||||
+ request.getHeader("X-Requested-With"));
|
||||
return matcherRequestedWith.matches(request) || matcherAccept.matches(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class WebsocketRequestMatcher {
|
||||
|
||||
static private final Logger log = LoggerFactory.getLogger(WebsocketRequestMatcher.class);
|
||||
|
||||
static public boolean isWebsocketConnectionRequest(HttpServletRequest request) {
|
||||
log.trace("Detecting if it's a Websocket Connection Request: " + request.getRequestURL());
|
||||
return request.getServletPath().equals("/progress/info")
|
||||
|| request.getServletPath().equals("/logs/info");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.models;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* Temporary enabled only inMemoryAuthentication
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
//@Entity
|
||||
//@Table(name="Authority")
|
||||
public class Authority implements GrantedAuthority {
|
||||
|
||||
@Id
|
||||
@Column(name="id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name="name")
|
||||
String name;
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* Temporary enabled only inMemoryAuthentication
|
||||
*
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
//@Entity
|
||||
//@Table(name = "USER")
|
||||
public class User implements UserDetails, Serializable {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
|
||||
@Column(name = "firstname")
|
||||
private String firstname;
|
||||
|
||||
@Column(name = "lastname")
|
||||
private String lastname;
|
||||
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "user_authority",
|
||||
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id"))
|
||||
private List<Authority> authorities;
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
// We can add the below fields in the users table.
|
||||
// For now, they are hardcoded.
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setAuthorities(List<Authority> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
public void setFirstname(String firstname) {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setLastname(String lastname) {
|
||||
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.repositories;
|
||||
|
||||
/**
|
||||
* Temporary disabled
|
||||
*
|
||||
* @author Fabio
|
||||
*
|
||||
*/
|
||||
//public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
||||
// Authority findByName(String name);
|
||||
//}
|
||||
public interface AuthorityRepository {
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.repositories;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.models.User;
|
||||
|
||||
public interface UserRepository {
|
||||
User findByUsername( String username );
|
||||
}
|
||||
//public interface UserRepository extends JpaRepository<User, Long> {
|
||||
// User findByUsername( String username );
|
||||
//}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.services;
|
||||
|
||||
/**
|
||||
* temporary disabled
|
||||
* @author Fabio
|
||||
*
|
||||
*/
|
||||
public interface AuthorityService {
|
||||
// List<Authority> findById(Long id);
|
||||
//
|
||||
// List<Authority> findByname(String name);
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.models.User;
|
||||
import it.fabioformosa.quartzmanager.security.models.UserRequest;
|
||||
|
||||
public interface UserService {
|
||||
List<User> findAll();
|
||||
|
||||
User findById(Long id);
|
||||
|
||||
User findByUsername(String username);
|
||||
|
||||
void resetCredentials();
|
||||
|
||||
User save(UserRequest user);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.services.impl;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.services.AuthorityService;
|
||||
|
||||
/**
|
||||
* Temporary disabled
|
||||
* @author Fabio
|
||||
*
|
||||
*/
|
||||
|
||||
//@Service
|
||||
public class AuthorityServiceImpl implements AuthorityService {
|
||||
|
||||
// @Autowired
|
||||
// private AuthorityRepository authorityRepository;
|
||||
//
|
||||
// @Override
|
||||
// public List<Authority> findById(Long id) {
|
||||
// Authority auth = this.authorityRepository.getOne(id);
|
||||
// List<Authority> auths = new ArrayList<>();
|
||||
// auths.add(auth);
|
||||
// return auths;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<Authority> findByname(String name) {
|
||||
// Authority auth = this.authorityRepository.findByName(name);
|
||||
// List<Authority> auths = new ArrayList<>();
|
||||
// auths.add(auth);
|
||||
// return auths;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.services.impl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.models.User;
|
||||
import it.fabioformosa.quartzmanager.security.repositories.UserRepository;
|
||||
|
||||
/**
|
||||
* Temporary disabled
|
||||
* @author Fabio
|
||||
*
|
||||
*/
|
||||
//@Service
|
||||
public class CustomUserDetailsService implements UserDetailsService {
|
||||
|
||||
protected final Log LOGGER = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
public void changePassword(String oldPassword, String newPassword) {
|
||||
|
||||
// Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
|
||||
// String username = currentUser.getName();
|
||||
//
|
||||
// if (authenticationManager != null) {
|
||||
// LOGGER.debug("Re-authenticating user '"+ username + "' for password change request.");
|
||||
//
|
||||
// authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, oldPassword));
|
||||
// } else {
|
||||
// LOGGER.debug("No authentication manager set. can't change Password!");
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// LOGGER.debug("Changing password for user '"+ username + "'");
|
||||
//
|
||||
// User user = (User) loadUserByUsername(username);
|
||||
//
|
||||
// user.setPassword(passwordEncoder.encode(newPassword));
|
||||
// userRepository.save(user);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User user = userRepository.findByUsername(username);
|
||||
if (user == null)
|
||||
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
|
||||
else
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package it.fabioformosa.quartzmanager.security.services.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import it.fabioformosa.quartzmanager.security.models.User;
|
||||
import it.fabioformosa.quartzmanager.security.models.UserRequest;
|
||||
import it.fabioformosa.quartzmanager.security.repositories.UserRepository;
|
||||
import it.fabioformosa.quartzmanager.security.services.AuthorityService;
|
||||
import it.fabioformosa.quartzmanager.security.services.UserService;
|
||||
|
||||
/**
|
||||
* Temporary disabled
|
||||
* @author Fabio
|
||||
*
|
||||
*/
|
||||
//@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private AuthorityService authService;
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public List<User> findAll() throws AccessDeniedException {
|
||||
// List<User> result = userRepository.findAll();
|
||||
// return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public User findById(Long id) throws AccessDeniedException {
|
||||
// User u = userRepository.getOne(id);
|
||||
// return u;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
// @PreAuthorize("hasRole('USER')")
|
||||
public User findByUsername(String username) throws UsernameNotFoundException {
|
||||
User u = userRepository.findByUsername(username);
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCredentials() {
|
||||
// List<User> users = userRepository.findAll();
|
||||
// for (User user : users) {
|
||||
// user.setPassword(passwordEncoder.encode("123"));
|
||||
// userRepository.save(user);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public User save(UserRequest userRequest) {
|
||||
User user = new User();
|
||||
// user.setUsername(userRequest.getUsername());
|
||||
// user.setPassword(passwordEncoder.encode(userRequest.getPassword()));
|
||||
// user.setFirstname(userRequest.getFirstname());
|
||||
// user.setLastname(userRequest.getLastname());
|
||||
// List<Authority> auth = authService.findByname("ROLE_USER");
|
||||
// user.setAuthorities(auth);
|
||||
// this.userRepository.save(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
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>2.2.2-SNAPSHOT</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>
|
||||
@@ -0,0 +1,32 @@
|
||||
package it.fabioformosa.quartzmanager.security.helpers.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
|
||||
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
|
||||
|
||||
public AuthenticationSuccessHandler(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) {
|
||||
super();
|
||||
this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler;
|
||||
}
|
||||
|
||||
public String cookieMustBeDeletedAtLogout() {
|
||||
return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication ) throws IOException, ServletException {
|
||||
clearAuthenticationAttributes(request);
|
||||
jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,15 +33,12 @@ public class JwtTokenHelper {
|
||||
return Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// @Value("${app.name}")
|
||||
private final String appName;
|
||||
|
||||
// @Autowired
|
||||
private final JwtSecurityProperties jwtSecurityProps;
|
||||
|
||||
private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;
|
||||
|
||||
// @Autowired
|
||||
public JwtTokenHelper(String appName, JwtSecurityProperties jwtSecurityProps) {
|
||||
super();
|
||||
this.appName = appName;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user