Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ running server:

`curl "http://localhost:8080/greeting?salutation=good%20morning&name=robin"`


### Docker
The application can be built into a Docker image and run on a docker environment:

```bash
docker build -t robjwilkins/showcase .

docker run -p 8080:8080 robjwilkins/showcase
```
```

### Add a demo of quartz

Quartz is a java based framework which provides scheduling functionality. This allows jobs to be
created which will be run at a later point-in-time.

The job details are persisted in a database. This demo uses an in-memory h2 database. A flyway migration
is also added to create the schema for this database
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test')
implementation("org.springframework.boot:spring-boot-starter-quartz")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation('org.flywaydb:flyway-core')
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
runtimeOnly('com.h2database:h2')
}

test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,37 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.quartz.*;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.UUID;

import static org.quartz.TriggerBuilder.newTrigger;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.ResponseEntity.noContent;
import static org.springframework.http.ResponseEntity.notFound;

@RestController
@RequestMapping(path = "/greeting", produces = APPLICATION_JSON_VALUE)
public class GreetingController {

private final Logger log = LoggerFactory.getLogger(GreetingController.class);
private static final Logger log = LoggerFactory.getLogger(GreetingController.class);
private static final String JOB_GROUP = "greeting-jobs";

private final Scheduler scheduler;

public GreetingController(Scheduler scheduler) {
this.scheduler = scheduler;
}

@GetMapping("/greeting")
@GetMapping
public Greeting getGreeting(@RequestParam(name = "salutation", required = false, defaultValue = "hello") String salutationParam,
@RequestParam(name = "name", required = false, defaultValue = "world") String nameParam) {
@RequestParam(name = "name", required = false, defaultValue = "world") String nameParam) {

log.info("A greeting was requested");

Expand All @@ -23,4 +45,43 @@ public Greeting getGreeting(@RequestParam(name = "salutation", required = false,

return greeting;
}

@PostMapping
public String postGreeting(@RequestParam(name = "salutation", required = false, defaultValue = "hello") String salutationParam,
@RequestParam(name = "name", required = false, defaultValue = "world") String nameParam,
@RequestParam(name = "durationMins", required = false, defaultValue = "2") String durationMins) throws SchedulerException {
var jobDataMap = new JobDataMap();
jobDataMap.put("salutation", salutationParam);
jobDataMap.put("name", nameParam);
jobDataMap.put("greeting", Greeting.of(salutationParam, nameParam));
var id = UUID.randomUUID().toString();
var jobDetail = JobBuilder.newJob()
.ofType(GreetingJob.class)
.withIdentity(id, JOB_GROUP)
.withDescription("Send Greeting Job")
.usingJobData(jobDataMap)
.requestRecovery()
.build();
var startAt = Timestamp.valueOf(LocalDateTime.from(
new Date().toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime())
.plusMinutes(1));
var trigger = newTrigger()
.forJob(jobDetail)
.withIdentity(jobDetail.getKey().getName(), "greeting-triggers")
.withDescription("Send Greeting Trigger")
.startAt(startAt)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0))
.build();
log.info("scheduling job to run in 1 minutes time");
scheduler.scheduleJob(jobDetail, trigger);
return id;
}

@DeleteMapping("/{id}")
public ResponseEntity<?> deleteGreeting(@PathVariable String id) throws SchedulerException {
log.info("deleting job: {}", id);
return scheduler.deleteJob(new JobKey(id, JOB_GROUP)) ? noContent().build() : notFound().build();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/wilkins/showcase/controllers/GreetingJob.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.wilkins.showcase.controllers;

import com.wilkins.showcase.services.Greeter;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public record GreetingJob(Greeter greeter) implements Job {

private static final Logger log = LoggerFactory.getLogger(GreetingJob.class);
@Override
public void execute(JobExecutionContext context) {
JobDataMap jobDataMap = context.getMergedJobDataMap();
log.info("******** {}, {} *************", context.getJobDetail().getKey().getName(),
greeter.greet((Greeting) jobDataMap.get("greeting")));
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/wilkins/showcase/services/Greeter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.wilkins.showcase.services;

import com.wilkins.showcase.controllers.Greeting;
import org.springframework.stereotype.Component;

@Component
public class Greeter {

public String greet(Greeting greeting) {
return greeting.salutation() + " " + greeting.name();
}
}
16 changes: 16 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
spring:
quartz:
job-store-type: jdbc
properties:
org:
quartz:
jobStore:
driverDelegateClass: "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"
wait-for-jobs-to-complete-on-shutdown: true
jdbc:
initialize-schema: never

datasource:
url: jdbc:h2:mem:testdb
username: sa
password: password
214 changes: 214 additions & 0 deletions src/main/resources/db/migration/V0__add_quartz.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
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),
job_class_name varchar(250) not null,
is_durable boolean not null,
is_nonconcurrent boolean not null,
is_update_data boolean not null,
requests_recovery boolean not null,
job_data bytea,
constraint qrtz_job_details_pkey
primary key (sched_name, job_name, job_group)
);

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 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),
next_fire_time bigint,
prev_fire_time bigint,
priority integer,
trigger_state varchar(16) not null,
trigger_type varchar(8) not null,
start_time bigint not null,
end_time bigint,
calendar_name varchar(200),
misfire_instr smallint,
job_data bytea,
constraint qrtz_triggers_pkey
primary key (sched_name, trigger_name, trigger_group),
constraint qrtz_triggers_sched_name_job_name_job_group_fkey
foreign key (sched_name, job_name, job_group) references qrtz_job_details
);

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 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,
constraint qrtz_blob_triggers_pkey
primary key (sched_name, trigger_name, trigger_group),
constraint qrtz_blob_triggers_sched_name_trigger_name_trigger_group_fkey
foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers
);

create table qrtz_calendars
(
sched_name varchar(120) not null,
calendar_name varchar(200) not null,
calendar bytea not null,
constraint qrtz_calendars_pkey
primary key (sched_name, calendar_name)
);

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),
constraint qrtz_cron_triggers_pkey
primary key (sched_name, trigger_name, trigger_group),
constraint qrtz_cron_triggers_sched_name_trigger_name_trigger_group_fkey
foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers
);

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),
job_group varchar(200),
is_nonconcurrent boolean,
requests_recovery boolean,
constraint qrtz_fired_triggers_pkey
primary key (sched_name, entry_id)
);

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);

create table qrtz_locks
(
sched_name varchar(120) not null,
lock_name varchar(40) not null,
constraint qrtz_locks_pkey
primary key (sched_name, lock_name)
);

create table qrtz_paused_trigger_grps
(
sched_name varchar(120) not null,
trigger_group varchar(200) not null,
constraint qrtz_paused_trigger_grps_pkey
primary key (sched_name, trigger_group)
);

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,
constraint qrtz_scheduler_state_pkey
primary key (sched_name, instance_name)
);

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,
constraint qrtz_simple_triggers_pkey
primary key (sched_name, trigger_name, trigger_group),
constraint qrtz_simple_triggers_sched_name_trigger_name_trigger_group_fkey
foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers
);

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),
str_prop_2 varchar(512),
str_prop_3 varchar(512),
int_prop_1 integer,
int_prop_2 integer,
long_prop_1 bigint,
long_prop_2 bigint,
dec_prop_1 numeric(13,4),
dec_prop_2 numeric(13,4),
bool_prop_1 boolean,
bool_prop_2 boolean,
constraint qrtz_simprop_triggers_pkey
primary key (sched_name, trigger_name, trigger_group),
constraint qrtz_simprop_triggers_sched_name_trigger_name_trigger_grou_fkey
foreign key (sched_name, trigger_name, trigger_group) references qrtz_triggers
);
Loading