Skip to content

Commit 9987441

Browse files
committed
Merge branch 'main' into v1.6
2 parents ae80e0d + 876c7e9 commit 9987441

File tree

16 files changed

+1253
-730
lines changed

16 files changed

+1253
-730
lines changed

CHANGELOG.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
# Changelog
22

3+
## v1.5.6 - (2025-03-06)
4+
5+
- Better ID search
6+
- Added info to the UI how to search
7+
8+
## v1.5.5 - (2025-03-04)
9+
10+
- MdcTriggerInterceptor adds now start date and scheduler name
11+
312
## v1.5.4 - (2025-01-19)
413

5-
- Added MdcTriggerInterceptor
14+
- Added MdcTriggerInterceptor
615

716
## v1.5.3 - (2025-01-14)
817

9-
- adjusted trigger cols that the UI does not break
10-
- showing always all existing schedulers
18+
- adjusted trigger cols that the UI does not break
19+
- showing always all existing schedulers
1120

1221
## v1.5.2 - (2025-01-13)
1322

14-
- FixedIntervalRetryStrategy
15-
- Added SchedulerCustomizer
23+
- FixedIntervalRetryStrategy
24+
- Added SchedulerCustomizer
1625

1726
## v1.5.1 - (2025-01-12)
1827

19-
- filter trigger by status
20-
- filter history by status
28+
- filter trigger by status
29+
- filter history by status
2130

