[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:
Ronald Dehuysser 2020-09-26 23:14:11 +02:00 committed by GitHub
parent 7c8b656a7d
commit 70203a988b
8 changed files with 218 additions and 0 deletions

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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());
}
}

View File

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

View File

@ -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();
}
}

View File

@ -0,0 +1,2 @@
org.jobrunr.background_job_server=true
org.jobrunr.dashboard=true

View File

@ -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;
}
}