Added the implementation of a post processor to lazily init partition

related artifacts
* Added BeanFactoryPostProcessor
* Fixed StepContext creation (now creates one per thread)
* Fixed StepContext exit status processing (now keeps track of being set
 manualy or not
* Created new Flow implementation to handle JSR-352 state transitions
* Updated flow parsing to not default to completed if no other
 transitions are provided
This commit is contained in:
Michael Minella
2013-12-07 12:14:47 -06:00
parent d8b435e211
commit 54a5be9eb2
18 changed files with 658 additions and 55 deletions

View File

@@ -26,7 +26,7 @@ import org.springframework.batch.core.job.flow.support.state.AbstractState;
*/
public class StateSupport extends AbstractState {
private FlowExecutionStatus status;
protected FlowExecutionStatus status;
public StateSupport(String name) {
this(name, FlowExecutionStatus.COMPLETED);

View File

@@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.job.flow.FlowExecution;
@@ -41,9 +42,14 @@ import org.springframework.batch.core.job.flow.StateSupport;
*/
public class SimpleFlowTests {
private SimpleFlow flow = new SimpleFlow("job");
protected SimpleFlow flow;
private FlowExecutor executor = new JobFlowExecutorSupport();
protected FlowExecutor executor = new JobFlowExecutorSupport();
@Before
public void setUp() {
flow = new SimpleFlow("job");
}
@Test(expected = IllegalArgumentException.class)
public void testEmptySteps() throws Exception {
@@ -205,22 +211,13 @@ public class SimpleFlowTests {
assertNull(state);
}
private List<StateTransition> collect(StateTransition s1, StateTransition s2) {
protected List<StateTransition> collect(StateTransition... states) {
List<StateTransition> list = new ArrayList<StateTransition>();
list.add(s1);
list.add(s2);
return list;
}
private List<StateTransition> collect(StateTransition s1, StateTransition s2, StateTransition s3) {
List<StateTransition> list = collect(s1, s2);
list.add(s3);
return list;
}
for (StateTransition stateTransition : states) {
list.add(stateTransition);
}
private List<StateTransition> collect(StateTransition s1, StateTransition s2, StateTransition s3, StateTransition s4) {
List<StateTransition> list = collect(s1, s2, s3);
list.add(s4);
return list;
}
@@ -228,7 +225,7 @@ public class SimpleFlowTests {
* @author Dave Syer
*
*/
private static class StubState extends StateSupport {
protected static class StubState extends StateSupport {
/**
* @param string

View File

@@ -0,0 +1,168 @@
package org.springframework.batch.core.jsr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.batch.runtime.context.StepContext;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
public class StepContextFactoryBeanTests {
private StepContextFactoryBean factory;
@Mock
private BatchPropertyContext propertyContext;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
factory = new StepContextFactoryBean();
factory.setBatchPropertyContext(propertyContext);
}
@Test(expected=FactoryBeanNotInitializedException.class)
public void testNoStepExecutionRegistered() throws Exception {
factory.getObject();
}
@Test
public void getObjectSingleThread() throws Exception {
StepSynchronizationManager.register(new StepExecution("step1", new JobExecution(5l), 3l));
StepContext context1 = factory.getObject();
StepContext context2 = factory.getObject();
assertTrue(context1 == context2);
assertEquals(3l, context1.getStepExecutionId());
StepSynchronizationManager.close();
StepSynchronizationManager.register(new StepExecution("step2", new JobExecution(5l), 2l));
StepContext context3 = factory.getObject();
StepContext context4 = factory.getObject();
assertTrue(context3 == context4);
assertTrue(context3 != context2);
assertEquals(2l, context3.getStepExecutionId());
StepSynchronizationManager.close();
}
@Test
public void getObjectSingleThreadWithProperties() throws Exception {
Properties props = new Properties();
props.put("key1", "value1");
when(propertyContext.getStepProperties("step3")).thenReturn(props);
StepSynchronizationManager.register(new StepExecution("step3", new JobExecution(5l), 3l));
StepContext context1 = factory.getObject();
StepContext context2 = factory.getObject();
assertTrue(context1 == context2);
assertEquals(3l, context1.getStepExecutionId());
assertEquals("value1", context1.getProperties().get("key1"));
StepSynchronizationManager.close();
}
@Test
public void getObjectMultiThread() throws Exception {
List<Future<StepContext>> stepContexts = new ArrayList<Future<StepContext>>();
AsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
for(int i = 0; i < 4; i++) {
final long count = i;
stepContexts.add(executor.submit(new Callable<StepContext>() {
@Override
public StepContext call() throws Exception {
try {
StepSynchronizationManager.register(new StepExecution("step" + count, new JobExecution(count)));
StepContext context = factory.getObject();
Thread.sleep(1000l);
return context;
} catch (Throwable ignore) {
return null;
}finally {
StepSynchronizationManager.release();
}
}
}));
}
Set<StepContext> contexts = new HashSet<StepContext>();
for (Future<StepContext> future : stepContexts) {
contexts.add(future.get());
}
assertEquals(4, contexts.size());
}
@Test
public void getObjectMultiThreadWithProperties() throws Exception {
for(int i = 0; i < 4; i++) {
Properties props = new Properties();
props.put("step" + i, "step" + i + "value");
when(propertyContext.getStepProperties("step" + i)).thenReturn(props);
}
List<Future<StepContext>> stepContexts = new ArrayList<Future<StepContext>>();
AsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
for(int i = 0; i < 4; i++) {
final long count = i;
stepContexts.add(executor.submit(new Callable<StepContext>() {
@Override
public StepContext call() throws Exception {
try {
StepSynchronizationManager.register(new StepExecution("step" + count, new JobExecution(count)));
StepContext context = factory.getObject();
Thread.sleep(1000l);
return context;
} catch (Throwable ignore) {
return null;
}finally {
StepSynchronizationManager.release();
}
}
}));
}
Set<StepContext> contexts = new HashSet<StepContext>();
for (Future<StepContext> future : stepContexts) {
contexts.add(future.get());
}
assertEquals(4, contexts.size());
for (StepContext stepContext : contexts) {
assertEquals(stepContext.getStepName() + "value", stepContext.getProperties().get(stepContext.getStepName()));
}
}
}

View File

@@ -32,12 +32,15 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.util.ExecutionContextUserSupport;
import org.springframework.util.ClassUtils;
public class StepContextTests {
private StepExecution stepExecution;
private StepContext stepContext;
private ExecutionContext executionContext;
private ExecutionContextUserSupport executionContextUserSupport = new ExecutionContextUserSupport(ClassUtils.getShortName(StepContext.class));
@Before
public void setUp() throws Exception {
@@ -68,6 +71,8 @@ public class StepContextTests {
@Test
public void testBasicProperties() {
assertEquals(javax.batch.runtime.BatchStatus.STARTED, stepContext.getBatchStatus());
assertEquals(null, stepContext.getExitStatus());
stepContext.setExitStatus("customExitStatus");
assertEquals("customExitStatus", stepContext.getExitStatus());
assertEquals(5l, stepContext.getStepExecutionId());
assertEquals("testStep", stepContext.getStepName());
@@ -121,7 +126,7 @@ public class StepContextTests {
String data = "saved data";
stepContext.setPersistentUserData(data);
assertEquals(data, stepContext.getPersistentUserData());
assertEquals(data, executionContext.get("batch_jsr_persistentUserData"));
assertEquals(data, executionContext.get(executionContextUserSupport.getKey("batch_jsr_persistentUserData")));
}
@Test

View File

@@ -15,11 +15,15 @@ import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.util.ExecutionContextUserSupport;
import org.springframework.util.ClassUtils;
public class StepExecutionTests {
private StepExecution stepExecution;
private javax.batch.runtime.StepExecution jsrStepExecution;
//The API that sets the persisted user data is on the StepContext so the key within the ExecutionContext is StepContext
private ExecutionContextUserSupport executionContextUserSupport = new ExecutionContextUserSupport(ClassUtils.getShortName(StepContext.class));
@Before
public void setUp() throws Exception {
@@ -39,7 +43,7 @@ public class StepExecutionTests {
stepExecution.setWriteSkipCount(8);
stepExecution.setStartTime(new Date(0));
stepExecution.setEndTime(new Date(10000000));
stepExecution.getExecutionContext().put("batch_jsr_persistentUserData", "persisted data");
stepExecution.getExecutionContext().put(executionContextUserSupport.getKey("batch_jsr_persistentUserData"), "persisted data");
jsrStepExecution = new org.springframework.batch.core.jsr.StepExecution(stepExecution);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.batch.core.jsr.configuration.xml;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import java.util.List;
@@ -38,7 +40,6 @@ import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
/**
* <p>

View File

@@ -0,0 +1,74 @@
/*
* Copyright 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.jsr.job.flow.support;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.flow.FlowExecution;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.State;
import org.springframework.batch.core.job.flow.StateSupport;
import org.springframework.batch.core.job.flow.support.JobFlowExecutorSupport;
import org.springframework.batch.core.job.flow.support.SimpleFlowTests;
import org.springframework.batch.core.job.flow.support.StateTransition;
public class DefaultFlowTests extends SimpleFlowTests {
@Override
@Before
public void setUp() {
flow = new DefaultFlow("flow1");
}
@Test
public void testNextBasedOnBatchStatus() throws Exception {
StepExecution stepExecution = new StepExecution("step1", new JobExecution(5l));
stepExecution.setExitStatus(new ExitStatus("unmapped exit code"));
stepExecution.setStatus(BatchStatus.FAILED);
executor = new FlowExecutor(stepExecution);
State startState = new StateSupport("step1", new FlowExecutionStatus("unmapped exit code"));
State endState = new StateSupport("failed", FlowExecutionStatus.FAILED);
StateTransition failureTransition = StateTransition.createStateTransition(startState, "FAILED", "failed");
StateTransition endTransition = StateTransition.createEndStateTransition(endState);
flow.setStateTransitions(collect(failureTransition, endTransition));
flow.afterPropertiesSet();
FlowExecution execution = flow.start(executor);
assertEquals(FlowExecutionStatus.FAILED, execution.getStatus());
assertEquals("failed", execution.getName());
}
public static class FlowExecutor extends JobFlowExecutorSupport {
private StepExecution stepExecution;
public FlowExecutor(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
@Override
public StepExecution getStepExecution() {
return stepExecution;
}
}
}