2231
## v1.5.0 - (2025-01-11)
2332

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
[![Java CI with Maven](https://github.com/sterlp/spring-persistent-tasks/actions/workflows/build.yml/badge.svg)](https://github.com/sterlp/spring-persistent-tasks/actions/workflows/build.yml)
2+
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.x-brightgreen.svg)](https://spring.io/projects/spring-boot)
3+
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
24

35
# Spring Persistent Tasks
46

@@ -8,6 +10,16 @@ Focus is the usage with spring boot and JPA.
810

911
Secondary goal is to support [Poor mans Workflow](https://github.com/sterlp/pmw)
1012

13+
# Key Features ✨
14+
1. **JPA-Powered Persistence** - Automatically persists tasks to your database
15+
1. **Spring Boot Auto-Configuration** - Simple setup with @EnableSpringPersistentTasks
16+
1. **Clustering Support** - Built-in lock management for distributed environments
17+
1. **Type-Safe Tasks** - Generic task definitions with state objects
18+
1. **Retry Mechanisms** - Automatic retry policies for failed executions
19+
1. **Transactional Integration** - Works with Spring's transaction management
20+
1. **Queue Management** - Intelligent task queuing and prioritization
21+
1. **Different DB Supports** --
22+
1123
# Documentation
1224

1325
Use for more advanced doc the [WIKI](https://github.com/sterlp/spring-persistent-tasks/wiki).
@@ -17,10 +29,10 @@ The README contains a shorter how to use.
1729

1830
## Tested in the pipeline
1931

20-
- H2
21-
- azure-sql-edge (MSSQL)
22-
- PostgreSQL
23-
- MariaDB
32+
- [![H2](https://img.shields.io/badge/H2-Database-green.svg)](https://www.h2database.com)
33+
- [![Azure SQL Edge](https://img.shields.io/badge/Azure_SQL_Edge-2022-0078D4?logo=microsoftsqlserver)](https://azure.microsoft.com/en-us/products/azure-sql/edge/)
34+
- [![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-336791?logo=postgresql)](https://www.postgresql.org)
35+
- [![MariaDB](https://img.shields.io/badge/MariaDB-11.1-003545?logo=mariadb)](https://mariadb.org)
2436

2537
![History](screenshots/supported-dbs.png)
2638

@@ -76,7 +88,7 @@ PersistentTaskService persistentTaskService;
7688

7789
public void triggerTask1(Vehicle vehicle) {
7890
persistentTaskService.runOrQueue(
79-
TaskTriggerBuilder.newTrigger("task1").state(vehicle).build());
91+
TriggerBuilder.newTrigger("task1").state(vehicle).build());
8092
}
8193
```
8294

core/src/main/java/org/sterl/spring/persistent_tasks/history/HistoryService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.sterl.spring.persistent_tasks.history.model.TriggerHistoryLastStateEntity;
1818
import org.sterl.spring.persistent_tasks.history.repository.TriggerHistoryDetailRepository;
1919
import org.sterl.spring.persistent_tasks.history.repository.TriggerHistoryLastStateRepository;
20+
import org.sterl.spring.persistent_tasks.shared.StringHelper;
2021
import org.sterl.spring.persistent_tasks.shared.stereotype.TransactionalService;
2122
import org.sterl.spring.persistent_tasks.trigger.TriggerService;
2223
import org.sterl.spring.persistent_tasks.trigger.model.TriggerEntity;
@@ -102,7 +103,7 @@ public Page<TriggerHistoryLastStateEntity> findTriggerState(
102103
if (key == null && status == null) {
103104
return triggerHistoryLastStateRepository.findAll(page);
104105
}
105-
final var id = key == null ? null : key.getId();
106+
final var id = StringHelper.applySearchWildCard(key);
106107
final var name = key == null ? null : key.getTaskName();
107108
return triggerHistoryLastStateRepository.findAll(id, name, status, page);
108109
}

core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ public List<TriggerEntity> rescheduleAbandonedTasks(OffsetDateTime timeout) {
169169
.toList();
170170

171171
int running = triggerService.markTriggersAsRunning(runningKeys, name);
172-
log.debug("({}) - {} trigger(s) are running on {} schedulers", running, runningKeys, schedulers);
172+
log.debug("({}) - {} trigger(s) are running on {} schedulers",
173+
running, runningKeys, schedulers);
173174
return triggerService.rescheduleAbandonedTasks(timeout);
174175
}
175176

core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/api/SchedulerResource.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ public class SchedulerResource {
2525
public List<SchedulerEntity> listAll() {
2626
return anyService.listAll();
2727
}
28-
29-
28+
3029
@GetMapping("/schedulers/{name}")
3130
public ResponseEntity<SchedulerEntity> get(@PathVariable("name") String name) {
3231
return ResponseEntity.of(anyService.findStatus(name));
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.sterl.spring.persistent_tasks.shared;
2+
3+
import org.sterl.spring.persistent_tasks.api.TriggerKey;
4+
5+
public class StringHelper {
6+
7+
/**
8+
* Replaces all <code>*</code> with <code>%</code> as needed
9+
*/
10+
public static String applySearchWildCard(String value) {
11+
if (value == null || value.length() == 0) return null;
12+
return value.replace('*', '%').replace('[', '_');
13+
}
14+
15+
/**
16+
* Replaces all <code>*</code> with <code>%</code> as needed for the id.
17+
*/
18+
public static String applySearchWildCard(TriggerKey key) {
19+
if (key == null) return null;
20+
return applySearchWildCard(key.getId());
21+
}
22+
}

core/src/main/java/org/sterl/spring/persistent_tasks/shared/repository/TriggerDataRepository.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
public interface TriggerDataRepository<T extends HasTriggerData> extends JpaRepository<T, Long> {
2020
@Query("""
2121
SELECT e FROM #{#entityName} e
22-
WHERE (e.data.key.id LIKE :id% OR :id IS NULL)
23-
AND (e.data.key.taskName = :taskName OR :taskName IS NULL)
24-
AND (e.data.status = :status OR :status IS NULL)
22+
WHERE (:id IS NULL OR e.data.key.id LIKE :id)
23+
AND (:taskName IS NULL OR e.data.key.taskName = :taskName)
24+
AND (:status IS NULL OR e.data.status = :status)
2525
""")
2626
Page<T> findAll(@Param("id") String id,
2727
@Param("taskName") String taskName,

core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.sterl.spring.persistent_tasks.api.TaskId;
1212
import org.sterl.spring.persistent_tasks.api.TriggerKey;
1313
import org.sterl.spring.persistent_tasks.api.TriggerStatus;
14+
import org.sterl.spring.persistent_tasks.shared.StringHelper;
1415
import org.sterl.spring.persistent_tasks.shared.stereotype.TransactionalCompontant;
1516
import org.sterl.spring.persistent_tasks.trigger.model.TriggerEntity;
1617
import org.sterl.spring.persistent_tasks.trigger.repository.TriggerRepository;
@@ -53,7 +54,7 @@ public List<TriggerEntity> findTriggersLastPingAfter(OffsetDateTime dateTime) {
5354
public Page<TriggerEntity> listTriggers(@Nullable TriggerKey key,
5455
@Nullable TriggerStatus status, Pageable page) {
5556
if (key == null && status == null) return triggerRepository.findAll(page);
56-
final var id = key == null ? null : key.getId();
57+
final var id = StringHelper.applySearchWildCard(key);
5758
final var name = key == null ? null : key.getTaskName();
5859
return triggerRepository.findAll(id, name, status, page);
5960
}

core/src/main/java/org/sterl/spring/persistent_tasks/trigger/interceptor/MdcTriggerInterceptor.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,33 @@
1313
@Component
1414
public class MdcTriggerInterceptor {
1515

16+
public static final String TASK_RUNNING_ON = "taskRunningOn";
17+
public static final String TASK_START = "taskStart";
1618
public static final String TASK_NAME = "taskName";
19+
public static final String TASK_KEY = "taskKey";
1720
public static final String TASK_ID = "taskId";
1821

1922
@EventListener
2023
public void beforeRun(TriggerRunningEvent data) {
24+
MDC.put(TASK_RUNNING_ON, data.runningOn());
25+
MDC.put(TASK_START, data.getData().getStart() + "");
2126
MDC.put(TASK_NAME, data.key().getTaskName());
22-
MDC.put(TASK_ID, data.key().getId());
27+
MDC.put(TASK_KEY, data.key().getId());
28+
MDC.put(TASK_ID, data.id() + "");
2329
}
2430
@EventListener
2531
public void onFailed(TriggerFailedEvent data) {
26-
MDC.remove(TASK_NAME);
27-
MDC.remove(TASK_ID);
32+
clearMdc();
2833
}
2934
@EventListener
3035
public void onSuccess(TriggerSuccessEvent data) {
36+
clearMdc();
37+
}
38+
private void clearMdc() {
39+
MDC.remove(TASK_RUNNING_ON);
40+
MDC.remove(TASK_START);
3141
MDC.remove(TASK_NAME);
42+
MDC.remove(TASK_KEY);
3243
MDC.remove(TASK_ID);
3344
}
3445
}

core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.time.Duration;
66
import java.time.OffsetDateTime;
7+
import java.util.UUID;
78

89
import org.junit.jupiter.api.BeforeEach;
910
import org.junit.jupiter.api.Test;
@@ -63,14 +64,28 @@ void testList() {
6364
@Test
6465
void testSearchById() {
6566
// GIVEN
67+
var uuid = UUID.randomUUID().toString();
6668
var key1 = triggerService.queue(TriggerBuilder
67-
.newTrigger("task1").build()).getKey();
69+
.newTrigger("task1")
70+
.id("[@foo:[email protected]:" + uuid + "]")
71+
.build())
72+
.getKey();
6873
var key2 = triggerService.queue(TriggerBuilder
6974
.newTrigger("task1").build()).getKey();
7075

7176
// WHEN
7277
var response = template.exchange(
73-
baseUrl + "?id=" + key1.getId().substring(0, 30),
78+
baseUrl + "?id=*" + key2.getId().substring(5, 30) + "*",
79+
HttpMethod.GET,
80+
null,
81+
String.class);
82+
// THEN
83+
assertThat(response.getBody()).contains(key2.getId());
84+
assertThat(response.getBody()).doesNotContain(key1.getId());
85+
86+
// WHEN
87+
response = template.exchange(
88+
baseUrl + "?id=" + key1.getId().substring(0, 30) + "*",
7489
HttpMethod.GET,
7590
null,
7691
String.class);
@@ -81,20 +96,20 @@ void testSearchById() {
8196
assertThat(response.getBody()).contains(key1.getId());
8297
assertThat(response.getBody()).doesNotContain(key2.getId());
8398
}
84-
99+
85100
@Test
86101
void testSearchByStatus() {
87102
// GIVEN
88103
var k1 = createStatus(new TriggerKey("1-foo", "foo"), TriggerStatus.WAITING).getKey();
89104
var k2 = createStatus(new TriggerKey("2-foo", "bar"), TriggerStatus.RUNNING).getKey();
90-
105+
91106
// WHEN
92107
var response = template.exchange(
93108
baseUrl + "?status=" + TriggerStatus.RUNNING,
94109
HttpMethod.GET,
95110
null,
96111
String.class);
97-
112+
98113
// THEN
99114
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
100115
assertThat(response.getBody()).isNotNull();

0 commit comments

Comments
 (0)