Make JobRepository extend JobExplorer

Resolves #4824
This commit is contained in:
Mahmoud Ben Hassine
2025-05-05 15:26:43 +02:00
parent bf53794d6a
commit b8c93d677e
4 changed files with 266 additions and 269 deletions

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.batch.core.explore;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -51,7 +52,9 @@ public interface JobExplorer {
* alphabetically).
* @return the list of job names that have been executed.
*/
List<String> getJobNames();
default List<String> getJobNames() {
return Collections.emptyList();
}
/*
* ===================================================================================
@@ -67,7 +70,9 @@ public interface JobExplorer {
* @param count The maximum number of instances to return.
* @return the {@link JobInstance} values up to a maximum of count values.
*/
List<JobInstance> getJobInstances(String jobName, int start, int count);
default List<JobInstance> getJobInstances(String jobName, int start, int count) {
return Collections.emptyList();
}
/**
* Fetch {@link JobInstance} values in descending order of creation (and, therefore,
@@ -79,8 +84,51 @@ public interface JobExplorer {
* @deprecated Since v6.0 and scheduled for removal in v6.2. Use
* {@link #getJobInstances(String, int, int)}
*/
@Deprecated(forRemoval = true)
List<JobInstance> findJobInstancesByJobName(String jobName, int start, int count);
@Deprecated(since = "6.0", forRemoval = true)
default List<JobInstance> findJobInstancesByJobName(String jobName, int start, int count) {
return Collections.emptyList();
}
/**
* Fetch the last job instances with the provided name, sorted backwards by primary
* key, using a 'like' criteria
* @param jobName {@link String} containing the name of the job.
* @param start int containing the offset of where list of job instances results
* should begin.
* @param count int containing the number of job instances to return.
* @return a list of {@link JobInstance} for the job name requested.
* @since 5.0
* @deprecated since v6.0 and scheduled for removal in v6.2. Use
* {@link #getJobInstances(String, int, int)}
*/
@Deprecated(since = "6.0", forRemoval = true)
default List<JobInstance> findJobInstancesByName(String jobName, int start, int count) {
return Collections.emptyList();
}
/**
* Check if an instance of this job already exists with the parameters provided.
* @param jobName the name of the job
* @param jobParameters the parameters to match
* @return true if a {@link JobInstance} already exists for this job name and job
* parameters
* @deprecated Since v6.0 and scheduled for removal in v6.2. Use
* {@link #getJobInstance(String, JobParameters)} and check for {@code null} result
* instead.
*/
@Deprecated(since = "6.0", forRemoval = true)
default boolean isJobInstanceExists(String jobName, JobParameters jobParameters) {
return getJobInstance(jobName, jobParameters) != null;
}
/**
* @param instanceId {@link Long} The ID for the {@link JobInstance} to obtain.
* @return the {@code JobInstance} that has this ID, or {@code null} if not found.
*/
@Nullable
default JobInstance getJobInstance(@Nullable Long instanceId) {
throw new UnsupportedOperationException();
}
/**
* Find the last job instance, by ID, for the given job.
@@ -94,13 +142,6 @@ public interface JobExplorer {
throw new UnsupportedOperationException();
}
/**
* @param instanceId {@link Long} The ID for the {@link JobInstance} to obtain.
* @return the {@code JobInstance} that has this ID, or {@code null} if not found.
*/
@Nullable
JobInstance getJobInstance(@Nullable Long instanceId);
/**
* @param jobName {@link String} name of the job.
* @param jobParameters {@link JobParameters} parameters for the job instance.
@@ -123,7 +164,9 @@ public interface JobExplorer {
* @throws NoSuchJobException thrown when there is no {@link JobInstance} for the
* jobName specified.
*/
long getJobInstanceCount(@Nullable String jobName) throws NoSuchJobException;
default long getJobInstanceCount(@Nullable String jobName) throws NoSuchJobException {
throw new UnsupportedOperationException();
}
/*
* ===================================================================================
@@ -140,7 +183,9 @@ public interface JobExplorer {
* @return the {@link JobExecution} that has this ID or {@code null} if not found.
*/
@Nullable
JobExecution getJobExecution(@Nullable Long executionId);
default JobExecution getJobExecution(@Nullable Long executionId) {
throw new UnsupportedOperationException();
}
/**
* Retrieve job executions by their job instance. The corresponding step executions
@@ -150,7 +195,23 @@ public interface JobExplorer {
* @param jobInstance The {@link JobInstance} to query.
* @return the list of all executions for the specified {@link JobInstance}.
*/
List<JobExecution> getJobExecutions(JobInstance jobInstance);
default List<JobExecution> getJobExecutions(JobInstance jobInstance) {
return Collections.emptyList();
}
/**
* Return all {@link JobExecution}s for given {@link JobInstance}, sorted backwards by
* creation order (so the first element is the most recent).
* @param jobInstance parent {@link JobInstance} of the {@link JobExecution}s to find.
* @return {@link List} containing JobExecutions for the jobInstance.
* @since 5.0
* @deprecated since v6.0 and scheduled for removal in v6.2. Use
* {@link #getJobExecutions(JobInstance)}
*/
@Deprecated(since = "6.0", forRemoval = true)
default List<JobExecution> findJobExecutions(JobInstance jobInstance) {
return Collections.emptyList();
}
/**
* Find the last {@link JobExecution} that has been created for a given
@@ -167,6 +228,16 @@ public interface JobExplorer {
throw new UnsupportedOperationException();
}
/**
* @param jobName the name of the job that might have run
* @param jobParameters parameters identifying the {@link JobInstance}
* @return the last execution of job if exists, null otherwise
*/
@Nullable
default JobExecution getLastJobExecution(String jobName, JobParameters jobParameters) {
throw new UnsupportedOperationException();
}
/**
* Retrieve running job executions. The corresponding step executions may not be fully
* hydrated (for example, their execution context may be missing), depending on the
@@ -175,7 +246,9 @@ public interface JobExplorer {
* @param jobName The name of the job.
* @return the set of running executions for jobs with the specified name.
*/
Set<JobExecution> findRunningJobExecutions(@Nullable String jobName);
default Set<JobExecution> findRunningJobExecutions(@Nullable String jobName) {
return Collections.emptySet();
}
/*
* ===================================================================================
@@ -195,6 +268,27 @@ public interface JobExplorer {
* @see #getJobExecution(Long)
*/
@Nullable
StepExecution getStepExecution(@Nullable Long jobExecutionId, @Nullable Long stepExecutionId);
default StepExecution getStepExecution(@Nullable Long jobExecutionId, @Nullable Long stepExecutionId) {
throw new UnsupportedOperationException();
}
/**
* @param jobInstance {@link JobInstance} instance containing the step executions.
* @param stepName the name of the step execution that might have run.
* @return the last execution of step for the given job instance.
*/
@Nullable
default StepExecution getLastStepExecution(JobInstance jobInstance, String stepName) {
throw new UnsupportedOperationException();
}
/**
* @param jobInstance {@link JobInstance} instance containing the step executions.
* @param stepName the name of the step execution that might have run.
* @return the execution count of the step within the given job instance.
*/
default long getStepExecutionCount(JobInstance jobInstance, String stepName) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.batch.core.repository.dao.ExecutionContextDao;
import org.springframework.batch.core.repository.dao.JobExecutionDao;
import org.springframework.batch.core.repository.dao.JobInstanceDao;
import org.springframework.batch.core.repository.dao.StepExecutionDao;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.lang.Nullable;
import java.util.List;
@@ -49,20 +50,13 @@ import java.util.Set;
*/
public class SimpleJobExplorer implements JobExplorer {
private JobInstanceDao jobInstanceDao;
protected JobInstanceDao jobInstanceDao;
private JobExecutionDao jobExecutionDao;
protected JobExecutionDao jobExecutionDao;
private StepExecutionDao stepExecutionDao;
protected StepExecutionDao stepExecutionDao;
private ExecutionContextDao ecDao;
/**
* Provides a default constructor with low visibility in case you want to use
* aop:proxy-target-class="true" for the AOP interceptor.
*/
SimpleJobExplorer() {
}
protected ExecutionContextDao ecDao;
/**
* Constructor to initialize the job {@link SimpleJobExplorer}.
@@ -80,6 +74,79 @@ public class SimpleJobExplorer implements JobExplorer {
this.ecDao = ecDao;
}
/*
* ===================================================================================
* Job operations
* ===================================================================================
*/
@Override
public List<String> getJobNames() {
return jobInstanceDao.getJobNames();
}
/*
* ===================================================================================
* Job instance operations
* ===================================================================================
*/
@Override
@Deprecated(since = "6.0", forRemoval = true)
public boolean isJobInstanceExists(String jobName, JobParameters jobParameters) {
return jobInstanceDao.getJobInstance(jobName, jobParameters) != null;
}
/**
* @deprecated since v6.0 and scheduled for removal in v6.2. Use
* {@link #getJobInstances(String, int, int)} instead.
*/
@Deprecated(since = "6.0", forRemoval = true)
@Override
public List<JobInstance> findJobInstancesByJobName(String jobName, int start, int count) {
return getJobInstances(jobName, start, count);
}
@Override
@Deprecated(since = "6.0", forRemoval = true)
public List<JobInstance> findJobInstancesByName(String jobName, int start, int count) {
return this.jobInstanceDao.findJobInstancesByName(jobName, start, count);
}
@Nullable
@Override
public JobInstance getJobInstance(@Nullable Long instanceId) {
return jobInstanceDao.getJobInstance(instanceId);
}
@Nullable
@Override
public JobInstance getJobInstance(String jobName, JobParameters jobParameters) {
return jobInstanceDao.getJobInstance(jobName, jobParameters);
}
@Nullable
@Override
public JobInstance getLastJobInstance(String jobName) {
return jobInstanceDao.getLastJobInstance(jobName);
}
@Override
public List<JobInstance> getJobInstances(String jobName, int start, int count) {
return jobInstanceDao.getJobInstances(jobName, start, count);
}
@Override
public long getJobInstanceCount(@Nullable String jobName) throws NoSuchJobException {
return jobInstanceDao.getJobInstanceCount(jobName);
}
/*
* ===================================================================================
* Job execution operations
* ===================================================================================
*/
@Override
public List<JobExecution> getJobExecutions(JobInstance jobInstance) {
List<JobExecution> executions = jobExecutionDao.findJobExecutions(jobInstance);
@@ -105,6 +172,32 @@ public class SimpleJobExplorer implements JobExplorer {
return lastJobExecution;
}
@Deprecated(since = "6.0", forRemoval = true)
@Override
public List<JobExecution> findJobExecutions(JobInstance jobInstance) {
List<JobExecution> jobExecutions = this.jobExecutionDao.findJobExecutions(jobInstance);
for (JobExecution jobExecution : jobExecutions) {
this.stepExecutionDao.addStepExecutions(jobExecution);
}
return jobExecutions;
}
@Override
@Nullable
public JobExecution getLastJobExecution(String jobName, JobParameters jobParameters) {
JobInstance jobInstance = jobInstanceDao.getJobInstance(jobName, jobParameters);
if (jobInstance == null) {
return null;
}
JobExecution jobExecution = jobExecutionDao.getLastJobExecution(jobInstance);
if (jobExecution != null) {
jobExecution.setExecutionContext(ecDao.getExecutionContext(jobExecution));
stepExecutionDao.addStepExecutions(jobExecution);
}
return jobExecution;
}
@Override
public Set<JobExecution> findRunningJobExecutions(@Nullable String jobName) {
Set<JobExecution> executions = jobExecutionDao.findRunningJobExecutions(jobName);
@@ -134,6 +227,24 @@ public class SimpleJobExplorer implements JobExplorer {
return jobExecution;
}
/*
* Find all dependencies for a JobExecution, including JobInstance (which requires
* JobParameters) plus StepExecutions
*/
private void getJobExecutionDependencies(JobExecution jobExecution) {
JobInstance jobInstance = jobInstanceDao.getJobInstance(jobExecution);
stepExecutionDao.addStepExecutions(jobExecution);
jobExecution.setJobInstance(jobInstance);
jobExecution.setExecutionContext(ecDao.getExecutionContext(jobExecution));
}
/*
* ===================================================================================
* Step execution operations
* ===================================================================================
*/
@Nullable
@Override
public StepExecution getStepExecution(@Nullable Long jobExecutionId, @Nullable Long executionId) {
@@ -147,38 +258,40 @@ public class SimpleJobExplorer implements JobExplorer {
return stepExecution;
}
@Override
@Nullable
@Override
public JobInstance getJobInstance(@Nullable Long instanceId) {
return jobInstanceDao.getJobInstance(instanceId);
public StepExecution getLastStepExecution(JobInstance jobInstance, String stepName) {
StepExecution latest = stepExecutionDao.getLastStepExecution(jobInstance, stepName);
if (latest != null) {
ExecutionContext stepExecutionContext = ecDao.getExecutionContext(latest);
latest.setExecutionContext(stepExecutionContext);
ExecutionContext jobExecutionContext = ecDao.getExecutionContext(latest.getJobExecution());
latest.getJobExecution().setExecutionContext(jobExecutionContext);
}
return latest;
}
@Nullable
/**
* @return number of executions of the step within given job instance
*/
@Override
public JobInstance getJobInstance(String jobName, JobParameters jobParameters) {
return jobInstanceDao.getJobInstance(jobName, jobParameters);
public long getStepExecutionCount(JobInstance jobInstance, String stepName) {
return stepExecutionDao.countStepExecutions(jobInstance, stepName);
}
@Nullable
@Override
public JobInstance getLastJobInstance(String jobName) {
return jobInstanceDao.getLastJobInstance(jobName);
private void getStepExecutionDependencies(StepExecution stepExecution) {
if (stepExecution != null) {
stepExecution.setExecutionContext(ecDao.getExecutionContext(stepExecution));
}
}
@Override
public List<JobInstance> getJobInstances(String jobName, int start, int count) {
return jobInstanceDao.getJobInstances(jobName, start, count);
}
@Override
public List<String> getJobNames() {
return jobInstanceDao.getJobNames();
}
@Override
public long getJobInstanceCount(@Nullable String jobName) throws NoSuchJobException {
return jobInstanceDao.getJobInstanceCount(jobName);
}
/*
* ===================================================================================
* protected methods
* ===================================================================================
*/
/**
* @return instance of {@link JobInstanceDao}.
@@ -212,32 +325,4 @@ public class SimpleJobExplorer implements JobExplorer {
return ecDao;
}
/*
* Find all dependencies for a JobExecution, including JobInstance (which requires
* JobParameters) plus StepExecutions
*/
private void getJobExecutionDependencies(JobExecution jobExecution) {
JobInstance jobInstance = jobInstanceDao.getJobInstance(jobExecution);
stepExecutionDao.addStepExecutions(jobExecution);
jobExecution.setJobInstance(jobInstance);
jobExecution.setExecutionContext(ecDao.getExecutionContext(jobExecution));
}
private void getStepExecutionDependencies(StepExecution stepExecution) {
if (stepExecution != null) {
stepExecution.setExecutionContext(ecDao.getExecutionContext(stepExecution));
}
}
/**
* @deprecated since v6.0 and scheduled for removal in v6.2. Use
* {@link #getJobInstances(String, int, int)} instead.
*/
@Deprecated(forRemoval = true)
@Override
public List<JobInstance> findJobInstancesByJobName(String jobName, int start, int count) {
return getJobInstances(jobName, start, count);
}
}

View File

@@ -22,15 +22,13 @@ import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.repository.dao.JobExecutionDao;
import org.springframework.batch.core.repository.dao.JobInstanceDao;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Isolation;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* <p>
@@ -48,99 +46,7 @@ import java.util.List;
* @author Mahmoud Ben Hassine
* @author Parikshit Dutta
*/
public interface JobRepository {
/*
* ===================================================================================
* Read only operations
* ===================================================================================
*/
/**
* Retrieve the names of all job instances sorted alphabetically - i.e. jobs that have
* ever been executed.
* @return the names of all job instances
* @since 5.0
*/
default List<String> getJobNames() {
return Collections.emptyList();
}
/**
* Fetch the last job instances with the provided name, sorted backwards by primary
* key, using a 'like' criteria
* @param jobName {@link String} containing the name of the job.
* @param start int containing the offset of where list of job instances results
* should begin.
* @param count int containing the number of job instances to return.
* @return a list of {@link JobInstance} for the job name requested.
* @since 5.0
*/
default List<JobInstance> findJobInstancesByName(String jobName, int start, int count) {
return Collections.emptyList();
}
/**
* Check if an instance of this job already exists with the parameters provided.
* @param jobName the name of the job
* @param jobParameters the parameters to match
* @return true if a {@link JobInstance} already exists for this job name and job
* parameters
*/
boolean isJobInstanceExists(String jobName, JobParameters jobParameters);
/**
* @param jobName {@link String} name of the job.
* @param jobParameters {@link JobParameters} parameters for the job instance.
* @return the {@link JobInstance} with the given name and parameters, or
* {@code null}.
*
* @since 5.0
*/
@Nullable
default JobInstance getJobInstance(String jobName, JobParameters jobParameters) {
throw new UnsupportedOperationException();
}
/**
* Return all {@link JobExecution}s for given {@link JobInstance}, sorted backwards by
* creation order (so the first element is the most recent).
* @param jobInstance parent {@link JobInstance} of the {@link JobExecution}s to find.
* @return {@link List} containing JobExecutions for the jobInstance.
* @since 5.0
*/
default List<JobExecution> findJobExecutions(JobInstance jobInstance) {
return Collections.emptyList();
}
/**
* @param jobName the name of the job that might have run
* @param jobParameters parameters identifying the {@link JobInstance}
* @return the last execution of job if exists, null otherwise
*/
@Nullable
JobExecution getLastJobExecution(String jobName, JobParameters jobParameters);
/**
* @param jobInstance {@link JobInstance} instance containing the step executions.
* @param stepName the name of the step execution that might have run.
* @return the last execution of step for the given job instance.
*/
@Nullable
StepExecution getLastStepExecution(JobInstance jobInstance, String stepName);
/**
* @param jobInstance {@link JobInstance} instance containing the step executions.
* @param stepName the name of the step execution that might have run.
* @return the execution count of the step within the given job instance.
*/
long getStepExecutionCount(JobInstance jobInstance, String stepName);
/*
* ===================================================================================
* Write/Update operations
* ===================================================================================
*/
public interface JobRepository extends JobExplorer {
/**
* Create a new {@link JobInstance} with the name and job parameters provided.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2024 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.support.SimpleJobExplorer;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
@@ -32,7 +33,6 @@ import org.springframework.batch.core.repository.dao.JobExecutionDao;
import org.springframework.batch.core.repository.dao.JobInstanceDao;
import org.springframework.batch.core.repository.dao.StepExecutionDao;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.time.LocalDateTime;
@@ -60,56 +60,13 @@ import java.util.List;
* @see StepExecutionDao
*
*/
public class SimpleJobRepository implements JobRepository {
public class SimpleJobRepository extends SimpleJobExplorer implements JobRepository {
private static final Log logger = LogFactory.getLog(SimpleJobRepository.class);
private JobInstanceDao jobInstanceDao;
private JobExecutionDao jobExecutionDao;
private StepExecutionDao stepExecutionDao;
private ExecutionContextDao ecDao;
/**
* Provide default constructor with low visibility in case user wants to use
* aop:proxy-target-class="true" for AOP interceptor.
*/
SimpleJobRepository() {
}
public SimpleJobRepository(JobInstanceDao jobInstanceDao, JobExecutionDao jobExecutionDao,
StepExecutionDao stepExecutionDao, ExecutionContextDao ecDao) {
super();
this.jobInstanceDao = jobInstanceDao;
this.jobExecutionDao = jobExecutionDao;
this.stepExecutionDao = stepExecutionDao;
this.ecDao = ecDao;
}
@Override
public List<String> getJobNames() {
return this.jobInstanceDao.getJobNames();
}
@Override
public List<JobInstance> findJobInstancesByName(String jobName, int start, int count) {
return this.jobInstanceDao.findJobInstancesByName(jobName, start, count);
}
@Override
public List<JobExecution> findJobExecutions(JobInstance jobInstance) {
List<JobExecution> jobExecutions = this.jobExecutionDao.findJobExecutions(jobInstance);
for (JobExecution jobExecution : jobExecutions) {
this.stepExecutionDao.addStepExecutions(jobExecution);
}
return jobExecutions;
}
@Override
public boolean isJobInstanceExists(String jobName, JobParameters jobParameters) {
return jobInstanceDao.getJobInstance(jobName, jobParameters) != null;
super(jobInstanceDao, jobExecutionDao, stepExecutionDao, ecDao);
}
@Override
@@ -249,34 +206,6 @@ public class SimpleJobRepository implements JobRepository {
ecDao.updateExecutionContext(jobExecution);
}
@Override
public JobInstance getJobInstance(String jobName, JobParameters jobParameters) {
return jobInstanceDao.getJobInstance(jobName, jobParameters);
}
@Override
@Nullable
public StepExecution getLastStepExecution(JobInstance jobInstance, String stepName) {
StepExecution latest = stepExecutionDao.getLastStepExecution(jobInstance, stepName);
if (latest != null) {
ExecutionContext stepExecutionContext = ecDao.getExecutionContext(latest);
latest.setExecutionContext(stepExecutionContext);
ExecutionContext jobExecutionContext = ecDao.getExecutionContext(latest.getJobExecution());
latest.getJobExecution().setExecutionContext(jobExecutionContext);
}
return latest;
}
/**
* @return number of executions of the step within given job instance
*/
@Override
public long getStepExecutionCount(JobInstance jobInstance, String stepName) {
return stepExecutionDao.countStepExecutions(jobInstance, stepName);
}
/**
* Check to determine whether or not the JobExecution that is the parent of the
* provided StepExecution has been interrupted. If, after synchronizing the status
@@ -293,23 +222,6 @@ public class SimpleJobRepository implements JobRepository {
}
}
@Override
@Nullable
public JobExecution getLastJobExecution(String jobName, JobParameters jobParameters) {
JobInstance jobInstance = jobInstanceDao.getJobInstance(jobName, jobParameters);
if (jobInstance == null) {
return null;
}
JobExecution jobExecution = jobExecutionDao.getLastJobExecution(jobInstance);
if (jobExecution != null) {
jobExecution.setExecutionContext(ecDao.getExecutionContext(jobExecution));
stepExecutionDao.addStepExecutions(jobExecution);
}
return jobExecution;
}
@Override
public void deleteStepExecution(StepExecution stepExecution) {
this.ecDao.deleteExecutionContext(stepExecution);
@@ -328,7 +240,7 @@ public class SimpleJobRepository implements JobRepository {
@Override
public void deleteJobInstance(JobInstance jobInstance) {
List<JobExecution> jobExecutions = findJobExecutions(jobInstance);
List<JobExecution> jobExecutions = getJobExecutions(jobInstance);
for (JobExecution jobExecution : jobExecutions) {
deleteJobExecution(jobExecution);
}