IN PROGRESS - issue BATCH-7: Remove transaction synchronization and state management from input/output sources (formerly buffering)

http://jira.springframework.org/browse/BATCH-7

Lazy-initialisation solution to the "who's in step scope" issue
This commit is contained in:
dsyer
2008-01-31 09:54:38 +00:00
parent 9d1ebb42c7
commit 15cffeb925
66 changed files with 653 additions and 761 deletions

View File

@@ -303,8 +303,8 @@ public class SimpleJobRepository implements JobRepository {
Step step = (Step) i.next();
StepInstance stepInstance = stepDao.createStep(job, step.getName());
// Ensure valid restart data is being returned.
if (stepInstance.getRestartData() == null || stepInstance.getRestartData().getProperties() == null) {
stepInstance.setRestartData(new GenericStreamContext(new Properties()));
if (stepInstance.getStreamContext() == null || stepInstance.getStreamContext().getProperties() == null) {
stepInstance.setStreamContext(new GenericStreamContext(new Properties()));
}
stepInstances.add(stepInstance);
}
@@ -326,8 +326,8 @@ public class SimpleJobRepository implements JobRepository {
step.setStepExecutionCount(stepDao.getStepExecutionCount(step));
// Ensure valid restart data is being returned.
if (step.getRestartData() == null || step.getRestartData().getProperties() == null) {
step.setRestartData(new GenericStreamContext(new Properties()));
if (step.getStreamContext() == null || step.getStreamContext().getProperties() == null) {
step.setStreamContext(new GenericStreamContext(new Properties()));
}
stepInstances.add(step);
}

View File

@@ -161,7 +161,7 @@ public class JdbcStepDao implements StepDao, InitializingBean {
StepInstance step = new StepInstance(new Long(rs.getLong(1)));
step.setStatus(BatchStatus.getStatus(rs.getString(2)));
step.setRestartData(new GenericStreamContext(PropertiesConverter.stringToProperties(rs.getString(3))));
step.setStreamContext(new GenericStreamContext(PropertiesConverter.stringToProperties(rs.getString(3))));
return step;
}
@@ -243,7 +243,7 @@ public class JdbcStepDao implements StepDao, InitializingBean {
StepInstance step = new StepInstance(job, rs.getString(2), new Long(rs.getLong(1)));
String status = rs.getString(3);
step.setStatus(BatchStatus.getStatus(status));
step.setRestartData(new GenericStreamContext(PropertiesConverter.stringToProperties(rs.getString(3))));
step.setStreamContext(new GenericStreamContext(PropertiesConverter.stringToProperties(rs.getString(3))));
return step;
}
};
@@ -422,7 +422,7 @@ public class JdbcStepDao implements StepDao, InitializingBean {
Assert.notNull(step.getId(), "Step Id cannot be null.");
Properties restartProps = null;
StreamContext streamContext = step.getRestartData();
StreamContext streamContext = step.getStreamContext();
if (streamContext != null) {
restartProps = streamContext.getProperties();
}

View File

@@ -80,7 +80,7 @@ public class MapStepDao implements StepDao {
return new ArrayList(steps);
}
public StreamContext getRestartData(Long stepId) {
public StreamContext getStreamContext(Long stepId) {
return (StreamContext) restartsById.get(stepId);
}

View File

@@ -25,6 +25,10 @@ import java.util.Properties;
import java.util.Set;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.BatchCriticalException;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.StreamManager;
import org.springframework.batch.repeat.context.SynchronizedAttributeAccessor;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.batch.statistics.StatisticsService;
@@ -35,53 +39,72 @@ import org.springframework.batch.statistics.StatisticsService;
* @author Dave Syer
*
*/
public class SimpleStepContext extends SynchronizedAttributeAccessor implements
StepContext, StatisticsProvider {
public class SimpleStepContext extends SynchronizedAttributeAccessor implements StepContext {
private Map callbacks = new HashMap();
private StepContext parent;
private StepExecution stepExecution;
private StatisticsService statisticsService;
private StreamManager streamManager;
private StreamContext streamContext;
/**
* Default constructor.
*/
public SimpleStepContext(StepExecution stepExecution) {
this(stepExecution, null, null);
this(stepExecution, null, null, null);
}
/**
* Default constructor.
*/
public SimpleStepContext(StepExecution stepExecution, StepContext parent) {
this(stepExecution, parent, null);
this(stepExecution, parent, null, null);
}
/**
* @param object
*/
public SimpleStepContext(StepExecution stepExecution, StepContext parent, StatisticsService statisticsService) {
public SimpleStepContext(StepExecution stepExecution, StepContext parent, StatisticsService statisticsService,
StreamManager streamManager) {
super();
this.parent = parent;
this.statisticsService = statisticsService;
this.streamManager = streamManager;
this.stepExecution = stepExecution;
}
/* (non-Javadoc)
* @see org.springframework.batch.repeat.context.SynchronizedAttributeAccessor#setAttribute(java.lang.String, java.lang.Object)
/*
* (non-Javadoc)
* @see org.springframework.batch.repeat.context.SynchronizedAttributeAccessor#setAttribute(java.lang.String,
* java.lang.Object)
*/
public void setAttribute(String name, Object value) {
super.setAttribute(name, value);
if (statisticsService!=null && (value instanceof StatisticsProvider)) {
if (statisticsService != null && (value instanceof StatisticsProvider)) {
statisticsService.register(this, (StatisticsProvider) value);
}
if (streamManager != null && (value instanceof ItemStream)) {
ItemStream stream = (ItemStream) value;
streamManager.register(this, stream);
stream.open();
if (streamContext != null) {
stream.restoreFrom(streamContext);
}
}
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.batch.statistics.StatisticsProvider#getStatistics()
*/
public Properties getStatistics() {
if (statisticsService==null) {
if (statisticsService == null) {
return new Properties();
}
return statisticsService.getStatistics(this);
@@ -100,13 +123,13 @@ public class SimpleStepContext extends SynchronizedAttributeAccessor implements
* (non-Javadoc)
*
* @see org.springframework.batch.repeat.RepeatContext#registerDestructionCallback(java.lang.String,
* java.lang.Runnable)
* java.lang.Runnable)
*/
/*
* (non-Javadoc)
*
* @see org.springframework.batch.execution.scope.StepContext#registerDestructionCallback(java.lang.String,
* java.lang.Runnable)
* java.lang.Runnable)
*/
public void registerDestructionCallback(String name, Runnable callback) {
synchronized (callbacks) {
@@ -119,13 +142,23 @@ public class SimpleStepContext extends SynchronizedAttributeAccessor implements
}
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.batch.execution.scope.StepContext#close()
*/
public void close() {
List errors = new ArrayList();
try {
if (streamManager != null) {
streamManager.close(this);
}
}
catch (Exception t) {
errors.add(t);
}
Set copy;
synchronized (callbacks) {
@@ -152,7 +185,8 @@ public class SimpleStepContext extends SynchronizedAttributeAccessor implements
*/
try {
callback.run();
} catch (RuntimeException t) {
}
catch (RuntimeException t) {
errors.add(t);
}
}
@@ -163,7 +197,14 @@ public class SimpleStepContext extends SynchronizedAttributeAccessor implements
return;
}
throw (RuntimeException) errors.get(0);
Exception error = (Exception) errors.get(0);
if (error instanceof RuntimeException) {
throw (RuntimeException) error;
}
else {
throw new BatchCriticalException("Could not close step context, rethrowing first of " + errors.size()
+ " execptions.", error);
}
}
/*
@@ -175,4 +216,20 @@ public class SimpleStepContext extends SynchronizedAttributeAccessor implements
return stepExecution;
}
/*
* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#getStreamContext()
*/
public StreamContext getStreamContext() {
return streamManager.getStreamContext(this);
}
/*
* (non-Javadoc)
* @see org.springframework.batch.execution.scope.StepContext#setInitialStreamContext(org.springframework.batch.item.StreamContext)
*/
public void setInitialStreamContext(StreamContext streamContext) {
this.streamContext = streamContext;
}
}

View File

@@ -16,6 +16,9 @@
package org.springframework.batch.execution.scope;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.StreamContextProvider;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.core.AttributeAccessor;
@@ -25,7 +28,7 @@ import org.springframework.core.AttributeAccessor;
* @author Dave Syer
*
*/
public interface StepContext extends AttributeAccessor, StatisticsProvider {
public interface StepContext extends AttributeAccessor, StreamContextProvider, StatisticsProvider {
/**
* Accessor for the {@link StepExecution} associated with the currently
@@ -34,7 +37,7 @@ public interface StepContext extends AttributeAccessor, StatisticsProvider {
* @return the {@link StepExecution} associated with the current step
*/
StepExecution getStepExecution();
/**
* Accessor for the parent context.
*
@@ -51,4 +54,14 @@ public interface StepContext extends AttributeAccessor, StatisticsProvider {
* Clean up any resources held during the context of the step.
*/
void close();
/**
* Provide the stream context that will be needed to restore
* {@link ItemStream} instances. If this is not set the streams will simply
* not be initialised and repositioned for restart (which is sometimes
* desirable).
*
* @param streamContext
*/
void setInitialStreamContext(StreamContext streamContext);
}

View File

@@ -34,8 +34,8 @@ import org.springframework.batch.execution.scope.StepScope;
import org.springframework.batch.execution.scope.StepSynchronizationManager;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.io.exception.BatchCriticalException;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.SimpleStreamManager;
import org.springframework.batch.item.stream.StreamManager;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
@@ -97,6 +97,8 @@ public class SimpleStepExecutor {
private AbstractStep step;
private StreamManager streamManager = new SimpleStreamManager();
/**
* Package private constructor so the factory can create a the executor.
*/
@@ -118,6 +120,20 @@ public class SimpleStepExecutor {
this.statisticsService = statisticsService;
}
/**
* Public setter for the {@link StreamManager}. This will be used to create
* the {@link StepContext}, and hence any component that is a
* {@link StatisticsProvider} and in step scope will be registered with the
* service. The {@link StepContext} is then a source of aggregate statistics
* for the step.
*
* @param streamManager the {@link StreamManager} to set. Default is a
* {@link SimpleStreamManager}.
*/
public void setStreamManager(StreamManager streamManager) {
this.streamManager = streamManager;
}
/**
* Injected strategy for transaction management
* @param transactionManager
@@ -173,8 +189,7 @@ public class SimpleStepExecutor {
* execution
* @see StepExecutor#execute(StepExecution)
*/
public void execute(final StepExecution stepExecution) throws BatchCriticalException,
StepInterruptedException {
public void execute(final StepExecution stepExecution) throws BatchCriticalException, StepInterruptedException {
final StepInstance stepInstance = stepExecution.getStep();
boolean isRestart = stepInstance.getStepExecutionCount() > 0 ? true : false;
@@ -182,24 +197,25 @@ public class SimpleStepExecutor {
ExitStatus status = ExitStatus.FAILED;
StepContext parentStepScopeContext = StepSynchronizationManager.getContext();
final StepContext stepScopeContext = new SimpleStepContext(stepExecution, parentStepScopeContext,
statisticsService);
StepSynchronizationManager.register(stepScopeContext);
StepContext parentStepContext = StepSynchronizationManager.getContext();
final StepContext stepContext = new SimpleStepContext(stepExecution, parentStepContext, statisticsService,
streamManager);
StepSynchronizationManager.register(stepContext);
// Add the job identifier so that it can be used to identify
// the conversation in StepScope
stepScopeContext.setAttribute(StepScope.ID_KEY, stepExecution.getJobExecution().getId());
stepContext.setAttribute(StepScope.ID_KEY, stepExecution.getJobExecution().getId());
final boolean saveRestartData = step.isSaveStreamContext();
if (saveRestartData && isRestart) {
stepContext.setInitialStreamContext(stepInstance.getStreamContext());
}
try {
stepExecution.setStartTime(new Date(System.currentTimeMillis()));
updateStatus(stepExecution, BatchStatus.STARTED);
final boolean saveRestartData = step.isSaveRestartData();
if (saveRestartData && isRestart) {
restoreFromRestartData(tasklet, stepInstance.getRestartData());
}
status = stepOperations.iterate(new RepeatCallback() {
public ExitStatus doInIteration(final RepeatContext context) throws Exception {
@@ -229,8 +245,8 @@ public class SimpleStepExecutor {
// TODO: check that stepExecution can
// aggregate these contributions if they
// come in asnchronously.
Properties statistics = stepScopeContext.getStatistics();
// come in asynchronously.
Properties statistics = stepContext.getStatistics();
contribution.setStatistics(statistics);
contribution.incrementCommitCount();
// Apply the contribution to the step
@@ -238,7 +254,7 @@ public class SimpleStepExecutor {
stepExecution.apply(contribution);
if (saveRestartData) {
stepInstance.setRestartData(getRestartData(tasklet));
stepInstance.setStreamContext(stepContext.getStreamContext());
jobRepository.update(stepInstance);
}
jobRepository.saveOrUpdate(stepExecution);
@@ -388,26 +404,6 @@ public class SimpleStepExecutor {
return exitStatus;
}
/**
* @param tasklet
* @return restart data from the {@link Tasklet} if it is
* {@link ItemStream}
*/
private StreamContext getRestartData(Tasklet tasklet) {
if (tasklet instanceof ItemStream) {
return ((ItemStream) tasklet).getRestartData();
}
else {
return null;
}
}
private void restoreFromRestartData(Tasklet tasklet, StreamContext streamContext) {
if (tasklet instanceof ItemStream && streamContext != null) {
((ItemStream) tasklet).restoreFrom(streamContext);
}
}
/**
* Setter for the {@link StepInterruptionPolicy}. The policy is used to
* check whether an external request has been made to interrupt the job

View File

@@ -16,14 +16,11 @@
package org.springframework.batch.execution.tasklet;
import java.util.Properties;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.support.PropertiesConverter;
import org.springframework.batch.item.StreamException;
/**
* An extension of {@link ItemOrientedTasklet} that delegates calls to
@@ -40,95 +37,30 @@ import org.springframework.batch.support.PropertiesConverter;
public class RestartableItemOrientedTasklet extends ItemOrientedTasklet implements ItemStream {
/**
* @see ItemStream#getRestartData()
* @see ItemStream#getStreamContext()
*/
public StreamContext getRestartData() {
StreamContext itemProviderRestartData = null;
StreamContext itemProcessorRestartData = null;
if (itemProvider instanceof ItemStream) {
itemProviderRestartData = ((ItemStream) itemProvider).getRestartData();
}
if (itemWriter instanceof ItemStream) {
itemProcessorRestartData = ((ItemStream) itemWriter).getRestartData();
}
RestartableItemOrientedTaskletRestartData restartData = new RestartableItemOrientedTaskletRestartData(itemProviderRestartData, itemProcessorRestartData);
return restartData;
public StreamContext getStreamContext() {
throw new UnsupportedOperationException("This class is not used");
}
/**
* @see ItemStream#restoreFrom(StreamContext)
*/
public void restoreFrom(StreamContext data) {
if (data == null || data.getProperties() == null)
return;
RestartableItemOrientedTaskletRestartData moduleRestartData;
if (data instanceof RestartableItemOrientedTaskletRestartData) {
moduleRestartData = (RestartableItemOrientedTaskletRestartData) data;
}
else {
moduleRestartData = new RestartableItemOrientedTaskletRestartData(data.getProperties());
}
if (itemProvider instanceof ItemStream) {
((ItemStream) itemProvider).restoreFrom(moduleRestartData.readerData);
}
if (itemWriter instanceof ItemStream) {
((ItemStream) itemWriter).restoreFrom(moduleRestartData.writerData);
}
}
private class RestartableItemOrientedTaskletRestartData implements StreamContext {
private static final String READER_KEY = "DATA_PROVIDER";
private static final String WRITER_KEY = "DATA_PROCESSOR";
private StreamContext readerData;
private StreamContext writerData;
public RestartableItemOrientedTaskletRestartData(StreamContext providerData, StreamContext writerData) {
this.readerData = providerData;
this.writerData = writerData;
}
public RestartableItemOrientedTaskletRestartData(Properties data) {
readerData = new GenericStreamContext(PropertiesConverter
.stringToProperties(data.getProperty(READER_KEY)));
writerData = new GenericStreamContext(PropertiesConverter.stringToProperties(data
.getProperty(WRITER_KEY)));
}
public Properties getProperties() {
Properties props = new Properties();
if (readerData != null) {
props.setProperty(READER_KEY, PropertiesConverter.propertiesToString(readerData.getProperties()));
}
if (writerData != null) {
props.setProperty(WRITER_KEY, PropertiesConverter.propertiesToString(writerData.getProperties()));
}
return props;
}
throw new UnsupportedOperationException("This class is not used");
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#open()
*/
public void open() throws Exception {
public void open() throws StreamException {
throw new UnsupportedOperationException("Not implemented.");
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#close()
*/
public void close() throws Exception {
public void close() throws StreamException {
throw new UnsupportedOperationException("Not implemented.");
}
}

View File

@@ -405,10 +405,10 @@ public class SimpleJobRepositoryTests extends TestCase {
jobDao.createJobInstance(jobConfiguration.getName(), jobParameters);
jobDaoControl.setReturnValue(databaseJob);
stepDao.createStep(databaseJob, "TestStep1");
databaseStep1.setRestartData(null);
databaseStep1.setStreamContext(null);
stepDaoControl.setReturnValue(databaseStep1);
stepDao.createStep(databaseJob, "TestStep2");
databaseStep2.setRestartData(new GenericStreamContext(null));
databaseStep2.setStreamContext(new GenericStreamContext(null));
stepDaoControl.setReturnValue(databaseStep2);
jobDao.save(new JobExecution(databaseJob));
jobDaoControl.setMatcher(new ArgumentsMatcher(){
@@ -426,10 +426,10 @@ public class SimpleJobRepositoryTests extends TestCase {
Iterator it = jobSteps.iterator();
StepInstance step = (StepInstance) it.next();
assertTrue(step.equals(databaseStep1));
assertTrue(step.getRestartData().getProperties().isEmpty());
assertTrue(step.getStreamContext().getProperties().isEmpty());
step = (StepInstance) it.next();
assertTrue(step.equals(databaseStep2));
assertTrue(step.getRestartData().getProperties().isEmpty());
assertTrue(step.getStreamContext().getProperties().isEmpty());
}
public void testFindStepsFixesInvalidRestartData() throws Exception{
@@ -438,12 +438,12 @@ public class SimpleJobRepositoryTests extends TestCase {
jobs.add(databaseJob);
jobDaoControl.setReturnValue(jobs);
stepDao.findStep(databaseJob, "TestStep1");
databaseStep1.setRestartData(null);
databaseStep1.setStreamContext(null);
stepDaoControl.setReturnValue(databaseStep1);
stepDao.getStepExecutionCount(databaseStep1);
stepDaoControl.setReturnValue(1);
stepDao.findStep(databaseJob, "TestStep2");
databaseStep2.setRestartData(new GenericStreamContext(null));
databaseStep2.setStreamContext(new GenericStreamContext(null));
stepDaoControl.setReturnValue(databaseStep2);
stepDao.getStepExecutionCount(databaseStep2);
stepDaoControl.setReturnValue(1);
@@ -470,9 +470,9 @@ public class SimpleJobRepositoryTests extends TestCase {
Iterator it = jobSteps.iterator();
StepInstance step = (StepInstance) it.next();
assertTrue(step.equals(databaseStep1));
assertTrue(step.getRestartData().getProperties().isEmpty());
assertTrue(step.getStreamContext().getProperties().isEmpty());
step = (StepInstance) it.next();
assertTrue(step.getRestartData().getProperties().isEmpty());
assertTrue(step.getStreamContext().getProperties().isEmpty());
assertTrue(step.equals(databaseStep2));
}

View File

@@ -153,11 +153,11 @@ public abstract class AbstractStepDaoTests extends AbstractTransactionalDataSour
Properties data = new Properties();
data.setProperty("restart.key1", "restartData");
StreamContext streamContext = new GenericStreamContext(data);
step1.setRestartData(streamContext);
step1.setStreamContext(streamContext);
stepDao.update(step1);
StepInstance tempStep = stepDao.findStep(jobInstance, step1.getName());
assertEquals(tempStep, step1);
assertEquals(tempStep.getRestartData().getProperties().toString(),
assertEquals(tempStep.getStreamContext().getProperties().toString(),
streamContext.getProperties().toString());
}

View File

@@ -107,16 +107,16 @@ public class MapStepDaoTests extends TestCase {
}
public void testSaveRestartData() throws Exception {
assertEquals(null, dao.getRestartData(step.getId()));
assertEquals(null, dao.getStreamContext(step.getId()));
step.setStatus(BatchStatus.COMPLETED);
Properties data = new Properties();
data.setProperty("restart.key1", "restartData");
StreamContext streamContext = new GenericStreamContext(data);
step.setRestartData(streamContext);
step.setStreamContext(streamContext);
dao.update(step);
StepInstance tempStep = dao.findStep(job, step.getName());
assertEquals(tempStep, step);
assertEquals(tempStep.getRestartData().getProperties().toString(),
assertEquals(tempStep.getStreamContext().getProperties().toString(),
streamContext.getProperties().toString());
}

View File

@@ -24,6 +24,10 @@ import java.util.Properties;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.item.stream.StreamManager;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.batch.statistics.StatisticsService;
import org.springframework.batch.support.PropertiesConverter;
@@ -131,21 +135,21 @@ public class SimpleStepContextTests extends TestCase {
assertTrue(list.contains("bar"));
assertTrue(list.contains("spam"));
}
public void testStatisticsWithNullService() throws Exception {
assertEquals(0, context.getStatistics().size());
}
public void testStatisticsWithNotNullService() throws Exception {
Map map = new HashMap();
context = new SimpleStepContext(null, null, new StubStatisticsService(map));
context = new SimpleStepContext(null, null, new StubStatisticsService(map), new StubStreamManager(map));
assertEquals(1, context.getStatistics().size());
assertEquals("bar", context.getStatistics().getProperty("foo"));
}
public void testStatisticsServiceRegistration() throws Exception {
Map map = new HashMap();
context = new SimpleStepContext(null, null, new StubStatisticsService(map));
context = new SimpleStepContext(null, null, new StubStatisticsService(map), new StubStreamManager(map));
StubStatisticsProvider provider = new StubStatisticsProvider();
context.setAttribute("foo", provider);
assertEquals(1, map.size());
@@ -155,7 +159,7 @@ public class SimpleStepContextTests extends TestCase {
/**
* @author Dave Syer
*
*
*/
private class StubStatisticsService implements StatisticsService {
private final Map map;
@@ -173,6 +177,35 @@ public class SimpleStepContextTests extends TestCase {
}
}
/**
* @author Dave Syer
*
*/
private class StubStreamManager implements StreamManager {
private final Map map;
private StubStreamManager(Map map) {
this.map = map;
}
public void close(Object key) {
}
public StreamContext getStreamContext(Object key) {
return new GenericStreamContext(PropertiesConverter.stringToProperties("foo=bar"));
}
public void open(Object key) {
}
public void register(Object key, ItemStream stream) {
map.put(key, stream);
}
public void restoreFrom(Object key, StreamContext data) {
}
}
/**
* @author Dave Syer
*

View File

@@ -99,12 +99,12 @@ public class SimpleStepConfigurationTests extends TestCase {
/**
* Test method for
* {@link org.springframework.batch.execution.step.simple.AbstractStep#isSaveRestartData()}.
* {@link org.springframework.batch.execution.step.simple.AbstractStep#isSaveStreamContext()}.
*/
public void testIsSaveRestartData() {
assertEquals(false, configuration.isSaveRestartData());
configuration.setSaveRestartData(true);
assertEquals(true, configuration.isSaveRestartData());
assertEquals(false, configuration.isSaveStreamContext());
configuration.setSaveStreamContext(true);
assertEquals(true, configuration.isSaveStreamContext());
}
}

View File

@@ -25,10 +25,10 @@ import java.util.Properties;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.JobSupport;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.JobSupport;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.domain.StepInstance;
@@ -43,7 +43,9 @@ import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.StreamException;
import org.springframework.batch.item.reader.ListItemReader;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.item.writer.AbstractItemWriter;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatContext;
@@ -72,7 +74,7 @@ public class SimpleStepExecutorTests extends TestCase {
private AbstractStep stepConfiguration;
private RepeatTemplate template;
private JobInstance jobInstance;
private ItemReader getReader(String[] args) {
@@ -111,7 +113,7 @@ public class SimpleStepExecutorTests extends TestCase {
template = new RepeatTemplate();
template.setCompletionPolicy(new SimpleCompletionPolicy(1));
stepExecutor.setChunkOperations(template);
jobInstance = new JobInstance(new Long(0), new JobParameters());
jobInstance.setJob(new JobSupport("FOO"));
}
@@ -120,8 +122,7 @@ public class SimpleStepExecutorTests extends TestCase {
StepInstance step = new StepInstance(new Long(9));
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
stepExecutor.execute(stepExecution);
assertEquals(1, processed.size());
@@ -158,8 +159,7 @@ public class SimpleStepExecutorTests extends TestCase {
final StepInstance step = new StepInstance(new Long(1));
final JobExecution jobExecution = new JobExecution(jobInstance);
final StepExecution stepExecution = new StepExecution(step,
jobExecution);
final StepExecution stepExecution = new StepExecution(step, jobExecution);
stepConfiguration.setTasklet(new Tasklet() {
public ExitStatus execute() throws Exception {
@@ -186,8 +186,7 @@ public class SimpleStepExecutorTests extends TestCase {
final StepInstance step = new StepInstance(new Long(1));
final JobExecution jobExecution = new JobExecution(jobInstance);
jobExecution.setId(new Long(1));
final StepExecution stepExecution = new StepExecution(step,
jobExecution);
final StepExecution stepExecution = new StepExecution(step, jobExecution);
template.setInterceptor(new RepeatInterceptorAdapter() {
public void open(RepeatContext context) {
@@ -210,8 +209,7 @@ public class SimpleStepExecutorTests extends TestCase {
StepInstance step = new StepInstance(new Long(1));
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
stepExecutor.execute(stepExecution);
assertEquals(1, processed.size());
@@ -237,8 +235,7 @@ public class SimpleStepExecutorTests extends TestCase {
StepInstance step = new StepInstance(new Long(1));
stepConfiguration.setTasklet(tasklet);
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
try {
stepExecutor.execute(stepExecution);
@@ -269,8 +266,7 @@ public class SimpleStepExecutorTests extends TestCase {
StepInstance step = new StepInstance(new Long(1));
stepConfiguration.setTasklet(tasklet);
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
try {
stepExecutor.execute(stepExecution);
@@ -285,21 +281,15 @@ public class SimpleStepExecutorTests extends TestCase {
* make sure a job that has never been executed before, but does have
* saveRestartData = true, doesn't have restoreFrom called on it.
*/
public void testNonRestartedJob() {
public void testNonRestartedJob() throws Exception {
StepInstance step = new StepInstance(new Long(1));
MockRestartableTasklet tasklet = new MockRestartableTasklet();
stepExecutor.setTasklet(tasklet);
stepConfiguration.setSaveRestartData(true);
stepConfiguration.setSaveStreamContext(true);
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
try {
stepExecutor.execute(stepExecution);
}
catch (Throwable t) {
fail();
}
stepExecutor.execute(stepExecution);
assertFalse(tasklet.isRestoreFromCalled());
assertTrue(tasklet.isGetRestartDataCalled());
@@ -309,24 +299,21 @@ public class SimpleStepExecutorTests extends TestCase {
* make sure a job that has been executed before, and is therefore being
* restarted, is restored.
*/
public void testRestartedJob() {
public void testRestartedJob() throws Exception {
StepInstance step = new StepInstance(new Long(1));
step.setStepExecutionCount(1);
MockRestartableTasklet tasklet = new MockRestartableTasklet();
stepExecutor.setTasklet(tasklet);
stepConfiguration.setSaveRestartData(true);
stepConfiguration.setSaveStreamContext(true);
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
stepExecution.getStep().setStreamContext(
new GenericStreamContext(PropertiesConverter.stringToProperties("foo=bar")));
try {
stepExecutor.execute(stepExecution);
}
catch (Throwable t) {
fail();
}
stepExecutor.execute(stepExecution);
assertTrue(tasklet.isRestoreFromCalled());
assertTrue(tasklet.isRestoreFromCalledWithSomeContext());
assertTrue(tasklet.isGetRestartDataCalled());
}
@@ -339,10 +326,9 @@ public class SimpleStepExecutorTests extends TestCase {
step.setStepExecutionCount(1);
MockRestartableTasklet tasklet = new MockRestartableTasklet();
stepConfiguration.setTasklet(tasklet);
stepConfiguration.setSaveRestartData(false);
stepConfiguration.setSaveStreamContext(false);
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step,
jobExecutionContext);
StepExecution stepExecution = new StepExecution(step, jobExecutionContext);
try {
stepExecutor.execute(stepExecution);
@@ -359,7 +345,7 @@ public class SimpleStepExecutorTests extends TestCase {
* Even though the job is restarted, and saveRestartData is true, nothing
* will be restored because the Tasklet does not implement Restartable.
*/
public void testRestartJobOnNonRestartableTasklet() {
public void testRestartJobOnNonRestartableTasklet() throws Exception {
StepInstance step = new StepInstance(new Long(1));
step.setStepExecutionCount(1);
stepConfiguration.setTasklet(new Tasklet() {
@@ -367,16 +353,11 @@ public class SimpleStepExecutorTests extends TestCase {
return ExitStatus.FINISHED;
}
});
stepConfiguration.setSaveRestartData(true);
stepConfiguration.setSaveStreamContext(true);
JobExecution jobExecution = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step, jobExecution);
try {
stepExecutor.execute(stepExecution);
}
catch (Throwable t) {
fail();
}
stepExecutor.execute(stepExecution);
}
public void testApplyConfigurationWithExceptionHandler() throws Exception {
@@ -426,10 +407,10 @@ public class SimpleStepExecutorTests extends TestCase {
return ExitStatus.FINISHED;
}
});
stepConfiguration.setSaveRestartData(true);
stepConfiguration.setSaveStreamContext(true);
JobExecution jobExecution = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step, jobExecution);
assertEquals(null, stepExecution.getStatistics().getProperty("foo"));
final Map map = new HashMap();
@@ -437,6 +418,7 @@ public class SimpleStepExecutorTests extends TestCase {
public Properties getStatistics(Object key) {
return PropertiesConverter.stringToProperties("foo=bar");
}
public void register(Object key, StatisticsProvider provider) {
map.put(key, provider);
}
@@ -448,8 +430,9 @@ public class SimpleStepExecutorTests extends TestCase {
catch (Throwable t) {
fail();
}
// At least once in that process the statistics service was asked for statistics...
// At least once in that process the statistics service was asked for
// statistics...
assertEquals("bar", stepExecution.getStatistics().getProperty("foo"));
// ...but nothing was registered because nothing with step scoped.
assertEquals(0, map.size());
@@ -461,17 +444,25 @@ public class SimpleStepExecutorTests extends TestCase {
private boolean restoreFromCalled = false;
private boolean restoreFromCalledWithSomeContext = false;
public ExitStatus execute() throws Exception {
StepSynchronizationManager.getContext().setAttribute("TASKLET_TEST", this);
return ExitStatus.FINISHED;
}
public StreamContext getRestartData() {
public boolean isRestoreFromCalledWithSomeContext() {
return restoreFromCalledWithSomeContext;
}
public StreamContext getStreamContext() {
getRestartDataCalled = true;
return null;
return new GenericStreamContext(PropertiesConverter.stringToProperties("spam=bucket"));
}
public void restoreFrom(StreamContext data) {
restoreFromCalled = true;
restoreFromCalledWithSomeContext = data.getProperties().size() > 0;
}
public boolean isGetRestartDataCalled() {
@@ -481,15 +472,13 @@ public class SimpleStepExecutorTests extends TestCase {
public boolean isRestoreFromCalled() {
return restoreFromCalled;
}
public void open() throws Exception {
throw new UnsupportedOperationException("Not implemented.");
public void open() throws StreamException {
}
public void close() throws Exception {
throw new UnsupportedOperationException("Not implemented.");
public void close() throws StreamException {
}
}
/*

View File

@@ -28,6 +28,7 @@ import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemRecoverer;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.KeyedItemReader;
import org.springframework.batch.item.StreamException;
import org.springframework.batch.item.reader.AbstractItemReader;
import org.springframework.batch.item.writer.AbstractItemWriter;
import org.springframework.batch.repeat.context.RepeatContextSupport;
@@ -60,7 +61,7 @@ public class ItemOrientedTaskletTests extends TestCase {
return null;
}
public void close() throws Exception {
public void close() throws StreamException {
}
};
@@ -182,7 +183,7 @@ public class ItemOrientedTaskletTests extends TestCase {
return "bar";
}
public void close() throws Exception {
public void close() throws StreamException {
}
});
@@ -287,7 +288,7 @@ public class ItemOrientedTaskletTests extends TestCase {
public Properties getStatistics() {
return PropertiesConverter.stringToProperties("foo=bar");
}
public void close() throws Exception {
public void close() throws StreamException {
}
}

View File

@@ -1,160 +0,0 @@
/*
* Copyright 2006-2007 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.execution.tasklet;
import java.util.Properties;
import junit.framework.TestCase;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.support.PropertiesConverter;
/**
* @author Peter Zozom
*/
public class RestartableItemOrientedTaskletTests extends TestCase {
private static class MockProvider implements ItemReader, ItemStream {
StreamContext data = new StreamContext() {
public Properties getProperties() {
return PropertiesConverter.stringToProperties("a=b");
}
};
public Object read() {
return null;
}
public StreamContext getRestartData() {
return data;
}
public void restoreFrom(StreamContext data) {
// restart data should be same as returned by getRestartData
assertEquals(this.data.getProperties(), data.getProperties());
}
public boolean recover(Object data, Throwable cause) {
return false;
}
public void open() throws Exception {
throw new UnsupportedOperationException("Not implemented.");
}
public void close() throws Exception {
throw new UnsupportedOperationException("Not implemented.");
}
}
private static class MockWriter implements ItemWriter, ItemStream {
StreamContext data = new StreamContext() {
public Properties getProperties() {
return PropertiesConverter.stringToProperties("x=y");
}
};
public void write(Object data) {
}
public StreamContext getRestartData() {
return data;
}
public void restoreFrom(StreamContext data) {
// restart data should be same as returned by getRestartData
assertEquals(this.data.getProperties(), data.getProperties());
}
public void open() throws Exception {
throw new UnsupportedOperationException("Not implemented.");
}
public void close() throws Exception {
throw new UnsupportedOperationException("Not implemented.");
}
}
private ItemReader itemProvider;
private ItemWriter itemWriter;
private RestartableItemOrientedTasklet module;
public void testRestart() {
// create data provider and data processor
itemProvider = new MockProvider();
itemWriter = new MockWriter();
// create and set up module
module = new RestartableItemOrientedTasklet();
module.setItemReader(itemProvider);
module.setItemWriter(itemWriter);
// get restart data
StreamContext data = module.getRestartData();
assertNotNull(data);
// restore from restart data (see asserts in mock classes)
module.restoreFrom(data);
}
public void testRestartFromGenericData() {
// create data provider and data processor
itemProvider = new MockProvider();
itemWriter = new MockWriter();
// create and set up module
module = new RestartableItemOrientedTasklet();
module.setItemReader(itemProvider);
module.setItemWriter(itemWriter);
// get restart data
StreamContext data = module.getRestartData();
assertNotNull(data);
data = new GenericStreamContext(data.getProperties());
// restore from restart data (see asserts in mock classes)
module.restoreFrom(data);
}
public void testRestartFromNotRestartable() {
// create and set up module
module = new RestartableItemOrientedTasklet();
module.setItemReader(null);
module.setItemWriter(null);
// get restart data
StreamContext data = module.getRestartData();
assertNotNull(data);
// restore from restart data (see asserts in mock classes)
module.restoreFrom(data);
//System.err.println(data.getProperties());
}
}

View File

@@ -27,7 +27,7 @@
class="org.springframework.batch.execution.step.simple.SimpleStep"
abstract="true">
<property name="allowStartIfComplete" value="true" />
<property name="saveRestartData" value="false" />
<property name="saveStreamContext" value="false" />
<property name="exceptionHandler">
<bean
class="org.springframework.batch.repeat.exception.handler.SimpleLimitExceptionHandler">