Skip to content

Commit a297cd1

Browse files
committed
Add initial support for a resourceless job repository
Resolves #4679
1 parent 675f90d commit a297cd1

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.core.repository.support;
17+
18+
import java.time.LocalDateTime;
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
23+
import org.springframework.batch.core.JobExecution;
24+
import org.springframework.batch.core.JobInstance;
25+
import org.springframework.batch.core.JobParameters;
26+
import org.springframework.batch.core.StepExecution;
27+
import org.springframework.batch.core.repository.JobRepository;
28+
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
29+
30+
/**
31+
* A {@link JobRepository} implementation that does not use or store batch meta-data. It
32+
* is intended for use-cases where restartability is not required and where the execution
33+
* context is not involved in any way (like sharing data between steps through the
34+
* execution context, or partitioned steps where partitions meta-data is shared between
35+
* the manager and workers through the execution context, etc). <br/>
36+
* This implementation holds a single job instance and a corresponding job execution that
37+
* are suitable for one-time jobs executed in their own JVM. This job repository works
38+
* with transactional steps as well as non-transactional steps (in which case, a
39+
* {@link ResourcelessTransactionManager} can be used). <br/>
40+
* This implementation is not thread-safe and should not be used in any concurrent
41+
* environment.
42+
*
43+
* @since 5.2.0
44+
* @author Mahmoud Ben Hassine
45+
*/
46+
public class ResourcelessJobRepository implements JobRepository {
47+
48+
private JobInstance jobInstance;
49+
50+
private JobExecution jobExecution;
51+
52+
@Override
53+
public boolean isJobInstanceExists(String jobName, JobParameters jobParameters) {
54+
return false;
55+
}
56+
57+
@Override
58+
public JobInstance createJobInstance(String jobName, JobParameters jobParameters) {
59+
this.jobInstance = new JobInstance(1L, jobName);
60+
return this.jobInstance;
61+
}
62+
63+
@Override
64+
public JobExecution createJobExecution(String jobName, JobParameters jobParameters) {
65+
if (this.jobInstance == null) {
66+
createJobInstance(jobName, jobParameters);
67+
}
68+
this.jobExecution = new JobExecution(this.jobInstance, 1L, jobParameters);
69+
return this.jobExecution;
70+
}
71+
72+
@Override
73+
public void update(JobExecution jobExecution) {
74+
jobExecution.setLastUpdated(LocalDateTime.now());
75+
this.jobExecution = jobExecution;
76+
}
77+
78+
@Override
79+
public void add(StepExecution stepExecution) {
80+
this.addAll(Collections.singletonList(stepExecution));
81+
}
82+
83+
@Override
84+
public void addAll(Collection<StepExecution> stepExecutions) {
85+
this.jobExecution.addStepExecutions(new ArrayList<>(stepExecutions));
86+
}
87+
88+
@Override
89+
public void update(StepExecution stepExecution) {
90+
stepExecution.setLastUpdated(LocalDateTime.now());
91+
if (this.jobExecution.isStopping()) {
92+
stepExecution.setTerminateOnly();
93+
}
94+
}
95+
96+
@Override
97+
public void updateExecutionContext(StepExecution stepExecution) {
98+
stepExecution.setLastUpdated(LocalDateTime.now());
99+
}
100+
101+
@Override
102+
public void updateExecutionContext(JobExecution jobExecution) {
103+
jobExecution.setLastUpdated(LocalDateTime.now());
104+
}
105+
106+
@Override
107+
public StepExecution getLastStepExecution(JobInstance jobInstance, String stepName) {
108+
return this.jobExecution.getStepExecutions()
109+
.stream()
110+
.filter(stepExecution -> stepExecution.getStepName().equals(stepName))
111+
.findFirst()
112+
.orElse(null);
113+
}
114+
115+
@Override
116+
public long getStepExecutionCount(JobInstance jobInstance, String stepName) {
117+
return this.jobExecution.getStepExecutions()
118+
.stream()
119+
.filter(stepExecution -> stepExecution.getStepName().equals(stepName))
120+
.count();
121+
}
122+
123+
@Override
124+
public JobExecution getLastJobExecution(String jobName, JobParameters jobParameters) {
125+
return this.jobExecution;
126+
}
127+
128+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.core.repository.support;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import org.springframework.batch.core.JobExecution;
21+
import org.springframework.batch.core.JobInstance;
22+
import org.springframework.batch.core.JobParameters;
23+
24+
import static org.junit.jupiter.api.Assertions.assertEquals;
25+
import static org.junit.jupiter.api.Assertions.assertFalse;
26+
import static org.junit.jupiter.api.Assertions.assertNotNull;
27+
28+
/**
29+
* Test class for {@link ResourcelessJobRepository}.
30+
*
31+
* @author Mahmoud Ben Hassine
32+
*/
33+
class ResourcelessJobRepositoryTests {
34+
35+
private final ResourcelessJobRepository jobRepository = new ResourcelessJobRepository();
36+
37+
@Test
38+
void isJobInstanceExists() {
39+
assertFalse(this.jobRepository.isJobInstanceExists("job", new JobParameters()));
40+
}
41+
42+
@Test
43+
void createJobInstance() {
44+
// given
45+
String jobName = "job";
46+
JobParameters jobParameters = new JobParameters();
47+
48+
// when
49+
JobInstance jobInstance = this.jobRepository.createJobInstance(jobName, jobParameters);
50+
51+
// then
52+
assertNotNull(jobInstance);
53+
assertEquals(jobName, jobInstance.getJobName());
54+
assertEquals(1L, jobInstance.getInstanceId());
55+
}
56+
57+
@Test
58+
void createJobExecution() {
59+
// given
60+
String jobName = "job";
61+
JobParameters jobParameters = new JobParameters();
62+
63+
// when
64+
JobExecution jobExecution = this.jobRepository.createJobExecution(jobName, jobParameters);
65+
66+
// then
67+
assertNotNull(jobExecution);
68+
assertEquals(1L, jobExecution.getId());
69+
assertEquals(jobName, jobExecution.getJobInstance().getJobName());
70+
assertEquals(1L, jobExecution.getJobInstance().getInstanceId());
71+
}
72+
73+
@Test
74+
void getLastJobExecution() {
75+
// given
76+
String jobName = "job";
77+
JobParameters jobParameters = new JobParameters();
78+
this.jobRepository.createJobExecution(jobName, jobParameters);
79+
80+
// when
81+
JobExecution jobExecution = this.jobRepository.getLastJobExecution(jobName, jobParameters);
82+
83+
// then
84+
assertNotNull(jobExecution);
85+
assertEquals(1L, jobExecution.getId());
86+
assertEquals(jobName, jobExecution.getJobInstance().getJobName());
87+
assertEquals(1L, jobExecution.getJobInstance().getInstanceId());
88+
}
89+
90+
}

0 commit comments

Comments
 (0)