From 82c106800d8dba07ebda7ff9fe116c7c44f3fd3f Mon Sep 17 00:00:00 2001
From: robokaso
Date: Tue, 20 Jan 2009 14:34:49 +0000
Subject: [PATCH] RESOLVED - BATCH-999: JobExecution ExecutionContext should
not be persisted by Step?
javadoc
---
.../batch/core/repository/JobRepository.java | 78 ++++++++++-------
.../dao/JdbcExecutionContextDao.java | 23 ++---
.../dao/MapExecutionContextDao.java | 20 +++--
.../support/SimpleJobRepository.java | 87 -------------------
4 files changed, 67 insertions(+), 141 deletions(-)
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java
index c4990b69a..1a16e2927 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/JobRepository.java
@@ -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;
/**
*
- * 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.
*
*
+ * @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
+ *
+ * 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.
+ *
+ *
+ *
+ * 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).
+ *
+ *
+ * @param jobName the name of the job that is to be executed
+ *
* @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 not
- * 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
*/
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcExecutionContextDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcExecutionContextDao.java
index 4e23dbc14..a460e62fb 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcExecutionContextDao.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcExecutionContextDao.java
@@ -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;
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapExecutionContextDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapExecutionContextDao.java
index 32cc1ec21..e6868d77f 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapExecutionContextDao.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapExecutionContextDao.java
@@ -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 contextsByStepExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
+ private static Map contextsByStepExecutionId = TransactionAwareProxyFactory
+ .createTransactionalMap();
+
+ private static Map contextsByJobExecutionId = TransactionAwareProxyFactory
+ .createTransactionalMap();
- private static Map 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) {
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java
index b3ddc2fa7..70d476573 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java
@@ -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;
}
- /**
- *
- * 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.
- *
- *
- *
- * 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.
- *
- *
- *
- * 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}:
- *
- * - First we find all jobs which match the given {@link JobParameters}
- * and job name
- * - What happens then depends on how many existing job instances we find:
- *
- * - If there are none, or the {@link Job} is marked restartable, then we
- * create a new {@link JobInstance}
- * - 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)
- * after it has been executed at least once.
- * - 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.
- *
- *
- *
- * 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).
- *
- *
- * @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 jobExecutions = jobExecutionDao.findJobExecutions(jobInstance);
List stepExecutions = new ArrayList(jobExecutions.size());