[BAEL-4639] Running background jobs in Spring with JobRunr (#10089)
* [BAEL-4639] Running background jobs in Spring with JobRunr * [BAEL-4639] Background jobs in Spring with JobRunr - update dependencies and readme * [BAEL-4639] Background jobs in Spring with JobRunr - add required newline to application.properties * [BAEL-4639] Background jobs in Spring with JobRunr - Make sure link is correct * [BAEL-4639] Background jobs in Spring with JobRunr - Cleanup and LiveTest added * [BAEL-4639] - Feedback * [BAEL-4639] Update test with feedback
This commit is contained in:
parent
7c8b656a7d
commit
70203a988b
|
@ -46,6 +46,7 @@
|
|||
<module>spring-boot-jasypt</module>
|
||||
<module>spring-boot-keycloak</module>
|
||||
<module>spring-boot-libraries</module>
|
||||
<module>spring-boot-libraries-2</module>
|
||||
<module>spring-boot-logging-log4j2</module>
|
||||
<module>spring-boot-kotlin</module>
|
||||
<module>spring-boot-mvc</module>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
## Spring Boot Libraries
|
||||
|
||||
This module contains articles about various Spring Boot libraries
|
||||
|
||||
### Relevant Articles:
|
||||
|
||||
- Running background jobs in Spring with JobRunr
|
|
@ -0,0 +1,46 @@
|
|||
<?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">
|
||||
<parent>
|
||||
<artifactId>spring-boot-modules</artifactId>
|
||||
<groupId>com.baeldung.spring-boot-modules</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-boot-libraries-2</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JobRunr -->
|
||||
<dependency>
|
||||
<groupId>org.jobrunr</groupId>
|
||||
<artifactId>jobrunr-spring-boot-starter</artifactId>
|
||||
<version>${jobrunr.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${awaitility.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<jobrunr.version>1.0.0</jobrunr.version>
|
||||
<awaitility.version>4.0.3</awaitility.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,37 @@
|
|||
package com.baeldung.jobrunr;
|
||||
|
||||
import com.baeldung.jobrunr.service.SampleJobService;
|
||||
import org.jobrunr.jobs.mappers.JobMapper;
|
||||
import org.jobrunr.scheduling.JobScheduler;
|
||||
import org.jobrunr.scheduling.cron.Cron;
|
||||
import org.jobrunr.storage.InMemoryStorageProvider;
|
||||
import org.jobrunr.storage.StorageProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@SpringBootApplication
|
||||
public class JobRunrSpringBootApp {
|
||||
|
||||
@Autowired
|
||||
private JobScheduler jobScheduler;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(JobRunrSpringBootApp.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StorageProvider storageProvider(JobMapper jobMapper) {
|
||||
InMemoryStorageProvider storageProvider = new InMemoryStorageProvider();
|
||||
storageProvider.setJobMapper(jobMapper);
|
||||
return storageProvider;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void scheduleRecurrently() {
|
||||
jobScheduler.<SampleJobService>scheduleRecurrently(x -> x.executeSampleJob("a recurring job"), Cron.every5minutes());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.baeldung.jobrunr.controller;
|
||||
|
||||
import com.baeldung.jobrunr.service.SampleJobService;
|
||||
import org.jobrunr.scheduling.JobScheduler;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/jobrunr")
|
||||
public class JobRunrController {
|
||||
|
||||
private JobScheduler jobScheduler;
|
||||
private SampleJobService sampleJobService;
|
||||
|
||||
private JobRunrController(JobScheduler jobScheduler, SampleJobService sampleJobService) {
|
||||
this.jobScheduler = jobScheduler;
|
||||
this.sampleJobService = sampleJobService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/enqueue/{input}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> enqueue(@PathVariable("input") @DefaultValue("default-input") String input) {
|
||||
jobScheduler.enqueue(() -> sampleJobService.executeSampleJob(input));
|
||||
return okResponse("job enqueued successfully");
|
||||
}
|
||||
|
||||
@GetMapping(value = "/schedule/{input}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<String> schedule(
|
||||
@PathVariable("input") @DefaultValue("default-input") String input,
|
||||
@RequestParam("scheduleAt") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime scheduleAt) {
|
||||
jobScheduler.schedule(() -> sampleJobService.executeSampleJob(input), scheduleAt);
|
||||
return okResponse("job scheduled successfully");
|
||||
}
|
||||
|
||||
private ResponseEntity<String> okResponse(String feedback) {
|
||||
return new ResponseEntity<>(feedback, HttpStatus.OK);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.baeldung.jobrunr.service;
|
||||
|
||||
import org.jobrunr.jobs.annotations.Job;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Service
|
||||
public class SampleJobService {
|
||||
|
||||
public static final long EXECUTION_TIME = 5000L;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private AtomicInteger count = new AtomicInteger();
|
||||
|
||||
@Job(name = "The sample job with variable %0", retries = 2)
|
||||
public void executeSampleJob(String variable) {
|
||||
|
||||
logger.info("The sample job has begun. The variable you passed is {}", variable);
|
||||
try {
|
||||
Thread.sleep(EXECUTION_TIME);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Error while executing sample job", e);
|
||||
} finally {
|
||||
count.incrementAndGet();
|
||||
logger.info("Sample job has finished...");
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumberOfInvocations() {
|
||||
return count.get();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.jobrunr.background_job_server=true
|
||||
org.jobrunr.dashboard=true
|
|
@ -0,0 +1,46 @@
|
|||
package com.baeldung.jobrunr;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.jobrunr.jobs.states.StateName;
|
||||
import org.jobrunr.storage.StorageProvider;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = DEFINED_PORT, classes = JobRunrSpringBootApp.class)
|
||||
public class JobRunrLiveTest {
|
||||
|
||||
@Autowired
|
||||
TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
StorageProvider storageProvider;
|
||||
|
||||
@Test
|
||||
public void givenEndpoint_whenJobEnqueued_thenJobIsProcessedWithin30Seconds() {
|
||||
String response = enqueueJobViaRest("some-input");
|
||||
assertEquals("job enqueued successfully", response);
|
||||
|
||||
await().atMost(30, TimeUnit.SECONDS).until(() -> storageProvider.countJobs(StateName.SUCCEEDED) == 1);
|
||||
}
|
||||
|
||||
private String enqueueJobViaRest(String input) {
|
||||
try {
|
||||
return restTemplate.getForObject(new URI("http://localhost:8080/jobrunr/enqueue/" + input), String.class);
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue