RESOLVED - BATCH-999: JobExecution ExecutionContext should not be persisted by Step?
javadoc
This commit is contained in:
@@ -22,42 +22,64 @@ 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.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Repository for storing batch {@link JobExecution} and {@link StepExecution}s.
|
||||
* Before using any methods, a {@link JobExecution} must first be obtained using
|
||||
* the createJobExecution method. Once a {@link JobExecution} is obtained, they
|
||||
* can be updated.
|
||||
* Repository responsible for persistence of batch metadata entities.
|
||||
* </p>
|
||||
*
|
||||
* @see JobInstance
|
||||
* @see JobExecution
|
||||
* @see StepExecution
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface JobRepository {
|
||||
|
||||
/**
|
||||
* Check if an instance of this job already exists with the parameters provided.
|
||||
* 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
|
||||
* @return true if a {@link JobInstance} already exists for this job name
|
||||
* and job parameters
|
||||
*/
|
||||
boolean isJobInstanceExists(String jobName, JobParameters jobParameters);
|
||||
|
||||
/**
|
||||
* Find or create a {@link JobExecution} for a given {@link Job} and
|
||||
* {@link JobParameters}. If the {@link Job} was already executed with these
|
||||
* {@link JobParameters}, its persisted values (including ID) will be
|
||||
* returned in a new {@link JobInstance}, associated with the
|
||||
* {@link JobExecution}. If no previous instance is found, the execution
|
||||
* will be associated with a new {@link JobInstance}
|
||||
* @param jobName the name of the job that is to be executed
|
||||
* <p>
|
||||
* Create a {@link JobExecution} for a given {@link Job} and
|
||||
* {@link JobParameters}. If matching {@link JobInstance} already exists,
|
||||
* the job must be restartable and it's last JobExecution must *not* be
|
||||
* completed. If matching {@link JobInstance} does not exist yet it will be
|
||||
* created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If this method is run in a transaction (as it normally would be) with
|
||||
* isolation level at {@link Isolation#REPEATABLE_READ} or better, then this
|
||||
* method should block if another transaction is already executing it (for
|
||||
* the same {@link JobParameters} and job name). The first transaction to
|
||||
* complete in this scenario obtains a valid {@link JobExecution}, and
|
||||
* others throw {@link JobExecutionAlreadyRunningException} (or timeout).
|
||||
* There are no such guarantees if the {@link JobInstanceDao} and
|
||||
* {@link JobExecutionDao} do not respect the transaction isolation levels
|
||||
* (e.g. if using a non-relational data-store, or if the platform does not
|
||||
* support the higher isolation levels).
|
||||
* </p>
|
||||
*
|
||||
* @param jobName the name of the job that is to be executed </p>
|
||||
*
|
||||
* @param jobParameters the runtime parameters for the job
|
||||
*
|
||||
* @return a valid job {@link JobExecution} for the arguments provided
|
||||
* @return a valid {@link JobExecution} for the arguments provided
|
||||
* @throws JobExecutionAlreadyRunningException if there is a
|
||||
* {@link JobExecution} already running for the job instance with the
|
||||
* provided job and parameters.
|
||||
@@ -68,11 +90,11 @@ public interface JobRepository {
|
||||
* found and was already completed successfully.
|
||||
*
|
||||
*/
|
||||
JobExecution createJobExecution(String jobName, JobParameters jobParameters) throws JobExecutionAlreadyRunningException,
|
||||
JobRestartException, JobInstanceAlreadyCompleteException;
|
||||
JobExecution createJobExecution(String jobName, JobParameters jobParameters)
|
||||
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException;
|
||||
|
||||
/**
|
||||
* Update the {@link JobExecution}.
|
||||
* Update the {@link JobExecution} (but not its {@link ExecutionContext}).
|
||||
*
|
||||
* Preconditions: {@link JobExecution} must contain a valid
|
||||
* {@link JobInstance} and be saved (have an id assigned).
|
||||
@@ -82,11 +104,10 @@ public interface JobRepository {
|
||||
void update(JobExecution jobExecution);
|
||||
|
||||
/**
|
||||
* Save the {@link StepExecution}. ID will be assigned - it is not permitted
|
||||
* that an ID be assigned before calling this method. Instead, it should be
|
||||
* left blank, to be assigned by a {@link JobRepository}. The
|
||||
* {@link ExecutionContext} of the {@link StepExecution} is <em>not</em>
|
||||
* saved: see {@link #updateExecutionContext(StepExecution)}.
|
||||
* Save the {@link StepExecution} and its {@link ExecutionContext}. ID will
|
||||
* be assigned - it is not permitted that an ID be assigned before calling
|
||||
* this method. Instead, it should be left blank, to be assigned by a
|
||||
* {@link JobRepository}.
|
||||
*
|
||||
* Preconditions: {@link StepExecution} must have a valid {@link Step}.
|
||||
*
|
||||
@@ -95,7 +116,7 @@ public interface JobRepository {
|
||||
void add(StepExecution stepExecution);
|
||||
|
||||
/**
|
||||
* Update the {@link StepExecution}.
|
||||
* Update the {@link StepExecution} (but not its {@link ExecutionContext}).
|
||||
*
|
||||
* Preconditions: {@link StepExecution} must be saved (have an id assigned).
|
||||
*
|
||||
@@ -104,11 +125,10 @@ public interface JobRepository {
|
||||
void update(StepExecution stepExecution);
|
||||
|
||||
/**
|
||||
* Persist the {@link ExecutionContext} of the given {@link StepExecution}
|
||||
* and enclosing {@link JobExecution}.
|
||||
* Persist the updated {@link ExecutionContext}s of the given
|
||||
* {@link StepExecution} and corresponding {@link JobExecution}.
|
||||
*
|
||||
* @param stepExecution the {@link StepExecution} containing the
|
||||
* {@link ExecutionContext} to be saved.
|
||||
* @param stepExecution
|
||||
*/
|
||||
void updateExecutionContext(StepExecution stepExecution);
|
||||
|
||||
@@ -125,7 +145,7 @@ public interface JobRepository {
|
||||
int getStepExecutionCount(JobInstance jobInstance, String stepName);
|
||||
|
||||
/**
|
||||
* @param jobName the name of the job that might have run
|
||||
* @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
|
||||
*/
|
||||
|
||||
@@ -70,10 +70,6 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
|
||||
|
||||
private ExecutionContextStringSerializer serializer;
|
||||
|
||||
/**
|
||||
* @param jobExecution
|
||||
* @return execution context associated with the given jobExecution.
|
||||
*/
|
||||
public ExecutionContext getExecutionContext(JobExecution jobExecution) {
|
||||
Long executionId = jobExecution.getId();
|
||||
Assert.notNull(executionId, "ExecutionId must not be null.");
|
||||
@@ -88,10 +84,6 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stepExecution
|
||||
* @return execution context associated with the given stepExecution.
|
||||
*/
|
||||
public ExecutionContext getExecutionContext(StepExecution stepExecution) {
|
||||
Long executionId = stepExecution.getId();
|
||||
Assert.notNull(executionId, "ExecutionId must not be null.");
|
||||
@@ -106,11 +98,6 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist or update the execution context associated with the given
|
||||
* jobExecution
|
||||
* @param jobExecution
|
||||
*/
|
||||
public void updateExecutionContext(final JobExecution jobExecution) {
|
||||
Long executionId = jobExecution.getId();
|
||||
ExecutionContext executionContext = jobExecution.getExecutionContext();
|
||||
@@ -122,11 +109,6 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
|
||||
persistSerializedContext(executionId, serializedContext, UPDATE_JOB_EXECUTION_CONTEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist or update the execution context associated with the given
|
||||
* stepExecution
|
||||
* @param stepExecution
|
||||
*/
|
||||
public void updateExecutionContext(final StepExecution stepExecution) {
|
||||
|
||||
Long executionId = stepExecution.getId();
|
||||
@@ -173,6 +155,11 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
|
||||
((XStreamExecutionContextStringSerializer) serializer).afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param executionId
|
||||
* @param serializedContext
|
||||
* @param sql with parameters (shortContext, longContext, executionId)
|
||||
*/
|
||||
private void persistSerializedContext(final Long executionId, String serializedContext, String sql) {
|
||||
|
||||
final String shortContext;
|
||||
|
||||
@@ -24,22 +24,29 @@ import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
|
||||
/**
|
||||
* In-memory implementation of {@link ExecutionContextDao} backed by static
|
||||
* maps.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MapExecutionContextDao implements ExecutionContextDao {
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByStepExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
private static Map<Long, ExecutionContext> contextsByStepExecutionId = TransactionAwareProxyFactory
|
||||
.createTransactionalMap();
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByJobExecutionId = TransactionAwareProxyFactory
|
||||
.createTransactionalMap();
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByJobExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
|
||||
public static void clear() {
|
||||
contextsByJobExecutionId.clear();
|
||||
contextsByStepExecutionId.clear();
|
||||
}
|
||||
|
||||
|
||||
private static ExecutionContext copy(ExecutionContext original) {
|
||||
return (ExecutionContext) SerializationUtils.deserialize(SerializationUtils.serialize(original));
|
||||
}
|
||||
|
||||
|
||||
public ExecutionContext getExecutionContext(StepExecution stepExecution) {
|
||||
return copy(contextsByStepExecutionId.get(stepExecution.getId()));
|
||||
}
|
||||
@@ -47,14 +54,13 @@ public class MapExecutionContextDao implements ExecutionContextDao {
|
||||
public void updateExecutionContext(StepExecution stepExecution) {
|
||||
contextsByStepExecutionId.put(stepExecution.getId(), copy(stepExecution.getExecutionContext()));
|
||||
}
|
||||
|
||||
|
||||
public ExecutionContext getExecutionContext(JobExecution jobExecution) {
|
||||
return copy(contextsByJobExecutionId.get(jobExecution.getId()));
|
||||
}
|
||||
|
||||
public void updateExecutionContext(JobExecution jobExecution) {
|
||||
contextsByJobExecutionId.put(jobExecution.getId(), copy(jobExecution.getExecutionContext()));
|
||||
|
||||
}
|
||||
|
||||
public void saveExecutionContext(JobExecution jobExecution) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
@@ -35,7 +34,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.transaction.annotation.Isolation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -81,71 +79,10 @@ public class SimpleJobRepository implements JobRepository {
|
||||
this.ecDao = ecDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JobRepository#isJobInstanceExists(String, JobParameters)
|
||||
*/
|
||||
public boolean isJobInstanceExists(String jobName, JobParameters jobParameters) {
|
||||
return jobInstanceDao.getJobInstance(jobName, jobParameters) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Create a {@link JobExecution} based on the passed in {@link Job} and
|
||||
* {@link JobParameters}. However, unique identification of a job can only
|
||||
* come from the database, and therefore must come from JobDao by either
|
||||
* creating a new job instance or finding an existing one, which will ensure
|
||||
* that the id of the job instance is populated with the correct value.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* There are two ways in which the method determines if a job should be
|
||||
* created or an existing one should be returned. The first is
|
||||
* restartability. The {@link Job} restartable property will be checked
|
||||
* first. If it is false, a new job will be created, regardless of whether
|
||||
* or not one exists. If it is true, the {@link JobInstanceDao} will be
|
||||
* checked to determine if the job already exists, if it does, it's steps
|
||||
* will be populated (there must be at least 1) and a new
|
||||
* {@link JobExecution} will be returned. If no job instance is found, a new
|
||||
* one will be created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A check is made to see if any job executions are already running, and an
|
||||
* exception will be thrown if one is detected. To detect a running job
|
||||
* execution we use the {@link JobExecutionDao}:
|
||||
* <ol>
|
||||
* <li>First we find all jobs which match the given {@link JobParameters}
|
||||
* and job name</li>
|
||||
* <li>What happens then depends on how many existing job instances we find:
|
||||
* <ul>
|
||||
* <li>If there are none, or the {@link Job} is marked restartable, then we
|
||||
* create a new {@link JobInstance}</li>
|
||||
* <li>If there is more than one and the {@link Job} is not marked as
|
||||
* restartable, it is an error. This could be caused by a job whose
|
||||
* restartable flag has changed to be more strict (true not false)
|
||||
* <em>after</em> it has been executed at least once.</li>
|
||||
* <li>If there is precisely one existing {@link JobInstance} then we check
|
||||
* the {@link JobExecution} instances for that job, and if any of them tells
|
||||
* us it is running (see {@link JobExecution#isRunning()}) then it is an
|
||||
* error.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
* If this method is run in a transaction (as it normally would be) with
|
||||
* isolation level at {@link Isolation#REPEATABLE_READ} or better, then this
|
||||
* method should block if another transaction is already executing it (for
|
||||
* the same {@link JobParameters} and job name). The first transaction to
|
||||
* complete in this scenario obtains a valid {@link JobExecution}, and
|
||||
* others throw {@link JobExecutionAlreadyRunningException} (or timeout).
|
||||
* There are no such guarantees if the {@link JobInstanceDao} and
|
||||
* {@link JobExecutionDao} do not respect the transaction isolation levels
|
||||
* (e.g. if using a non-relational data-store, or if the platform does not
|
||||
* support the higher isolation levels).
|
||||
* </p>
|
||||
*
|
||||
* @see JobRepository#createJobExecution(String, JobParameters)
|
||||
*
|
||||
*/
|
||||
public JobExecution createJobExecution(String jobName, JobParameters jobParameters)
|
||||
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
|
||||
|
||||
@@ -202,15 +139,6 @@ public class SimpleJobRepository implements JobRepository {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a JobExecution. A JobExecution is considered one 'execution' of a
|
||||
* particular job. Therefore, it must have it's jobId field set before it is
|
||||
* passed into this method. It also has it's own unique identifier, because
|
||||
* it must be updatable separately.
|
||||
*
|
||||
* @param jobExecution to be stored.
|
||||
* @throws IllegalArgumentException if jobExecution is null.
|
||||
*/
|
||||
public void update(JobExecution jobExecution) {
|
||||
|
||||
Assert.notNull(jobExecution, "JobExecution cannot be null.");
|
||||
@@ -221,12 +149,6 @@ public class SimpleJobRepository implements JobRepository {
|
||||
jobExecutionDao.updateJobExecution(jobExecution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the {@link StepExecution}.
|
||||
*
|
||||
* Preconditions: step name must be given and associated
|
||||
* {@link JobExecution} must already be saved (have an id assigned).
|
||||
*/
|
||||
public void add(StepExecution stepExecution) {
|
||||
validateStepExecution(stepExecution);
|
||||
|
||||
@@ -235,12 +157,6 @@ public class SimpleJobRepository implements JobRepository {
|
||||
ecDao.saveExecutionContext(stepExecution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the {@link StepExecution}.
|
||||
*
|
||||
* Preconditions: step name must be given and associated
|
||||
* {@link JobExecution} must already be saved (have an id assigned).
|
||||
*/
|
||||
public void update(StepExecution stepExecution) {
|
||||
validateStepExecution(stepExecution);
|
||||
Assert.notNull(stepExecution.getId(), "StepExecution must already be saved (have an id assigned)");
|
||||
@@ -261,9 +177,6 @@ public class SimpleJobRepository implements JobRepository {
|
||||
ecDao.updateExecutionContext(stepExecution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last execution of the step within given job instance
|
||||
*/
|
||||
public StepExecution getLastStepExecution(JobInstance jobInstance, String stepName) {
|
||||
List<JobExecution> jobExecutions = jobExecutionDao.findJobExecutions(jobInstance);
|
||||
List<StepExecution> stepExecutions = new ArrayList<StepExecution>(jobExecutions.size());
|
||||
|
||||
Reference in New Issue
Block a user