/*
* 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.repeat.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.callback.NestedRepeatCallback;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
/**
* Test various approaches to chunking of a batch. Not really a unit test, but
* it should be fast.
*
* @author Dave Syer
*
*/
public class ChunkedRepeatTests extends AbstractTradeBatchTests {
int count = 0;
/**
* Chunking using a dedicated TerminationPolicy. Transactions would be laid
* on at the level of chunkTemplate.execute() or the surrounding callback.
*
* @throws Exception
*/
@Test
public void testChunkedBatchWithTerminationPolicy() throws Exception {
RepeatTemplate repeatTemplate = new RepeatTemplate();
final RepeatCallback callback = new ItemReaderRepeatCallback<Trade>(provider, processor);
final RepeatTemplate chunkTemplate = new RepeatTemplate();
// The policy is resettable so we only have to resolve this dependency
// once
chunkTemplate.setCompletionPolicy(new SimpleCompletionPolicy(2));
RepeatStatus result = repeatTemplate.iterate(new NestedRepeatCallback(chunkTemplate, callback) {
@Override
public RepeatStatus doInIteration(RepeatContext context) throws Exception {
count++; // for test assertion
return super.doInIteration(context);
}
});
assertEquals(NUMBER_OF_ITEMS, processor.count);
// The chunk executes 3 times because the last one
// returns false. We terminate the main batch when
// we encounter a partially empty chunk.
assertEquals(3, count);
assertFalse(result.isContinuable());
}
/**
* Chunking with an asynchronous taskExecutor in the chunks. Transactions
* have to be at the level of the business callback.
*
* @throws Exception
*/
@Test
public void testAsynchronousChunkedBatchWithCompletionPolicy() throws Exception {
RepeatTemplate repeatTemplate = new RepeatTemplate();
final RepeatCallback callback = new ItemReaderRepeatCallback<Trade>(provider, processor);
final TaskExecutorRepeatTemplate chunkTemplate = new TaskExecutorRepeatTemplate();
// The policy is resettable so we only have to resolve this dependency
// once
chunkTemplate.setCompletionPolicy(new SimpleCompletionPolicy(2));
chunkTemplate.setTaskExecutor(new SimpleAsyncTaskExecutor());
RepeatStatus result = repeatTemplate.iterate(new NestedRepeatCallback(chunkTemplate, callback) {
@Override
public RepeatStatus doInIteration(RepeatContext context) throws Exception {
count++; // for test assertion
return super.doInIteration(context);
}
});
assertEquals(NUMBER_OF_ITEMS, processor.count);
assertFalse(result.isContinuable());
assertTrue("Expected at least 3 chunks but found: "+count, count>=3);
}
/**
* Explicit chunking of input data. Transactions would be laid on at the
* level of template.execute().
*
* @throws Exception
*/
@Test
public void testChunksWithTruncatedItemProvider() throws Exception {
RepeatTemplate template = new RepeatTemplate();
// This pattern would work with an asynchronous callback as well
// (but non-transactional in that case).
class Chunker {
boolean ready = false;
int count = 0;
void set() {
ready = true;
}
boolean ready() {
return ready;
}
boolean first() {
return count == 0;
}
void reset() {
count = 0;
ready = false;
}
void increment() {
count++;
}
}
final Chunker chunker = new Chunker();
while (!chunker.ready()) {
ItemReader<Trade> truncated = new ItemReader<Trade>() {
int count = 0;
@Override
public Trade read() throws Exception {
if (count++ < 2)
return provider.read();
return null;
}
};
chunker.reset();
template.iterate(new ItemReaderRepeatCallback<Trade>(truncated, processor) {
@Override
public RepeatStatus doInIteration(RepeatContext context) throws Exception {
RepeatStatus result = super.doInIteration(context);
if (!result.isContinuable() && chunker.first()) {
chunker.set();
}
chunker.increment();
return result;
}
});
}
assertEquals(NUMBER_OF_ITEMS, processor.count);
}
}