/*
* Copyright 2008-2014 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.core.partition.support;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
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.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ExecutionContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class SimpleStepExecutionSplitterTests {
private Step step;
private JobRepository jobRepository;
private StepExecution stepExecution;
@Before
public void setUp() throws Exception {
step = new TaskletStep("step");
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean();
jobRepository = factory.getObject();
stepExecution = jobRepository.createJobExecution("job", new JobParameters()).createStepExecution("bar");
jobRepository.add(stepExecution);
}
@Test
public void testSimpleStepExecutionProviderJobRepositoryStep() throws Exception {
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
Set<StepExecution> execs = splitter.split(stepExecution, 2);
assertEquals(2, execs.size());
for (StepExecution execution : execs) {
assertNotNull("step execution partition is saved", execution.getId());
}
}
/**
* Tests the results of BATCH-2490
* @throws Exception
*/
@Test
public void testAddressabilityOfSetResults() throws Exception {
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
Set<StepExecution> execs = splitter.split(stepExecution, 2);
assertEquals(2, execs.size());
StepExecution execution = execs.iterator().next();
execs.remove(execution);
assertEquals(1, execs.size());
}
@Test
public void testSimpleStepExecutionProviderJobRepositoryStepPartitioner() throws Exception {
final Map<String, ExecutionContext> map = Collections.singletonMap("foo", new ExecutionContext());
SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new Partitioner() {
@Override
public Map<String, ExecutionContext> partition(int gridSize) {
return map;
}
});
assertEquals(1, splitter.split(stepExecution, 2).size());
}
@Test
public void testRememberGridSize() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(2, split.size());
stepExecution = update(split, stepExecution, BatchStatus.FAILED);
assertEquals(2, provider.split(stepExecution, 3).size());
}
@Test
public void testRememberPartitionNames() throws Exception {
class CustomPartitioner implements Partitioner, PartitionNameProvider {
@Override
public Map<String, ExecutionContext> partition(int gridSize) {
return Collections.singletonMap("foo", new ExecutionContext());
}
@Override
public Collection<String> getPartitionNames(int gridSize) {
return Arrays.asList("foo");
}
}
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new CustomPartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(1, split.size());
assertEquals("step:foo", split.iterator().next().getStepName());
stepExecution = update(split, stepExecution, BatchStatus.FAILED);
split = provider.split(stepExecution, 2);
assertEquals("step:foo", split.iterator().next().getStepName());
}
@Test
public void testGetStepName() {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
assertEquals("step", provider.getStepName());
}
@Test
public void testUnkownStatus() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(2, split.size());
stepExecution = update(split, stepExecution, BatchStatus.UNKNOWN);
try {
provider.split(stepExecution, 2);
}
catch (JobExecutionException e) {
String message = e.getMessage();
assertTrue("Wrong message: " + message, message.contains("UNKNOWN"));
}
}
@Test
public void testCompleteStatusAfterFailure() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, false, step.getName(),
new SimplePartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(2, split.size());
StepExecution nextExecution = update(split, stepExecution, BatchStatus.COMPLETED, false);
// If already complete in another JobExecution we don't execute again
assertEquals(0, provider.split(nextExecution, 2).size());
}
@Test
public void testCompleteStatusSameJobExecution() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, false, step.getName(),
new SimplePartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(2, split.size());
stepExecution = update(split, stepExecution, BatchStatus.COMPLETED);
// If already complete in the same JobExecution we should execute again
assertEquals(2, provider.split(stepExecution, 2).size());
}
@Test
public void testIncompleteStatus() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(2, split.size());
stepExecution = update(split, stepExecution, BatchStatus.STARTED);
// If not already complete we don't execute again
try {
provider.split(stepExecution, 2);
}
catch (JobExecutionException e) {
String message = e.getMessage();
assertTrue("Wrong message: " + message, message.contains("STARTED"));
}
}
@Test
public void testAbandonedStatus() throws Exception {
SimpleStepExecutionSplitter provider = new SimpleStepExecutionSplitter(jobRepository, true, step.getName(),
new SimplePartitioner());
Set<StepExecution> split = provider.split(stepExecution, 2);
assertEquals(2, split.size());
stepExecution = update(split, stepExecution, BatchStatus.ABANDONED);
// If not already complete we don't execute again
try {
provider.split(stepExecution, 2);
}
catch (JobExecutionException e) {
String message = e.getMessage();
assertTrue("Wrong message: " + message, message.contains("ABANDONED"));
}
}
private StepExecution update(Set<StepExecution> split, StepExecution stepExecution, BatchStatus status)
throws Exception {
return update(split, stepExecution, status, true);
}
private StepExecution update(Set<StepExecution> split, StepExecution stepExecution, BatchStatus status,
boolean sameJobExecution) throws Exception {
ExecutionContext executionContext = stepExecution.getExecutionContext();
for (StepExecution child : split) {
child.setEndTime(new Date());
child.setStatus(status);
jobRepository.update(child);
}
stepExecution.setEndTime(new Date());
stepExecution.setStatus(status);
jobRepository.update(stepExecution);
JobExecution jobExecution = stepExecution.getJobExecution();
if (!sameJobExecution) {
jobExecution.setStatus(BatchStatus.FAILED);
jobExecution.setEndTime(new Date());
jobRepository.update(jobExecution);
JobInstance jobInstance = jobExecution.getJobInstance();
jobExecution = jobRepository.createJobExecution(jobInstance.getJobName(), jobExecution.getJobParameters());
}
stepExecution = jobExecution.createStepExecution(stepExecution.getStepName());
stepExecution.setExecutionContext(executionContext);
jobRepository.add(stepExecution);
return stepExecution;
}
}