diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapJobExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapJobExecutionDao.java index 138490e86..ae1464fc4 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapJobExecutionDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/MapJobExecutionDao.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.lang.SerializationUtils; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.support.transaction.TransactionAwareProxyFactory; @@ -18,27 +19,32 @@ import org.springframework.util.Assert; */ public class MapJobExecutionDao implements JobExecutionDao { - private static Map executionsById = TransactionAwareProxyFactory.createTransactionalMap(); - + private static Map executionsById = TransactionAwareProxyFactory.createTransactionalMap(); + private static long currentId = 0; public static void clear() { executionsById.clear(); } + private static JobExecution copy(JobExecution original) { + JobExecution copy = (JobExecution) SerializationUtils.deserialize(SerializationUtils.serialize(original)); + return copy; + } + public void saveJobExecution(JobExecution jobExecution) { Assert.isTrue(jobExecution.getId() == null); Long newId = new Long(currentId++); jobExecution.setId(newId); jobExecution.incrementVersion(); - executionsById.put(newId, jobExecution); + executionsById.put(newId, copy(jobExecution)); } public List findJobExecutions(JobInstance jobInstance) { List executions = new ArrayList(); for (JobExecution exec : executionsById.values()) { if (exec.getJobInstance().equals(jobInstance)) { - executions.add(exec); + executions.add(copy(exec)); } } Collections.sort(executions, new Comparator() { @@ -47,9 +53,11 @@ public class MapJobExecutionDao implements JobExecutionDao { long result = (e1.getId() - e2.getId()); if (result > 0) { return -1; - } else if (result < 0) { + } + else if (result < 0) { return 1; - } else { + } + else { return 0; } } @@ -62,7 +70,7 @@ public class MapJobExecutionDao implements JobExecutionDao { Assert.notNull(id, "JobExecution is expected to have an id (should be saved already)"); Assert.notNull(executionsById.get(id), "JobExecution must already be saved"); jobExecution.incrementVersion(); - executionsById.put(id, jobExecution); + executionsById.put(id, copy(jobExecution)); } public JobExecution getLastJobExecution(JobInstance jobInstance) { @@ -78,11 +86,14 @@ public class MapJobExecutionDao implements JobExecutionDao { lastExec = exec; } } - return lastExec; + return copy(lastExec); } - /* (non-Javadoc) - * @see org.springframework.batch.core.repository.dao.JobExecutionDao#findRunningJobExecutions(java.lang.String) + /* + * (non-Javadoc) + * + * @seeorg.springframework.batch.core.repository.dao.JobExecutionDao# + * findRunningJobExecutions(java.lang.String) */ public Set findRunningJobExecutions(String jobName) { Set result = new HashSet(); @@ -90,16 +101,20 @@ public class MapJobExecutionDao implements JobExecutionDao { if (!exec.getJobInstance().getJobName().equals(jobName) || !exec.isRunning()) { continue; } - result.add(exec); + result.add(copy(exec)); } return result; } - /* (non-Javadoc) - * @see org.springframework.batch.core.repository.dao.JobExecutionDao#getJobExecution(java.lang.Long) + /* + * (non-Javadoc) + * + * @see + * org.springframework.batch.core.repository.dao.JobExecutionDao#getJobExecution + * (java.lang.Long) */ public JobExecution getJobExecution(Long executionId) { - return executionsById.get(executionId); + return copy(executionsById.get(executionId)); } public void synchronizeStatus(JobExecution jobExecution) { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/MapJobExecutionDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/MapJobExecutionDaoTests.java index a731f003e..6015ed0ee 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/MapJobExecutionDaoTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/MapJobExecutionDaoTests.java @@ -1,7 +1,15 @@ package org.springframework.batch.core.repository.dao; +import java.util.Date; + +import org.junit.Test; import org.junit.runner.RunWith; import org.junit.internal.runners.JUnit4ClassRunner; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobInstance; +import org.springframework.batch.core.JobParameters; + +import static org.junit.Assert.*; @RunWith(JUnit4ClassRunner.class) public class MapJobExecutionDaoTests extends AbstractJobExecutionDaoTests { @@ -12,4 +20,21 @@ public class MapJobExecutionDaoTests extends AbstractJobExecutionDaoTests { return new MapJobExecutionDao(); } + /** + * Modifications to saved entity do not affect the persisted object. + */ + @Test + public void testPersistentCopy() { + JobExecutionDao tested = new MapJobExecutionDao(); + JobExecution jobExecution = new JobExecution(new JobInstance((long) 1, new JobParameters(), "mapJob")); + + assertNull(jobExecution.getStartTime()); + tested.saveJobExecution(jobExecution); + jobExecution.setStartTime(new Date()); + + JobExecution retrieved = tested.getJobExecution(jobExecution.getId()); + assertNull(retrieved.getStartTime()); + + } + }