Create the DeployerPartitionHandler and related

DeployerStepExecutionHandler

Created a PartitionHandler that delegates to a TaskLauncher from Spring
Cloud Deployer to execute workers.

Resolves spring-cloud/spring-cloud-task#109

Updates per code review
This commit is contained in:
Michael Minella
2016-03-18 12:43:13 -05:00
committed by Glenn Renfro
parent 726441dda3
commit 3d3b90812e
22 changed files with 2105 additions and 7 deletions

View File

@@ -15,12 +15,16 @@
*/
package org.springframework.cloud.task.batch.configuration;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.core.job.AbstractJob;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.task.batch.listener.TaskBatchExecutionListener;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
/**
* Injects a configured {@link TaskBatchExecutionListener} into any batch jobs (beans
@@ -35,10 +39,18 @@ public class TaskBatchExecutionListenerBeanPostProcessor implements BeanPostProc
@Autowired
private ApplicationContext applicationContext;
private List<String> jobNames = new ArrayList<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if(jobNames.size() > 0) {
if(!jobNames.contains(beanName)) {
return bean;
}
}
int length = this.applicationContext
.getBeanNamesForType(TaskBatchExecutionListener.class).length;
@@ -61,4 +73,10 @@ public class TaskBatchExecutionListenerBeanPostProcessor implements BeanPostProc
throws BeansException {
return bean;
}
public void setJobNames(List<String> jobNames) {
Assert.notNull(jobNames, "A list is required");
this.jobNames = jobNames;
}
}

View File

@@ -0,0 +1,332 @@
/*
* Copyright 2016 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.cloud.task.batch.partition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.partition.PartitionHandler;
import org.springframework.batch.core.partition.StepExecutionSplitter;
import org.springframework.batch.poller.DirectPoller;
import org.springframework.batch.poller.Poller;
import org.springframework.cloud.deployer.spi.core.AppDefinition;
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
import org.springframework.cloud.deployer.spi.task.TaskLauncher;
import org.springframework.cloud.task.listener.annotation.BeforeTask;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* <p>A {@link PartitionHandler} implementation that delegates to a {@link TaskLauncher} for
* each of the workers. The id of the worker's StepExecution is passed as an environment
* variable to the worker. The worker, bootstrapped by the
* {@link DeployerStepExecutionHandler}, looks up the StepExecution in the JobRepository
* and executes it. This PartitionHandler polls the JobRepository for the results.</p>
*
* <p>If the job fails, the partitions will be re-executed per normal batch rules (steps that
* are complete should do nothing, failed steps should restart based on their
* configurations).</p>
*
* <p>This PartitionHandler and all of the worker processes must share the same JobRepository
* data store (aka point the same database).</p>
*
* @author Michael Minella
*/
public class DeployerPartitionHandler implements PartitionHandler, EnvironmentAware {
public static final String SPRING_CLOUD_TASK_JOB_EXECUTION_ID =
"spring.cloud.task.job-execution-id";
public static final String SPRING_CLOUD_TASK_STEP_EXECUTION_ID =
"spring.cloud.task.step-execution-id";
public static final String SPRING_CLOUD_TASK_STEP_NAME =
"spring.cloud.task.step-name";
private int maxWorkers = -1;
private int gridSize = 1;
private int currentWorkers = 0;
private TaskLauncher taskLauncher;
private JobExplorer jobExplorer;
private TaskExecution taskExecution;
private Resource resource;
private Map<String, String> environmentProperties = new HashMap<>();
private String stepName;
private Log logger = LogFactory.getLog(DeployerPartitionHandler.class);
private long pollInterval = 10000;
private long timeout = -1;
private Environment environment;
public DeployerPartitionHandler(TaskLauncher taskLauncher,
JobExplorer jobExplorer,
Resource resource,
String stepName) {
Assert.notNull(taskLauncher, "A taskLauncher is required");
Assert.notNull(jobExplorer, "A jobExplorer is required");
Assert.notNull(resource, "A resource is required");
Assert.hasText(stepName, "A step name is required");
this.taskLauncher = taskLauncher;
this.jobExplorer = jobExplorer;
this.resource = resource;
this.stepName = stepName;
}
/**
* The maximum number of workers to be executing at once.
*
* @param maxWorkers number of workers. Defaults to -1 (unlimited)
*/
public void setMaxWorkers(int maxWorkers) {
Assert.isTrue(maxWorkers != 0, "maxWorkers cannot be 0");
this.maxWorkers = maxWorkers;
}
/**
* Approximate size of the pool of worker JVMs available. May be used by the
* {@link StepExecutionSplitter} to determine how many partitions to create (at the
* discretion of the {@link org.springframework.batch.core.partition.support.Partitioner}).
*
* @param gridSize size of grid. Defaults to 1
*/
public void setGridSize(int gridSize) {
this.gridSize = gridSize;
}
/**
* System properties to be made available for all workers.
*
* @param environmentProperties Map of properties
*/
public void setEnvironmentProperties(Map<String, String> environmentProperties) {
this.environmentProperties = environmentProperties;
}
/**
* The interval to check the job repository for completed steps.
*
* @param pollInterval interval. Defaults to 10 seconds
*/
public void setPollInterval(long pollInterval) {
this.pollInterval = pollInterval;
}
/**
* Timeout for the master step. This is a timeout for all workers to complete.
*
* @param timeout timeout. Defaults to none (-1).
*/
public void setTimeout(long timeout) {
this.timeout = timeout;
}
@BeforeTask
public void beforeTask(TaskExecution taskExecution) {
this.taskExecution = taskExecution;
}
@Override
public Collection<StepExecution> handle(StepExecutionSplitter stepSplitter,
StepExecution stepExecution) throws Exception {
final Set<StepExecution> tempCandidates =
stepSplitter.split(stepExecution, this.gridSize);
// Following two lines due to https://jira.spring.io/browse/BATCH-2490
final Set<StepExecution> candidates = new HashSet<>(tempCandidates.size());
candidates.addAll(tempCandidates);
int partitions = candidates.size();
logger.debug(String.format("%s partitions were returned", partitions));
final Set<StepExecution> executed = new HashSet<>(candidates.size());
if(CollectionUtils.isEmpty(candidates)) {
return null;
}
launchWorkers(candidates, executed);
candidates.removeAll(executed);
return pollReplies(stepExecution, executed, candidates, partitions);
}
private void launchWorkers(Set<StepExecution> candidates, Set<StepExecution> executed) {
for (StepExecution execution : candidates) {
if(this.currentWorkers < this.maxWorkers || this.maxWorkers < 0) {
launchWorker(execution);
this.currentWorkers++;
executed.add(execution);
}
}
}
private void launchWorker(StepExecution workerStepExecution) {
//TODO: Refactor these to be passed as command line args once SCD-20 is complete
// https://github.com/spring-cloud/spring-cloud-deployer/issues/20
Map<String, String> parameters = getParameters(this.taskExecution.getParameters());
parameters.put(SPRING_CLOUD_TASK_JOB_EXECUTION_ID,
String.valueOf(workerStepExecution.getJobExecution().getId()));
parameters.put(SPRING_CLOUD_TASK_STEP_EXECUTION_ID,
String.valueOf(workerStepExecution.getId()));
parameters.put(SPRING_CLOUD_TASK_STEP_NAME, this.stepName);
AppDefinition definition =
new AppDefinition(String.format("%s:%s:%s",
taskExecution.getTaskName(),
workerStepExecution.getJobExecution().getJobInstance().getJobName(),
workerStepExecution.getStepName()),
parameters);
Map<String, String> environmentProperties = new HashMap<>(this.environmentProperties.size());
environmentProperties.putAll(getCurrentEnvironmentProperties());
environmentProperties.putAll(this.environmentProperties);
AppDeploymentRequest request =
new AppDeploymentRequest(definition, this.resource, environmentProperties);
taskLauncher.launch(request);
}
private Collection<StepExecution> pollReplies(final StepExecution masterStepExecution,
final Set<StepExecution> executed,
final Set<StepExecution> candidates,
final int size) throws Exception {
final Collection<StepExecution> result = new ArrayList<>(executed.size());
Callable<Collection<StepExecution>> callback = new Callable<Collection<StepExecution>>() {
@Override
public Collection<StepExecution> call() throws Exception {
Set<StepExecution> newExecuted = new HashSet<>();
for (StepExecution curStepExecution : executed) {
if (!result.contains(curStepExecution)) {
StepExecution partitionStepExecution =
jobExplorer.getStepExecution(masterStepExecution.getJobExecutionId(), curStepExecution.getId());
if (isComplete(partitionStepExecution.getStatus())) {
result.add(partitionStepExecution);
currentWorkers--;
if (!candidates.isEmpty()) {
launchWorkers(candidates, newExecuted);
candidates.removeAll(newExecuted);
}
}
}
}
executed.addAll(newExecuted);
if(result.size() == size) {
return result;
}
else {
return null;
}
}
};
Poller<Collection<StepExecution>> poller = new DirectPoller<>(this.pollInterval);
Future<Collection<StepExecution>> resultsFuture = poller.poll(callback);
if(timeout >= 0) {
return resultsFuture.get(timeout, TimeUnit.MILLISECONDS);
}
else {
return resultsFuture.get();
}
}
private boolean isComplete(BatchStatus status) {
return status.equals(BatchStatus.COMPLETED) || status.isGreaterThan(BatchStatus.STARTED);
}
private Map<String, String> getParameters(List<String> parameters) {
Map<String, String> parameterMap = new HashMap<>(parameters.size());
for (String parameter : parameters) {
String[] pieces = parameter.split("=");
parameterMap.put(pieces[0], pieces[1]);
}
return parameterMap;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
private Map<String, String> getCurrentEnvironmentProperties() {
Map<String, String> currentEnvironment = new HashMap<>();
Set<String> keys = new HashSet<>();
for(Iterator it = ((AbstractEnvironment) this.environment).getPropertySources().iterator(); it.hasNext(); ) {
PropertySource propertySource = (PropertySource) it.next();
if (propertySource instanceof MapPropertySource) {
keys.addAll(Arrays.asList(((MapPropertySource) propertySource).getPropertyNames()));
}
}
for (String key : keys) {
currentEnvironment.put(key, this.environment.getProperty(key));
}
return currentEnvironment;
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2016 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.cloud.task.batch.partition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.NoSuchStepException;
import org.springframework.batch.core.step.StepLocator;
import org.springframework.batch.integration.partition.BeanFactoryStepLocator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
/**
* <p>A {@link CommandLineRunner} used to execute a {@link Step}. No result is provided
* directly to the associated {@link DeployerPartitionHandler} as it will obtain the step
* results directly from the shared job repository.</p>
*
* <p>The {@link StepExecution} is rehydrated based on the environment variables provided.
* Specifically, the following variables are required:</p>
* <ul>
* <li>{@link DeployerPartitionHandler#SPRING_CLOUD_TASK_JOB_EXECUTION_ID}: The id of
* the JobExecution.</li>
* <li>{@link DeployerPartitionHandler#SPRING_CLOUD_TASK_STEP_EXECUTION_ID}: The id of
* the StepExecution.</li>
* <li>{@link DeployerPartitionHandler#SPRING_CLOUD_TASK_STEP_NAME}: The id of the
* bean definition for the Step to execute. The id must be found within the provided
* {@link BeanFactory}</li>
* </ul>
*
* @author Michael Minella
*/
public class DeployerStepExecutionHandler implements CommandLineRunner {
private JobExplorer jobExplorer;
private JobRepository jobRepository;
private Log logger = LogFactory.getLog(DeployerStepExecutionHandler.class);
@Autowired
private Environment environment;
private StepLocator stepLocator;
public DeployerStepExecutionHandler(BeanFactory beanFactory, JobExplorer jobExplorer, JobRepository jobRepository) {
Assert.notNull(beanFactory, "A beanFactory is required");
Assert.notNull(jobExplorer, "A jobExplorer is required");
Assert.notNull(jobRepository, "A jobRepository is required");
this.stepLocator = new BeanFactoryStepLocator();
((BeanFactoryStepLocator) this.stepLocator).setBeanFactory(beanFactory);
this.jobExplorer = jobExplorer;
this.jobRepository = jobRepository;
}
@Override
public void run(String... args) throws Exception {
validateRequest();
Long jobExecutionId = Long.parseLong(environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID));
Long stepExecutionId = Long.parseLong(environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID));
StepExecution stepExecution = jobExplorer.getStepExecution(jobExecutionId, stepExecutionId);
if (stepExecution == null) {
throw new NoSuchStepException(String.format("No StepExecution could be located for step execution id %s within job execution %s", stepExecutionId, jobExecutionId));
}
String stepName = environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME);
Step step = stepLocator.getStep(stepName);
try {
logger.debug(String.format("Executing step %s with step execution id %s and job execution id %s", stepExecution.getStepName(), stepExecutionId, jobExecutionId));
step.execute(stepExecution);
}
catch (JobInterruptedException e) {
stepExecution.setStatus(BatchStatus.STOPPED);
jobRepository.update(stepExecution);
}
catch (Throwable e) {
stepExecution.addFailureException(e);
stepExecution.setStatus(BatchStatus.FAILED);
jobRepository.update(stepExecution);
}
}
private void validateRequest() {
Assert.isTrue(environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID), "A job execution id is required");
Assert.isTrue(environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID), "A step execution id is required");
Assert.isTrue(environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME), "A step name is required");
Assert.isTrue(this.stepLocator.getStepNames().contains(environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)), "The step requested cannot be found in the provided BeanFactory");
}
}

View File

@@ -0,0 +1,609 @@
/*
* Copyright 2016 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.cloud.task.batch.partition;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.partition.StepExecutionSplitter;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.cloud.deployer.spi.core.AppDefinition;
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
import org.springframework.cloud.deployer.spi.task.TaskLauncher;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.mock.env.MockEnvironment;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Michael Minella
*/
public class DeployerPartitionHandlerTests {
@Mock
private TaskLauncher taskLauncher;
@Mock
private JobExplorer jobExplorer;
@Mock
private Resource resource;
@Mock
private StepExecutionSplitter splitter;
@Mock
private JobRepository jobRepository;
private Environment environment;
@Captor ArgumentCaptor<AppDeploymentRequest> appDeploymentRequestArgumentCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.environment = new MockEnvironment();
}
@Test
public void testConstructorValidation() {
validateConstructorValidation(null, null, null, null, "A taskLauncher is required");
validateConstructorValidation(this.taskLauncher, null, null, null, "A jobExplorer is required");
validateConstructorValidation(this.taskLauncher, this.jobExplorer, null, null, "A resource is required");
validateConstructorValidation(this.taskLauncher, this.jobExplorer, this.resource, null, "A step name is required");
new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step-name");
}
@Test
public void testNoPartitions() throws Exception {
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
StepExecution stepExecution = new StepExecution("step1", new JobExecution(1L));
when(this.splitter.split(stepExecution, 1)).thenReturn(new HashSet<StepExecution>());
Collection<StepExecution> results = handler.handle(this.splitter, stepExecution);
verify(this.taskLauncher, never()).launch((AppDeploymentRequest) any());
assertNull(results);
}
@Test
public void testSinglePartition() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish = getStepExecutionFinish(workerStepExecutionStart, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher).launch(this.appDeploymentRequestArgumentCaptor.capture());
AppDeploymentRequest request = this.appDeploymentRequestArgumentCaptor.getValue();
assertEquals(this.resource, request.getResource());
assertEquals(0, request.getEnvironmentProperties().size());
AppDefinition appDefinition = request.getDefinition();
assertEquals("partitionedJobTask:partitionedJob:step1:partition1", appDefinition.getName());
assertEquals("1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID));
assertEquals("4", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID));
assertEquals("step1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME));
assertEquals(1, results.size());
StepExecution resultStepExecution = results.iterator().next();
assertEquals(BatchStatus.COMPLETED, resultStepExecution.getStatus());
assertEquals("step1:partition1", resultStepExecution.getStepName());
}
@Test
public void testThreePartitions() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart1 = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish1 = getStepExecutionFinish(workerStepExecutionStart1, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart2 = getStepExecutionStart(jobExecution, 5L);
StepExecution workerStepExecutionFinish2 = getStepExecutionFinish(workerStepExecutionStart2, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart3 = getStepExecutionStart(jobExecution, 6L);
StepExecution workerStepExecutionFinish3 = getStepExecutionFinish(workerStepExecutionStart3, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart1);
stepExecutions.add(workerStepExecutionStart2);
stepExecutions.add(workerStepExecutionStart3);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish1);
when(this.jobExplorer.getStepExecution(1L, 5L)).thenReturn(workerStepExecutionFinish2);
when(this.jobExplorer.getStepExecution(1L, 6L)).thenReturn(workerStepExecutionFinish3);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher, times(3)).launch(this.appDeploymentRequestArgumentCaptor.capture());
List<AppDeploymentRequest> allValues = this.appDeploymentRequestArgumentCaptor.getAllValues();
validateAppDeploymentRequests(allValues, 3);
validateStepExecutionResults(results);
}
@Test
public void testThreePartitionsTwoWorkers() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart1 = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish1 = getStepExecutionFinish(workerStepExecutionStart1, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart2 = getStepExecutionStart(jobExecution, 5L);
StepExecution workerStepExecutionFinish2 = getStepExecutionFinish(workerStepExecutionStart2, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart3 = getStepExecutionStart(jobExecution, 6L);
StepExecution workerStepExecutionFinish3 = getStepExecutionFinish(workerStepExecutionStart3, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
handler.setMaxWorkers(2);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart1);
stepExecutions.add(workerStepExecutionStart2);
stepExecutions.add(workerStepExecutionStart3);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish1);
when(this.jobExplorer.getStepExecution(1L, 5L)).thenReturn(workerStepExecutionFinish2);
when(this.jobExplorer.getStepExecution(1L, 6L)).thenReturn(workerStepExecutionFinish3);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher, times(3)).launch(this.appDeploymentRequestArgumentCaptor.capture());
List<AppDeploymentRequest> allValues = this.appDeploymentRequestArgumentCaptor.getAllValues();
validateAppDeploymentRequests(allValues, 3);
validateStepExecutionResults(results);
}
@Test
public void testFailedWorker() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart1 = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish1 = getStepExecutionFinish(workerStepExecutionStart1, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart2 = getStepExecutionStart(jobExecution, 5L);
StepExecution workerStepExecutionFinish2 = getStepExecutionFinish(workerStepExecutionStart2, BatchStatus.FAILED);
StepExecution workerStepExecutionStart3 = getStepExecutionStart(jobExecution, 6L);
StepExecution workerStepExecutionFinish3 = getStepExecutionFinish(workerStepExecutionStart3, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
handler.setMaxWorkers(2);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart1);
stepExecutions.add(workerStepExecutionStart2);
stepExecutions.add(workerStepExecutionStart3);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish1);
when(this.jobExplorer.getStepExecution(1L, 5L)).thenReturn(workerStepExecutionFinish2);
when(this.jobExplorer.getStepExecution(1L, 6L)).thenReturn(workerStepExecutionFinish3);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher, times(3)).launch(this.appDeploymentRequestArgumentCaptor.capture());
List<AppDeploymentRequest> allValues = this.appDeploymentRequestArgumentCaptor.getAllValues();
validateAppDeploymentRequests(allValues, 3);
Iterator<StepExecution> resultsIterator = results.iterator();
Set<String> names = new HashSet<>(results.size());
while (resultsIterator.hasNext()) {
StepExecution curResult = resultsIterator.next();
if(curResult.getStepName().equals("step1:partition2")) {
assertEquals(BatchStatus.FAILED, curResult.getStatus());
}
else {
assertEquals(BatchStatus.COMPLETED, curResult.getStatus());
}
assertTrue(!names.contains(curResult.getStepName()));
names.add(curResult.getStepName());
}
}
@Test
public void testPassingEnvironmentProperties() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish = getStepExecutionFinish(workerStepExecutionStart, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
Map<String, String> environmentParameters = new HashMap<>(2);
environmentParameters.put("foo", "bar");
environmentParameters.put("baz", "qux");
handler.setEnvironmentProperties(environmentParameters);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher).launch(this.appDeploymentRequestArgumentCaptor.capture());
AppDeploymentRequest request = this.appDeploymentRequestArgumentCaptor.getValue();
assertEquals(this.resource, request.getResource());
assertEquals(2, request.getEnvironmentProperties().size());
assertEquals("bar", request.getEnvironmentProperties().get("foo"));
assertEquals("qux", request.getEnvironmentProperties().get("baz"));
AppDefinition appDefinition = request.getDefinition();
assertEquals("partitionedJobTask:partitionedJob:step1:partition1", appDefinition.getName());
assertEquals("1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID));
assertEquals("4", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID));
assertEquals("step1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME));
assertEquals(1, results.size());
StepExecution resultStepExecution = results.iterator().next();
assertEquals(BatchStatus.COMPLETED, resultStepExecution.getStatus());
assertEquals("step1:partition1", resultStepExecution.getStepName());
}
@Test
public void testOverridingEnvironmentProperties() throws Exception {
((MockEnvironment) this.environment).setProperty("foo", "zoo");
((MockEnvironment) this.environment).setProperty("task", "batch");
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish = getStepExecutionFinish(workerStepExecutionStart, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
Map<String, String> environmentParameters = new HashMap<>(2);
environmentParameters.put("foo", "bar");
environmentParameters.put("baz", "qux");
handler.setEnvironmentProperties(environmentParameters);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher).launch(this.appDeploymentRequestArgumentCaptor.capture());
AppDeploymentRequest request = this.appDeploymentRequestArgumentCaptor.getValue();
assertEquals(this.resource, request.getResource());
assertEquals(3, request.getEnvironmentProperties().size());
assertEquals("bar", request.getEnvironmentProperties().get("foo"));
assertEquals("qux", request.getEnvironmentProperties().get("baz"));
assertEquals("batch", request.getEnvironmentProperties().get("task"));
AppDefinition appDefinition = request.getDefinition();
assertEquals("partitionedJobTask:partitionedJob:step1:partition1", appDefinition.getName());
assertEquals("1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID));
assertEquals("4", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID));
assertEquals("step1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME));
assertEquals(1, results.size());
StepExecution resultStepExecution = results.iterator().next();
assertEquals(BatchStatus.COMPLETED, resultStepExecution.getStatus());
assertEquals("step1:partition1", resultStepExecution.getStepName());
}
@Test
public void testPollInterval() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart1 = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish1 = getStepExecutionFinish(workerStepExecutionStart1, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart2 = getStepExecutionStart(jobExecution, 5L);
StepExecution workerStepExecutionFinish2 = getStepExecutionFinish(workerStepExecutionStart2, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
handler.setPollInterval(20000L);
handler.setMaxWorkers(1);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart1);
stepExecutions.add(workerStepExecutionStart2);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish1);
when(this.jobExplorer.getStepExecution(1L, 5L)).thenReturn(workerStepExecutionFinish2);
handler.beforeTask(taskExecution);
Date startTime = new Date();
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
Date endTime = new Date();
verify(this.taskLauncher, times(2)).launch(this.appDeploymentRequestArgumentCaptor.capture());
List<AppDeploymentRequest> allRequests = this.appDeploymentRequestArgumentCaptor.getAllValues();
validateAppDeploymentRequests(allRequests, 2);
validateStepExecutionResults(results);
assertTrue(endTime.getTime() - startTime.getTime() > 20000);
}
@Test(expected = TimeoutException.class)
public void testTimeout() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart1 = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish1 = getStepExecutionFinish(workerStepExecutionStart1, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart2 = getStepExecutionStart(jobExecution, 5L);
StepExecution workerStepExecutionFinish2 = getStepExecutionFinish(workerStepExecutionStart2, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
handler.setPollInterval(20000L);
handler.setMaxWorkers(1);
handler.setTimeout(1000L);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart1);
stepExecutions.add(workerStepExecutionStart2);
when(this.splitter.split(masterStepExecution, 1)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish1);
when(this.jobExplorer.getStepExecution(1L, 5L)).thenReturn(workerStepExecutionFinish2);
handler.beforeTask(taskExecution);
handler.handle(this.splitter, masterStepExecution);
}
@Test
public void testGridSize() throws Exception {
StepExecution masterStepExecution = createMasterStepExecution();
JobExecution jobExecution = masterStepExecution.getJobExecution();
StepExecution workerStepExecutionStart1 = getStepExecutionStart(jobExecution, 4L);
StepExecution workerStepExecutionFinish1 = getStepExecutionFinish(workerStepExecutionStart1, BatchStatus.COMPLETED);
StepExecution workerStepExecutionStart2 = getStepExecutionStart(jobExecution, 5L);
StepExecution workerStepExecutionFinish2 = getStepExecutionFinish(workerStepExecutionStart2, BatchStatus.COMPLETED);
DeployerPartitionHandler handler = new DeployerPartitionHandler(this.taskLauncher, this.jobExplorer, this.resource, "step1");
handler.setEnvironment(this.environment);
handler.setGridSize(2);
TaskExecution taskExecution = new TaskExecution();
taskExecution.setTaskName("partitionedJobTask");
Set<StepExecution> stepExecutions = new HashSet<>();
stepExecutions.add(workerStepExecutionStart1);
stepExecutions.add(workerStepExecutionStart2);
when(this.splitter.split(masterStepExecution, 2)).thenReturn(stepExecutions);
when(this.jobExplorer.getStepExecution(1L, 4L)).thenReturn(workerStepExecutionFinish1);
when(this.jobExplorer.getStepExecution(1L, 5L)).thenReturn(workerStepExecutionFinish2);
handler.beforeTask(taskExecution);
Collection<StepExecution> results = handler.handle(this.splitter, masterStepExecution);
verify(this.taskLauncher, times(2)).launch(this.appDeploymentRequestArgumentCaptor.capture());
List<AppDeploymentRequest> allRequests = this.appDeploymentRequestArgumentCaptor.getAllValues();
validateAppDeploymentRequests(allRequests, 2);
validateStepExecutionResults(results);
}
private StepExecution getStepExecutionFinish(StepExecution stepExecutionStart, BatchStatus status) {
StepExecution workerStepExecutionFinish = new StepExecution(stepExecutionStart.getStepName(), stepExecutionStart.getJobExecution());
workerStepExecutionFinish.setId(stepExecutionStart.getId());
workerStepExecutionFinish.setStatus(status);
return workerStepExecutionFinish;
}
private StepExecution getStepExecutionStart(JobExecution jobExecution, long id) {
StepExecution workerStepExecutionStart = new StepExecution("step1:partition" + (id - 3), jobExecution);
workerStepExecutionStart.setId(id);
return workerStepExecutionStart;
}
private StepExecution createMasterStepExecution() {
JobExecution jobExecution = new JobExecution(1L);
jobExecution.setJobInstance(new JobInstance(2L, "partitionedJob"));
StepExecution masterStepExecution = new StepExecution("masterStep", jobExecution);
masterStepExecution.setId(3L);
return masterStepExecution;
}
private void validateStepExecutionResults(Collection<StepExecution> results) {
Iterator<StepExecution> resultsIterator = results.iterator();
Set<String> names = new HashSet<>(results.size());
while (resultsIterator.hasNext()) {
StepExecution curResult = resultsIterator.next();
assertEquals(BatchStatus.COMPLETED, curResult.getStatus());
assertTrue(!names.contains(curResult.getStepName()));
names.add(curResult.getStepName());
}
}
private void validateAppDeploymentRequests(List<AppDeploymentRequest> allRequests, int numberOfPartitions) {
Collections.sort(allRequests, new Comparator<AppDeploymentRequest>() {
@Override
public int compare(AppDeploymentRequest o1, AppDeploymentRequest o2) {
return o1.getDefinition().getName().compareTo(o2.getDefinition().getName());
}
});
for(int i = 4; i < (numberOfPartitions + 4); i++) {
AppDeploymentRequest request = allRequests.get(i - 4);
assertEquals(this.resource, request.getResource());
assertEquals(0, request.getEnvironmentProperties().size());
AppDefinition appDefinition = request.getDefinition();
assertEquals("partitionedJobTask:partitionedJob:step1:partition" + (i - 3), appDefinition.getName());
assertEquals("1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID));
assertEquals(String.valueOf(i), appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID));
assertEquals("step1", appDefinition.getProperties().get(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME));
}
}
private void validateConstructorValidation(TaskLauncher taskLauncher, JobExplorer jobExplorer, Resource resource, String stepName, String expectedMessage) {
try {
new DeployerPartitionHandler(taskLauncher, jobExplorer, resource, stepName);
}
catch (IllegalArgumentException iae) {
assertEquals(expectedMessage, iae.getMessage());
}
}
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright 2016 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.cloud.task.batch.partition;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.NoSuchStepException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.env.Environment;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
/**
* @author Michael Minella
*/
public class DeployerStepExecutionHandlerTests {
@Mock
private JobExplorer jobExplorer;
@Mock
private JobRepository jobRepository;
@Mock
private Environment environment;
@Mock
private ListableBeanFactory beanFactory;
@Mock
private Step step;
@Captor
private ArgumentCaptor<StepExecution> stepExecutionArgumentCaptor;
private DeployerStepExecutionHandler handler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.handler = new DeployerStepExecutionHandler(this.beanFactory, this.jobExplorer, this.jobRepository);
ReflectionTestUtils.setField(this.handler, "environment", this.environment);
}
@Test
public void testConstructorValidation() {
validateConstructorValidation(null, null, null, "A beanFactory is required");
validateConstructorValidation(this.beanFactory, null, null, "A jobExplorer is required");
validateConstructorValidation(this.beanFactory, this.jobExplorer, null, "A jobRepository is required");
new DeployerStepExecutionHandler(this.beanFactory, this.jobExplorer, this.jobRepository);
}
@Test
public void testValidationOfRequestValuesExist() throws Exception {
validateEnvironmentConfiguration("A job execution id is required", new String[0]);
validateEnvironmentConfiguration("A step execution id is required", new String[] {DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID});
validateEnvironmentConfiguration("A step name is required", new String[] {DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID, DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID});
}
@Test
public void testValidationOfRequestStepFound() throws Exception {
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn(true);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("foo");
when(this.beanFactory.getBeanNamesForType(Step.class)).thenReturn(new String[] {"bar", "baz"});
try {
this.handler.run();
}
catch (IllegalArgumentException iae) {
assertEquals("The step requested cannot be found in the provided BeanFactory", iae.getMessage());
}
}
@Test
public void testMissingStepExecution() throws Exception {
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn(true);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("foo");
when(this.beanFactory.getBeanNamesForType(Step.class)).thenReturn(new String[] {"foo", "bar", "baz"});
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn("2");
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn("1");
try {
this.handler.run();
}
catch (NoSuchStepException nsse) {
assertEquals("No StepExecution could be located for step execution id 2 within job execution 1", nsse.getMessage());
}
}
@Test
public void testRunSuccessful() throws Exception {
StepExecution workerStep = new StepExecution("workerStep", new JobExecution(1L), 2L);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn(true);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("workerStep");
when(this.beanFactory.getBeanNamesForType(Step.class)).thenReturn(new String[] {"workerStep", "foo", "bar"});
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn("2");
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn("1");
when(this.jobExplorer.getStepExecution(1L, 2L)).thenReturn(workerStep);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("workerStep");
when(this.beanFactory.getBean("workerStep", Step.class)).thenReturn(this.step);
handler.run();
verify(this.step).execute(workerStep);
verifyZeroInteractions(this.jobRepository);
}
@Test
public void testJobInterruptedException() throws Exception {
StepExecution workerStep = new StepExecution("workerStep", new JobExecution(1L), 2L);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn(true);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("workerStep");
when(this.beanFactory.getBeanNamesForType(Step.class)).thenReturn(new String[] {"workerStep", "foo", "bar"});
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn("2");
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn("1");
when(this.jobExplorer.getStepExecution(1L, 2L)).thenReturn(workerStep);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("workerStep");
when(this.beanFactory.getBean("workerStep", Step.class)).thenReturn(this.step);
doThrow(new JobInterruptedException("expected")).when(this.step).execute(workerStep);
handler.run();
verify(this.jobRepository).update(this.stepExecutionArgumentCaptor.capture());
assertEquals(BatchStatus.STOPPED, this.stepExecutionArgumentCaptor.getValue().getStatus());
}
@Test
public void testRuntimeException() throws Exception {
StepExecution workerStep = new StepExecution("workerStep", new JobExecution(1L), 2L);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn(true);
when(this.environment.containsProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn(true);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("workerStep");
when(this.beanFactory.getBeanNamesForType(Step.class)).thenReturn(new String[] {"workerStep", "foo", "bar"});
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_EXECUTION_ID)).thenReturn("2");
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_JOB_EXECUTION_ID)).thenReturn("1");
when(this.jobExplorer.getStepExecution(1L, 2L)).thenReturn(workerStep);
when(this.environment.getProperty(DeployerPartitionHandler.SPRING_CLOUD_TASK_STEP_NAME)).thenReturn("workerStep");
when(this.beanFactory.getBean("workerStep", Step.class)).thenReturn(this.step);
doThrow(new RuntimeException("expected")).when(this.step).execute(workerStep);
handler.run();
verify(this.jobRepository).update(this.stepExecutionArgumentCaptor.capture());
assertEquals(BatchStatus.FAILED, this.stepExecutionArgumentCaptor.getValue().getStatus());
}
private void validateEnvironmentConfiguration(String errorMessage, String[] properties) throws Exception {
for (String property : properties) {
when(this.environment.containsProperty(property)).thenReturn(true);
}
try {
this.handler.run();
}
catch (IllegalArgumentException iae) {
assertEquals(errorMessage, iae.getMessage());
}
}
private void validateConstructorValidation(BeanFactory beanFactory, JobExplorer jobExplorer, JobRepository jobRepository, String message) {
try {
new DeployerStepExecutionHandler(beanFactory, jobExplorer, jobRepository);
}
catch (IllegalArgumentException iae) {
assertEquals(message, iae.getMessage());
}
}
}