/* * Copyright 2006-2013 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.configuration.xml; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.partition.PartitionHandler; import org.springframework.batch.core.partition.StepExecutionSplitter; import org.springframework.batch.core.partition.support.PartitionStep; import org.springframework.batch.core.partition.support.StepExecutionAggregator; import org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler; 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.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.ReflectionUtils; /** * @author Dave Syer * @author Josh Long */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class PartitionStepParserTests implements ApplicationContextAware { @Autowired @Qualifier("job1") private Job job1; @Autowired @Qualifier("job2") private Job job2; @Autowired @Qualifier("job3") private Job job3; @Autowired @Qualifier("job4") private Job job4; @Autowired @Qualifier("job5") private Job job5; @Autowired @Qualifier("nameStoringTasklet") private NameStoringTasklet nameStoringTasklet; @Autowired private JobRepository jobRepository; @Autowired private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean; private ApplicationContext applicationContext; private List<String> savedStepNames = new ArrayList<String>(); @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Before public void setUp() { nameStoringTasklet.setStepNamesList(savedStepNames); mapJobRepositoryFactoryBean.clear(); } @SuppressWarnings("unchecked") private <T> T accessPrivateField(Object o, String fieldName) { Field field = ReflectionUtils.findField(o.getClass(), fieldName); boolean previouslyAccessibleValue = field.isAccessible(); field.setAccessible(true); T val = (T) ReflectionUtils.getField(field, o); field.setAccessible(previouslyAccessibleValue); return val; } @Test public void testDefaultHandlerStep() throws Exception { assertNotNull(job1); JobExecution jobExecution = jobRepository.createJobExecution(job1.getName(), new JobParameters()); job1.execute(jobExecution); assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); Collections.sort(savedStepNames); assertEquals("[step1:partition0, step1:partition1]", savedStepNames.toString()); List<String> stepNames = getStepNames(jobExecution); assertEquals(3, stepNames.size()); assertEquals("[s1, step1:partition0, step1:partition1]", stepNames.toString()); assertEquals("bar", jobExecution.getExecutionContext().get("foo")); } @Test public void testHandlerRefStep() throws Exception { assertNotNull(job2); JobExecution jobExecution = jobRepository.createJobExecution(job2.getName(), new JobParameters()); job2.execute(jobExecution); assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); Collections.sort(savedStepNames); assertEquals("[s2:partition0, s2:partition1, s2:partition2, s3]", savedStepNames.toString()); List<String> stepNames = getStepNames(jobExecution); assertEquals(5, stepNames.size()); assertEquals("[s2, s2:partition0, s2:partition1, s2:partition2, s3]", stepNames.toString()); } /** * BATCH-1509 we now support the ability define steps inline for partitioned * steps. this demonstrates that the execution proceeds as expected and that * the partition handler has a reference to the inline step definition */ @Test public void testNestedPartitionStepStepReference() throws Throwable { assertNotNull("the reference to the job3 configured in the XML file must not be null", job3); JobExecution jobExecution = jobRepository.createJobExecution(job3.getName(), new JobParameters()); job3.execute(jobExecution); for (StepExecution se : jobExecution.getStepExecutions()) { String stepExecutionName = se.getStepName(); // the partitioned step if (stepExecutionName.equalsIgnoreCase("j3s1")) { PartitionStep partitionStep = (PartitionStep) this.applicationContext.getBean(stepExecutionName); // prove that the reference in the {@link // TaskExecutorPartitionHandler} is the step configured inline TaskExecutorPartitionHandler taskExecutorPartitionHandler = accessPrivateField(partitionStep, "partitionHandler"); TaskletStep taskletStep = accessPrivateField(taskExecutorPartitionHandler, "step"); assertNotNull("the taskletStep wasn't configured with a step. " + "We're trusting that the factory ensured " + "a reference was given.", taskletStep); } } assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); Collections.sort(savedStepNames); assertEquals("[j3s1:partition0, j3s1:partition1, j3s1:partition2, j3s1:partition3, j3s1:partition4, j3s1:partition5]", savedStepNames.toString()); List<String> stepNames = getStepNames(jobExecution); assertEquals(7, stepNames.size()); assertEquals("[j3s1, j3s1:partition0, j3s1:partition1, j3s1:partition2, j3s1:partition3, j3s1:partition4, j3s1:partition5]", stepNames.toString()); } /** * BATCH-1509 we now support the ability define steps inline for partitioned * steps. this demonstrates that the execution proceeds as expected and that * the partition handler has a reference to the inline step definition */ @Test public void testNestedPartitionStep() throws Throwable { assertNotNull("the reference to the job4 configured in the XML file must not be null", job4); JobExecution jobExecution = jobRepository.createJobExecution(job4.getName(), new JobParameters()); job4.execute(jobExecution); for (StepExecution se : jobExecution.getStepExecutions()) { String stepExecutionName = se.getStepName(); if (stepExecutionName.equalsIgnoreCase("j4s1")) { // the partitioned // step PartitionStep partitionStep = (PartitionStep) this.applicationContext.getBean(stepExecutionName); // prove that the reference in the {@link // TaskExecutorPartitionHandler} is the step configured inline TaskExecutorPartitionHandler taskExecutorPartitionHandler = accessPrivateField(partitionStep, "partitionHandler"); TaskletStep taskletStep = accessPrivateField(taskExecutorPartitionHandler, "step"); assertNotNull("the taskletStep wasn't configured with a step. " + "We're trusting that the factory ensured " + "a reference was given.", taskletStep); } } assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); // Step names not saved by this one (it geosn't have that tasklet) assertEquals("[]", savedStepNames.toString()); List<String> stepNames = getStepNames(jobExecution); assertEquals(7, stepNames.size()); assertEquals("[j4s1, j4s1:partition0, j4s1:partition1, j4s1:partition2, j4s1:partition3, j4s1:partition4, j4s1:partition5]", stepNames.toString()); } @Test public void testCustomHandlerRefStep() throws Exception { assertNotNull(job5); JobExecution jobExecution = jobRepository.createJobExecution(job5.getName(), new JobParameters()); job5.execute(jobExecution); assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); List<String> stepNames = getStepNames(jobExecution); assertEquals(1, stepNames.size()); assertEquals("[j5s1]", stepNames.toString()); } private List<String> getStepNames(JobExecution jobExecution) { List<String> list = new ArrayList<String>(); for (StepExecution stepExecution : jobExecution.getStepExecutions()) { list.add(stepExecution.getStepName()); } Collections.sort(list); return list; } public static class CustomPartitionHandler implements PartitionHandler { @Override public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution) throws Exception { return Arrays.asList(stepExecution); } } public static class CustomStepExecutionAggregator implements StepExecutionAggregator { @Override public void aggregate(StepExecution result, Collection<StepExecution> executions) { result.getJobExecution().getExecutionContext().put("foo", "bar"); } } }