Add Spring Integration prototype code

This commit is contained in:
dsyer
2008-06-13 10:29:14 +00:00
parent 77716240fc
commit 615c90eb5e
57 changed files with 4465 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
/*
* 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.integration.batch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
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.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
/**
* @author Dave Syer
*
*/
public class JobRepositorySupport implements JobRepository {
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#createJobExecution(org.springframework.batch.core.Job, org.springframework.batch.core.JobParameters)
*/
public JobExecution createJobExecution(Job job, JobParameters jobParameters)
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
return new JobExecution(new JobInstance(0L, jobParameters, job.getName()));
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#getLastStepExecution(org.springframework.batch.core.JobInstance, org.springframework.batch.core.Step)
*/
public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
return null;
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#getStepExecutionCount(org.springframework.batch.core.JobInstance, org.springframework.batch.core.Step)
*/
public int getStepExecutionCount(JobInstance jobInstance, Step step) {
return 0;
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#saveOrUpdate(org.springframework.batch.core.JobExecution)
*/
public void saveOrUpdate(JobExecution jobExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#saveOrUpdate(org.springframework.batch.core.StepExecution)
*/
public void saveOrUpdate(StepExecution stepExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#saveOrUpdateExecutionContext(org.springframework.batch.core.StepExecution)
*/
public void saveOrUpdateExecutionContext(StepExecution stepExecution) {
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.integration.batch;
import java.util.List;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
public class JobSupport implements Job {
String name;
public JobSupport(String name){
this.name = name;
}
public void execute(JobExecution execution) throws JobExecutionException {
// TODO Auto-generated method stub
}
public String getName() {
return name;
}
@SuppressWarnings("unchecked")
public List getSteps() {
// TODO Auto-generated method stub
return null;
}
public boolean isRestartable() {
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -0,0 +1,57 @@
package org.springframework.integration.batch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.annotation.Handler;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = "/integration-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@MessageEndpoint(input = "smokein", output = "smokeout")
public class SmokeTests {
@Autowired
@Qualifier("smokein")
private MessageChannel smokein;
@Autowired
@Qualifier("smokeout")
private MessageChannel smokeout;
// This has to be static because the MessageBus registers the handler
// more than once (every time a test instance is created), but only one of
// them will get the message.
private volatile static int count = 0;
@Handler
public String process(String message) {
count++;
String result = message + ": " + count;
return result;
}
@Test
public void testDummyWithSimpleAssert() throws Exception {
assertTrue(true);
}
@Test
public void testVanillaSendAndReceive() throws Exception {
smokein.send(new GenericMessage<String>("foo"));
Message<?> message = smokeout.receive(100);
String result = (String) (message == null ? null : message.getPayload());
assertEquals("foo: 1", result);
assertEquals(1, count);
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.integration.batch;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
/**
* @author Dave Syer
*
*/
public class StepSupport implements Step {
private String name;
private int startLimit;
/**
* @param name
*/
public StepSupport(String name) {
super();
this.name = name;
}
/* (non-Javadoc)
* @see org.springframework.batch.core.Step#execute(org.springframework.batch.core.StepExecution)
*/
public void execute(StepExecution stepExecution) throws JobInterruptedException {
}
/* (non-Javadoc)
* @see org.springframework.batch.core.Step#getName()
*/
public String getName() {
return name;
}
/* (non-Javadoc)
* @see org.springframework.batch.core.Step#getStartLimit()
*/
public int getStartLimit() {
return startLimit;
}
/* (non-Javadoc)
* @see org.springframework.batch.core.Step#isAllowStartIfComplete()
*/
public boolean isAllowStartIfComplete() {
return false;
}
/**
* Public setter for the startLimit.
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
}

View File

@@ -0,0 +1,330 @@
package org.springframework.integration.batch.chunk;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
import org.springframework.batch.core.repository.support.SimpleJobRepository;
import org.springframework.batch.core.step.item.SimpleStepFactoryBean;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ChunkMessageItemWriterIntegrationTests {
private ChunkMessageChannelItemWriter writer = new ChunkMessageChannelItemWriter();
@Autowired
@Qualifier("requests")
private MessageChannel requests;
@Autowired
@Qualifier("replies")
private MessageChannel replies;
private SimpleStepFactoryBean factory;
private SimpleJobRepository jobRepository;
private static long jobCounter;
@Before
public void setUp() {
factory = new SimpleStepFactoryBean();
jobRepository = new SimpleJobRepository(new MapJobInstanceDao(),
new MapJobExecutionDao(), new MapStepExecutionDao());
factory.setJobRepository(jobRepository);
factory.setTransactionManager(new ResourcelessTransactionManager());
factory.setBeanName("step");
factory.setItemWriter(writer);
factory.setCommitInterval(4);
writer.setReplyChannel(replies);
writer.setRequestChannel(requests);
TestItemWriter.count = 0;
// Drain queues
Message<?> message = requests.receive(10);
while (message!=null) {
System.err.println(message);
message = requests.receive(10);
}
message = replies.receive(10);
while (message!=null) {
System.err.println(message);
message = replies.receive(10);
}
}
@After
public void tearDown() {
while(requests.receive(10L)!=null) {}
while(replies.receive(10L)!=null) {}
}
@Test
public void testOpenWithNoState() throws Exception {
writer.open(new ExecutionContext());
}
@Test
public void testUpdateAndOpenWithState() throws Exception {
ExecutionContext executionContext = new ExecutionContext();
writer.update(executionContext);
writer.open(executionContext);
assertEquals(0, executionContext
.getLong(ChunkMessageChannelItemWriter.EXPECTED));
assertEquals(0, executionContext
.getLong(ChunkMessageChannelItemWriter.ACTUAL));
}
@Test
public void testVanillaIteration() throws Exception {
factory.setItemReader(new ListItemReader(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("1,2,3,4,5,6"))));
Step step = (Step) factory.getObject();
StepExecution stepExecution = getStepExecution(step);
step.execute(stepExecution);
waitForResults(6, 10);
assertEquals(6, TestItemWriter.count);
assertEquals(6, stepExecution.getItemCount().intValue());
}
@Test
public void testSimulatedRestart() throws Exception {
factory.setItemReader(new ListItemReader(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("1,2,3,4,5,6"))));
Step step = (Step) factory.getObject();
StepExecution stepExecution = getStepExecution(step);
// Set up context with two messages (chunks) in the backlog
stepExecution.getExecutionContext().putLong(
ChunkMessageChannelItemWriter.EXPECTED, 6);
stepExecution.getExecutionContext().putLong(
ChunkMessageChannelItemWriter.ACTUAL, 4);
// And make the back log real
requests.send(getSimpleMessage("foo", stepExecution.getJobExecution().getJobId()));
requests.send(getSimpleMessage("bar", stepExecution.getJobExecution().getJobId()));
step.execute(stepExecution);
waitForResults(8, 10);
assertEquals(8, TestItemWriter.count);
assertEquals(6, stepExecution.getItemCount().intValue());
}
@Test
public void testSimulatedRestartWithBadMessagesFromAnotherJob() throws Exception {
factory.setItemReader(new ListItemReader(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("1,2,3,4,5,6"))));
Step step = (Step) factory.getObject();
StepExecution stepExecution = getStepExecution(step);
// Set up context with two messages (chunks) in the backlog
stepExecution.getExecutionContext().putLong(
ChunkMessageChannelItemWriter.EXPECTED, 3);
stepExecution.getExecutionContext().putLong(
ChunkMessageChannelItemWriter.ACTUAL, 2);
// And make the back log real
requests.send(getSimpleMessage("foo", new Long(4321)));
try {
step.execute(stepExecution);
fail("Expected UnexpectedJobExecutionException");
} catch (UnexpectedJobExecutionException e) {
String message = e.getCause().getMessage();
assertTrue("Message does not contain 'wrong job': "+message, message.contains("wrong job"));
}
waitForResults(1, 10);
assertEquals(1, TestItemWriter.count);
assertEquals(0, stepExecution.getItemCount().intValue());
}
/**
* @param jobId
* @param string
* @return
*/
@SuppressWarnings("unchecked")
private GenericMessage<ChunkRequest> getSimpleMessage(String string, Long jobId) {
ChunkRequest chunk = new ChunkRequest(StringUtils
.commaDelimitedListToSet(string), jobId, 0);
GenericMessage<ChunkRequest> message = new GenericMessage<ChunkRequest>(chunk);
return message;
}
@Test
public void testEarlyCompletionSignalledInHandler() throws Exception {
factory.setItemReader(new ListItemReader(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("1,bad,3,4,5,6"))));
factory.setCommitInterval(2);
Step step = (Step) factory.getObject();
StepExecution stepExecution = getStepExecution(step);
try {
step.execute(stepExecution);
fail("Expected AsynchronousFailureException");
} catch (AsynchronousFailureException e) {
assertTrue(e.getMessage().contains("bad"));
}
waitForResults(2, 10);
// The number of items processed is actually between 1 and 6, because
// the one that failed might have been processed out of order.
assertTrue(1 <= TestItemWriter.count);
assertTrue(6 >= TestItemWriter.count);
// But it should fail the step in any case
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
}
@Test
public void testSimulatedRestartWithNoBacklog() throws Exception {
factory.setItemReader(new ListItemReader(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("1,2,3,4,5,6"))));
Step step = (Step) factory.getObject();
StepExecution stepExecution = getStepExecution(step);
// Set up expectation of three messages (chunks) in the backlog
stepExecution.getExecutionContext().putLong(
ChunkMessageChannelItemWriter.EXPECTED, 6);
stepExecution.getExecutionContext().putLong(
ChunkMessageChannelItemWriter.ACTUAL, 3);
/*
* With no backlog we process all the items, but the listener can't
* reconcile the expected number of items with the actual. An infinite
* loop would be bad, so the best we can do is fail as fast as possible.
*/
try {
step.execute(stepExecution);
fail("Expected UnexpectedJobExecutionException");
} catch (UnexpectedJobExecutionException e) {
String message = e.getCause().getMessage();
assertTrue("Message did not contain 'timed out': " + message,
message.toLowerCase().contains("timed out"));
}
assertEquals(0, TestItemWriter.count);
assertEquals(0, stepExecution.getItemCount().intValue());
}
/**
* This one is flakey - we try to force it to wait until after the step to
* finish processing just by waiting for long enough.
*
* @throws Exception
*/
@Test
public void testFailureInStepListener() throws Exception {
factory.setItemReader(new ListItemReader(Arrays.asList(StringUtils
.commaDelimitedListToStringArray("wait,bad,3,4,5,6"))));
Step step = (Step) factory.getObject();
StepExecution stepExecution = getStepExecution(step);
step.execute(stepExecution);
waitForResults(2, 10);
// The number of items processed is actually between 1 and 6, because
// the one that failed might have been processed out of order.
assertTrue(1 <= TestItemWriter.count);
assertTrue(6 >= TestItemWriter.count);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
assertEquals(ExitStatus.FAILED.getExitCode(), stepExecution
.getExitStatus().getExitCode());
String exitDescription = stepExecution.getExitStatus()
.getExitDescription();
assertTrue("Exit description does not contain exception type name: "
+ exitDescription, exitDescription
.contains(AsynchronousFailureException.class.getName()));
}
// TODO: test failure in chunk handler
// TODO : test non-dispatch of empty chunk
/**
* @param expected
* @param maxWait
* @throws InterruptedException
*/
private void waitForResults(int expected, int maxWait) throws InterruptedException {
int count = 0;
while (TestItemWriter.count<expected && count<maxWait) {
count++;
Thread.sleep(10);
}
}
private StepExecution getStepExecution(Step step)
throws JobExecutionAlreadyRunningException, JobRestartException,
JobInstanceAlreadyCompleteException {
SimpleJob job = new SimpleJob();
job.setName("job");
JobExecution jobExecution = jobRepository.createJobExecution(job,
new JobParametersBuilder().addLong("job.counter", jobCounter++)
.toJobParameters());
StepExecution stepExecution = jobExecution.createStepExecution(step);
return stepExecution;
}
}

View File

@@ -0,0 +1,83 @@
package org.springframework.integration.batch.chunk;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.batch.core.SkipListener;
import org.springframework.batch.core.listener.SkipListenerSupport;
import org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy;
import org.springframework.batch.item.AbstractItemWriter;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.util.StringUtils;
public class ItemWriterChunkHandlerTests {
private ItemWriterChunkHandler handler = new ItemWriterChunkHandler();
protected int count = 0;
private SkipListenerSupport listener = new SkipListenerSupport() {
@Override
public void onSkipInWrite(Object item, Throwable t) {
count++;
}
};
@SuppressWarnings("unchecked")
@Test
public void testVanillaHandleChunk() {
handler.setItemWriter(new AbstractItemWriter() {
public void write(Object item) throws Exception {
count++;
}
});
ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"),
12L, 10));
assertEquals(0, response.getSkipCount());
assertEquals(new Long(12L), response.getJobId());
assertEquals(ExitStatus.CONTINUABLE, response.getExitStatus());
assertEquals(2, count);
}
@SuppressWarnings("unchecked")
@Test
public void testSetItemSkipPolicy() {
handler.setItemWriter(new AbstractItemWriter() {
public void write(Object item) throws Exception {
count++;
throw new RuntimeException("Planned failure");
}
});
handler.setItemSkipPolicy(new AlwaysSkipItemSkipPolicy());
ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"),
12L, 10));
assertEquals(2, response.getSkipCount());
assertEquals(new Long(12L), response.getJobId());
assertEquals(ExitStatus.CONTINUABLE, response.getExitStatus());
assertEquals(2, count);
}
@SuppressWarnings("unchecked")
@Test
public void testRegisterSkipListener() {
handler.setItemWriter(new AbstractItemWriter() {
public void write(Object item) throws Exception {
count++;
throw new RuntimeException("Planned failure");
}
});
handler.setItemSkipPolicy(new AlwaysSkipItemSkipPolicy());
handler.registerSkipListener(listener);
ChunkResponse response = handler.handleChunk(new ChunkRequest(StringUtils.commaDelimitedListToSet("foo,bar"),
12L, 10));
assertEquals(2, response.getSkipCount());
assertEquals(4, count);
}
@Test
public void testSetSkipListeners() {
handler.setSkipListeners(new SkipListener[] { listener });
testRegisterSkipListener();
}
}

View File

@@ -0,0 +1,49 @@
package org.springframework.integration.batch.chunk;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.item.AbstractItemWriter;
import org.springframework.stereotype.Component;
@Component
public class TestItemWriter extends AbstractItemWriter {
private static final Log logger = LogFactory.getLog(TestItemWriter.class);
/**
* Counts the number of chunks processed in the handler.
*/
public volatile static int count = 0;
/**
* Item that causes failure in handler.
*/
public final static String FAIL_ON = "bad";
/**
* Item that causes handler to wait to simulate delayed processing.
*/
public static final String WAIT_ON = "wait";
public void write(Object item) throws Exception {
count++;
logger.debug("Writing: "+item);
if (item.equals(WAIT_ON)) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Unexpected interruption.", e);
}
}
if (item.equals(FAIL_ON)) {
throw new IllegalStateException("Planned failure on: " + FAIL_ON);
}
}
}

View File

@@ -0,0 +1,200 @@
/*
* 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.integration.batch.file;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
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.JobParametersBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.FieldSet;
import org.springframework.batch.item.file.mapping.PassThroughFieldSetMapper;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.batch.JobRepositorySupport;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.channel.ThreadLocalChannel;
import org.springframework.integration.dispatcher.DirectChannel;
import org.springframework.integration.message.Message;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.ReflectionUtils;
/**
* @author Dave Syer
*
*/
public class FileToMessagesJobFactoryBeanTests {
private static final String FILE_INPUT_PATH = ResourcePayloadAsJobParameterStrategy.FILE_INPUT_PATH;
private FileToMessagesJobFactoryBean factory = new FileToMessagesJobFactoryBean();
private ThreadLocalChannel receiver = new ThreadLocalChannel();
private JobRepositorySupport jobRepository;
@Before
public void setUp() {
jobRepository = new JobRepositorySupport();
factory.setJobRepository(jobRepository);
factory.setTransactionManager(new ResourcelessTransactionManager());
FlatFileItemReader itemReader = new FlatFileItemReader();
itemReader.setFieldSetMapper(new PassThroughFieldSetMapper());
factory.setItemReader(itemReader);
DirectChannel channel = new DirectChannel();
factory.setChannel(channel);
channel.subscribe(this.receiver);
}
@After
public void tearDown() {
while(receiver.receive(10L)!=null) {}
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#setBeanName(java.lang.String)}.
* @throws Exception
*/
@Test
public void testSetBeanName() throws Exception {
assertNotNull(((Job) factory.getObject()).getName());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#setItemReader(org.springframework.batch.item.ItemReader)}.
*/
@Test
public void testSetItemReader() {
Method method = ReflectionUtils.findMethod(FileToMessagesJobFactoryBean.class, "setItemReader",
new Class<?>[] { ItemReader.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#setChannel(org.springframework.integration.channel.MessageChannel)}.
*/
@Test
public void testSetChannel() {
Method method = ReflectionUtils.findMethod(FileToMessagesJobFactoryBean.class, "setChannel",
new Class<?>[] { MessageChannel.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#setJobRepository(org.springframework.batch.core.repository.JobRepository)}.
*/
@Test
public void testSetJobRepository() {
Method method = ReflectionUtils.findMethod(FileToMessagesJobFactoryBean.class, "setJobRepository",
new Class<?>[] { JobRepository.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#setTransactionManager(org.springframework.transaction.PlatformTransactionManager)}.
*/
@Test
public void testSetTransactionManager() {
Method method = ReflectionUtils.findMethod(FileToMessagesJobFactoryBean.class, "setTransactionManager",
new Class<?>[] { PlatformTransactionManager.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#getObject()}.
* @throws Exception
*/
@Test
public void testGetObjectNotBroken() throws Exception {
assertNotNull(factory.getObject());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#getObjectType()}.
*/
@Test
public void testGetObjectType() {
FileToMessagesJobFactoryBean factory = new FileToMessagesJobFactoryBean();
assertEquals(Job.class, factory.getObjectType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.file.FileToMessagesJobFactoryBean#isSingleton()}.
*/
@Test
public void testIsSingleton() {
FileToMessagesJobFactoryBean factory = new FileToMessagesJobFactoryBean();
assertEquals(true, factory.isSingleton());
}
@SuppressWarnings("unchecked")
@Test
public void testVanillaJobExecution() throws Exception {
Job job = (Job) factory.getObject();
JobParameters jobParameters = new JobParametersBuilder().addString(FILE_INPUT_PATH, "classpath:/log4j.properties").toJobParameters();
JobExecution jobExecution = jobRepository.createJobExecution(job, jobParameters);
job.execute(jobExecution);
assertNotNull(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
FieldSet payload;
Message<FieldSet> message;
// first line from properties file
message = (Message<FieldSet>) receiver.receive(100L);
assertNotNull(message);
payload = message.getPayload();
assertNotNull(payload);
// second line from properties file
message = (Message<FieldSet>) receiver.receive(100L);
assertNotNull(message);
payload = message.getPayload();
assertNotNull(payload);
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.integration.batch.file;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.batch.core.JobParameters;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.integration.batch.file.ResourcePayloadAsJobParameterStrategy;
import org.springframework.integration.message.GenericMessage;
/**
* @author Dave Syer
*
*/
public class ResourcePayloadAsJobParameterStrategyTests {
/**
*
*/
private static final String INPUT_FILE_PATH = ResourcePayloadAsJobParameterStrategy.FILE_INPUT_PATH;
/**
* Test method for {@link org.springframework.integration.batch.file.ResourcePayloadAsJobParameterStrategy#getJobParameters(org.springframework.integration.message.Message)}.
*/
@Test
public void testGetJobParameters() {
ResourcePayloadAsJobParameterStrategy strategy = new ResourcePayloadAsJobParameterStrategy();
JobParameters parameters = strategy.getJobParameters(new GenericMessage<Resource>(new ClassPathResource("log4j.properties")));
assertTrue(parameters.getParameters().containsKey(INPUT_FILE_PATH));
}
/**
* Test method for {@link org.springframework.integration.batch.file.ResourcePayloadAsJobParameterStrategy#getJobParameters(org.springframework.integration.message.Message)}.
*/
@Test
public void testGetJobParametersWithWrongPayload() {
ResourcePayloadAsJobParameterStrategy strategy = new ResourcePayloadAsJobParameterStrategy();
try {
strategy.getJobParameters(new GenericMessage<String>("log4j.properties"));
fail("Expected ClassCastException");
} catch (ClassCastException e) {
String message = e.getMessage();
assertTrue("Wrong message: "+message, message.contains("String cannot be cast"));
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.integration.batch.file;
import static org.junit.Assert.assertNotNull;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.Resource;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Dave Syer
*
*/
@ContextConfiguration()
@RunWith(SpringJUnit4ClassRunner.class)
@MessageEndpoint(input = "resources", output = "requests")
public class ResourceSplitterIntegrationTests {
@Autowired
@Qualifier("resources")
private MessageChannel resources;
@Autowired
@Qualifier("requests")
private MessageChannel requests;
/*
* This is so cool (but see INT-190)...<br/>
*
* The incoming message is a Resource pattern, and it is converted to the
* correct payload type with Spring's default strategy
*/
@Splitter
public Resource[] handle(Resource[] message) {
List<Resource> list = Arrays.asList(message);
System.err.println(list);
return message;
}
@SuppressWarnings("unchecked")
@Test
public void testVanillaConversion() throws Exception {
resources.send(new GenericMessage<String>("classpath:*-context.xml"));
Message<Resource> message = (Message<Resource>) requests.receive(100L);
assertNotNull(message);
message = (Message<Resource>) requests.receive(100L);
assertNotNull(message);
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.integration.batch.item;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.Message;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Dave Syer
*
*/
@ContextConfiguration()
@RunWith(SpringJUnit4ClassRunner.class)
public class MessageChannelItemWriterIntegrationTests {
@Autowired
private MessageChannel channel;
@Autowired
private ItemWriter itemWriter;
@Test
public void testSend() throws Exception {
itemWriter.write("foo");
Message<?> message = channel.receive(10);
assertNotNull(message);
assertEquals("foo", message.getPayload());
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.integration.batch.item;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.channel.ThreadLocalChannel;
import org.springframework.integration.dispatcher.DirectChannel;
import org.springframework.integration.endpoint.HandlerEndpoint;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.Target;
import org.springframework.integration.util.ErrorHandler;
import org.springframework.util.ReflectionUtils;
/**
* @author Dave Syer
*
*/
public class MessageChannelItemWriterTests {
/**
* Test method for {@link org.springframework.integration.batch.item.MessageChannelItemWriter#setChannel(org.springframework.integration.channel.MessageChannel)}.
*/
@Test
public void testSetChannel() {
Method method = ReflectionUtils.findMethod(MessageChannelItemWriter.class, "setChannel", new Class<?>[] {MessageChannel.class});
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for {@link org.springframework.integration.batch.item.MessageChannelItemWriter#write(java.lang.Object)}.
* @throws Exception
*/
@Test
public void testWrite() throws Exception {
DirectChannel channel = new DirectChannel();
ThreadLocalChannel receiver = new ThreadLocalChannel();
channel.subscribe(receiver);
MessageChannelItemWriter writer = new MessageChannelItemWriter();
writer.setChannel(channel);
writer.write("foo");
Message<?> message = receiver.receive(10);
assertNotNull(message);
assertEquals("foo", message.getPayload());
}
/**
* Test method for {@link org.springframework.integration.batch.item.MessageChannelItemWriter#write(java.lang.Object)}.
* @throws Exception
*/
@Test
public void testWriteWithRollback() throws Exception {
DirectChannel channel = new DirectChannel();
channel.subscribe(new Target() {
public boolean send(Message<?> message) {
throw new RuntimeException("Planned failure");
}
});
MessageChannelItemWriter writer = new MessageChannelItemWriter();
writer.setChannel(channel);
try {
writer.write("foo");
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
assertEquals("Planned failure", e.getMessage());
}
}
/**
* Test method for {@link org.springframework.integration.batch.item.MessageChannelItemWriter#write(java.lang.Object)}.
* @throws Exception
*/
@Test
public void testWriteWithRollbackOnEndpoint() throws Exception {
DirectChannel channel = new DirectChannel();
HandlerEndpoint endpoint = new HandlerEndpoint(new MessageHandler() {
public Message<?> handle(Message<?> message) {
throw new RuntimeException("Planned failure");
}
});
// INT-184: this shouldn't be necessary?
endpoint.setErrorHandler(new ErrorHandler() {
public void handle(Throwable t) {
throw (RuntimeException)t;
}
});
channel.subscribe(endpoint);
endpoint.start();
MessageChannelItemWriter writer = new MessageChannelItemWriter();
writer.setChannel(channel);
try {
writer.write("foo");
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
assertEquals("Planned failure", e.getMessage());
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.integration.batch.job;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.configuration.JobLocator;
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;
/**
* @author Dave Syer
*
*/
@ContextConfiguration()
@RunWith(SpringJUnit4ClassRunner.class)
public class MessageOrientedStepIntegrationTests {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private JobLocator jobLocator;
@Test
public void testLaunchJob() throws Exception {
JobExecution jobExecution = jobLauncher.run(jobLocator.getJob("job"), new JobParameters());
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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.integration.batch.job;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
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.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.batch.JobRepositorySupport;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.channel.ThreadLocalChannel;
import org.springframework.integration.dispatcher.DirectChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.Target;
import org.springframework.util.ReflectionUtils;
/**
* @author Dave Syer
*
*/
public class MessageOrientedStepTests {
private MessageOrientedStep step = new MessageOrientedStep();
private JobExecution jobExecution;
private DirectChannel requestChannel;
private MessageChannel replyChannel;
/**
* @return
*/
@Before
public void createStep() {
replyChannel = new ThreadLocalChannel();
requestChannel = new DirectChannel();
step.setName("step");
step.setRequestChannel(requestChannel);
step.setReplyChannel(replyChannel);
step.setStartLimit(10);
step.setJobRepository(new JobRepositorySupport());
JobInstance jobInstance = new JobInstance(0L, new JobParameters(), "job");
jobExecution = new JobExecution(jobInstance);
}
/**
* Test method for
* {@link org.springframework.integration.batch.job.MessageOrientedStep#setRequestChannel(org.springframework.integration.channel.MessageChannel)}.
*/
@Test
public void testSetRequestChannel() {
Method method = ReflectionUtils.findMethod(MessageOrientedStep.class, "setRequestChannel",
new Class<?>[] { MessageChannel.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.job.MessageOrientedStep#setReplyChannel(org.springframework.integration.channel.MessageChannel)}.
*/
@Test
public void testSetReplyChannel() {
Method method = ReflectionUtils.findMethod(MessageOrientedStep.class, "setReplyChannel",
new Class<?>[] { MessageChannel.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.job.MessageOrientedStep#execute(org.springframework.batch.core.StepExecution)}.
* @throws Exception
* @throws
*/
@Test
public void testExecuteWithTimeout() throws Exception {
try {
step.execute(jobExecution.createStepExecution(step));
fail("Expected StepExecutionTimeoutException");
}
catch (StepExecutionTimeoutException e) {
// expected
String message = e.getMessage();
assertTrue("Wrong message: " + message, message.contains("waiting for steps"));
}
}
@Test
public void testVanillaExecute() throws Exception {
requestChannel.subscribe(new Target() {
public boolean send(Message<?> message) {
JobExecutionRequest jobExecution = (JobExecutionRequest) message.getPayload();
jobExecution.setStatus(BatchStatus.COMPLETED);
return replyChannel.send(message);
}
});
step.execute(jobExecution.createStepExecution(step));
}
@Test
public void testExecuteWithFailure() throws Exception {
requestChannel.subscribe(new Target() {
public boolean send(Message<?> message) {
JobExecutionRequest jobExecution = (JobExecutionRequest) message.getPayload();
jobExecution.registerThrowable(new RuntimeException("Planned failure"));
return replyChannel.send(message);
}
});
try {
step.execute(jobExecution.createStepExecution(step));
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
String message = e.getMessage();
assertEquals("Wrong message: " + message, "Planned failure", message);
}
}
@Test
public void testExecuteOnRestart() throws Exception {
JobExecutionRequest jobExecutionRequest = new JobExecutionRequest(jobExecution);
jobExecutionRequest.setStatus(BatchStatus.COMPLETED);
// Send a message to the reply channel to simulate step that we were
// waiting for when we failed on the last execution.
replyChannel.send(new GenericMessage<JobExecutionRequest>(jobExecutionRequest));
StepExecution stepExecution = jobExecution.createStepExecution(step);
stepExecution.getExecutionContext().putString(MessageOrientedStep.WAITING, "true");
step.execute(stepExecution);
}
}

View File

@@ -0,0 +1,247 @@
/*
* 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.integration.batch.job;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StartLimitExceededException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.support.PropertiesConverter;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.batch.JobRepositorySupport;
import org.springframework.integration.batch.JobSupport;
import org.springframework.integration.batch.StepSupport;
import org.springframework.util.ReflectionUtils;
/**
* @author Dave Syer
*
*/
public class StepExecutionMessageHandlerTests {
/**
* Test method for
* {@link org.springframework.integration.batch.job.StepExecutionMessageHandler#setStep(org.springframework.batch.core.Step)}.
*/
@Test
public void testSetStep() {
Method method = ReflectionUtils.findMethod(StepExecutionMessageHandler.class, "setStep",
new Class<?>[] { Step.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.job.StepExecutionMessageHandler#setJobRepository(org.springframework.batch.core.repository.JobRepository)}.
*/
@Test
public void testSetJobRepository() {
Method method = ReflectionUtils.findMethod(StepExecutionMessageHandler.class, "setJobRepository",
new Class<?>[] { JobRepository.class });
assertNotNull(method);
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
assertEquals(1, annotations.length);
assertEquals(Required.class, annotations[0].annotationType());
}
/**
* Test method for
* {@link org.springframework.integration.batch.job.StepExecutionMessageHandler#handle(org.springframework.integration.message.Message)}.
* @throws Exception
* @throws JobRestartException
* @throws JobExecutionAlreadyRunningException
*/
@SuppressWarnings("unchecked")
@Test
public void testVanillaHandle() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport();
StepExecutionMessageHandler handler = createHandler(jobRepository);
JobExecutionRequest message = handler.handle(new JobExecutionRequest(jobRepository.createJobExecution(
new JobSupport("job"), new JobParameters())));
assertEquals(1, message.getJobExecution().getStepExecutions().size());
assertEquals(BatchStatus.COMPLETED, message.getStatus());
}
@SuppressWarnings("unchecked")
@Test
public void testHandleWithInputs() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport();
StepExecutionMessageHandler handler = createHandler(jobRepository);
handler.setInputKeys(new String[] { "foo" });
JobExecutionRequest jobExecutionRequest = new JobExecutionRequest(jobRepository.createJobExecution(
new JobSupport("job"), new JobParameters()));
jobExecutionRequest.setAttribute("foo", "bar");
JobExecutionRequest message = handler.handle(jobExecutionRequest);
assertEquals(1, message.getJobExecution().getStepExecutions().size());
StepExecution stepExecution = (StepExecution) message.getJobExecution().getStepExecutions().iterator().next();
assertTrue(stepExecution.getExecutionContext().containsKey("foo"));
}
@SuppressWarnings("unchecked")
@Test
public void testHandleWithInputsAndOutputs() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport();
StepExecutionMessageHandler handler = createHandler(jobRepository);
handler.setInputKeys(new String[] { "foo" });
handler.setOutputKeys(new String[] { "bar" });
JobExecutionRequest jobExecutionRequest = new JobExecutionRequest(jobRepository.createJobExecution(
new JobSupport("job"), new JobParameters()));
jobExecutionRequest.setAttribute("foo", "bar");
// The step has to add the output attribute to the context
handler.setStep(new StepSupport("step") {
@Override
public void execute(StepExecution stepExecution) throws JobInterruptedException {
stepExecution.getExecutionContext().putString("bar", "spam");
}
});
handler.handle(jobExecutionRequest);
assertFalse(jobExecutionRequest.hasAttribute("foo"));
assertTrue(jobExecutionRequest.hasAttribute("bar"));
}
@SuppressWarnings("unchecked")
@Test
public void testHandleFailedJob() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport();
StepExecutionMessageHandler handler = createHandler(jobRepository);
JobExecution jobExecution = jobRepository.createJobExecution(new JobSupport("job"), new JobParameters());
jobExecution.setStatus(BatchStatus.FAILED);
JobExecutionRequest message = handler.handle(new JobExecutionRequest(jobExecution));
assertEquals(0, message.getJobExecution().getStepExecutions().size());
}
@SuppressWarnings("unchecked")
@Test
public void testHandleRestart() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport() {
@Override
public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
StepExecution stepExecution = new StepExecution(step.getName(), new JobExecution(jobInstance));
stepExecution.setStatus(BatchStatus.FAILED);
stepExecution.setExecutionContext(new ExecutionContext(PropertiesConverter
.stringToProperties("foo=bar")));
return stepExecution;
}
/*
* (non-Javadoc)
* @see org.springframework.integration.batch.JobRepositorySupport#getStepExecutionCount(org.springframework.batch.core.JobInstance,
* org.springframework.batch.core.Step)
*/
@Override
public int getStepExecutionCount(JobInstance jobInstance, Step step) {
return 1;
}
};
StepExecutionMessageHandler handler = createHandler(jobRepository);
JobExecution jobExecution = jobRepository.createJobExecution(new JobSupport("job"), new JobParameters());
JobExecutionRequest message = handler.handle(new JobExecutionRequest(jobExecution));
assertNotNull(message);
assertEquals(1, jobExecution.getStepExecutions().size());
StepExecution stepExecution = (StepExecution) jobExecution.getStepExecutions().iterator().next();
assertTrue(stepExecution.getExecutionContext().containsKey("foo"));
}
@SuppressWarnings("unchecked")
@Test
public void testHandleRestartAlreadyComplete() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport() {
@Override
public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
StepExecution stepExecution = new StepExecution(step.getName(), new JobExecution(jobInstance));
stepExecution.setStatus(BatchStatus.COMPLETED);
stepExecution.setExecutionContext(new ExecutionContext(PropertiesConverter
.stringToProperties("foo=bar")));
return stepExecution;
}
};
StepExecutionMessageHandler handler = createHandler(jobRepository);
JobExecution jobExecution = jobRepository.createJobExecution(new JobSupport("job"), new JobParameters());
JobExecutionRequest message = handler.handle(new JobExecutionRequest(jobExecution));
assertNotNull(message);
assertEquals(1, jobExecution.getStepExecutions().size());
StepExecution stepExecution = (StepExecution) jobExecution.getStepExecutions().iterator().next();
assertEquals(BatchStatus.STARTING, stepExecution.getStatus());
// We expect to get the context from the previous execution, even if we
// do not execute
assertTrue(stepExecution.getExecutionContext().containsKey("foo"));
}
@SuppressWarnings("unchecked")
@Test
public void testHandleRestartStartLimitExceeded() throws Exception {
JobRepositorySupport jobRepository = new JobRepositorySupport() {
@Override
public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
return new StepExecution(step.getName(), new JobExecution(jobInstance));
}
@Override
public int getStepExecutionCount(JobInstance jobInstance, Step step) {
// sufficiently high restart count
return 100;
}
};
StepExecutionMessageHandler handler = createHandler(jobRepository);
JobExecution jobExecution = jobRepository.createJobExecution(new JobSupport("job"), new JobParameters());
JobExecutionRequest message = handler.handle(new JobExecutionRequest(jobExecution));
assertNotNull(message);
assertEquals(1, jobExecution.getStepExecutions().size());
JobExecutionRequest payload = message;
assertEquals(BatchStatus.FAILED, payload.getStatus());
assertTrue(payload.hasErrors());
Throwable error = payload.getLastThrowable();
assertTrue(error instanceof StartLimitExceededException);
String text = error.getMessage();
assertTrue("Wrong exit description: " + text, text.toLowerCase().contains("start limit"));
}
/**
* @param jobRepository
* @return
*
*/
public StepExecutionMessageHandler createHandler(JobRepositorySupport jobRepository) {
StepExecutionMessageHandler handler = new StepExecutionMessageHandler();
StepSupport step = new StepSupport("step");
step.setStartLimit(10);
handler.setStep(step);
handler.setJobRepository(jobRepository);
return handler;
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.integration.batch.job;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.ExitStatus;
/**
* @author Dave Syer
*
*/
public class TestTasklet implements Tasklet {
/* (non-Javadoc)
* @see org.springframework.batch.core.step.tasklet.Tasklet#execute()
*/
public ExitStatus execute() throws Exception {
return ExitStatus.FINISHED;
}
}

View File

@@ -0,0 +1,58 @@
package org.springframework.integration.batch.launch;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.JobExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.StringMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration()
@RunWith(SpringJUnit4ClassRunner.class)
public class JobLaunchingMessageHandlerIntegrationTests {
@Autowired @Qualifier("requests")
private MessageChannel requestChannel;
@Autowired @Qualifier("response")
private MessageChannel responseChannel;
@Before
public void setUp(){
requestChannel.purge(null);
responseChannel.purge(null);
}
@Test @DirtiesContext @SuppressWarnings("unchecked")
public void testNoReply(){
requestChannel.send(new StringMessage("testJob"));
Message<JobExecution> executionMessage = (Message<JobExecution>)responseChannel.receive(1000);
assertNull("JobExecution message received when no return address set", executionMessage);
}
@SuppressWarnings("unchecked")
@Test @DirtiesContext
public void testReply(){
StringMessage trigger = new StringMessage("testJob");
trigger.getHeader().setProperty("dontclash", "12");
trigger.getHeader().setReturnAddress("response");
requestChannel.send(trigger);
Message<JobExecution> executionMessage = (Message<JobExecution>)responseChannel.receive(1000);
assertNotNull("No response received", executionMessage);
JobExecution execution = executionMessage.getPayload();
assertNotNull("JobExectuion not returned", execution);
}
}

View File

@@ -0,0 +1,82 @@
package org.springframework.integration.batch.launch;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.batch.JobSupport;
import org.springframework.integration.bus.MessageBus;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.StringMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
@ContextConfiguration(locations = { "/job-execution-context.xml" })
public class JobLaunchingMessageHandlerTests extends AbstractJUnit4SpringContextTests {
JobLaunchingMessageHandler messageHandler;
StubJobLauncher jobLauncher;
// @Autowired
// @Qualifier("jobs") TODO: Qualifier seems to be broken here why ?????
public AbstractMessageChannel jobsChannel;
@Autowired
public MessageBus messageBus;
@Before
public void setUp() {
jobLauncher = new StubJobLauncher();
messageHandler = new JobLaunchingMessageHandler(jobLauncher, new StubMessageToJobStrategy());
jobsChannel = (AbstractMessageChannel) applicationContext.getBean("jobs");
}
@Test
public void testSimpleDelivery() throws Exception{
messageHandler.handle(new StringMessage("testjob"));
assertEquals("Wrong job count", 1, jobLauncher.jobs.size());
assertEquals("Wrong job name", jobLauncher.jobs.get(0).getName(), "testjob");
}
private static class StubJobLauncher implements JobLauncher {
List<Job> jobs = new ArrayList<Job>();
List<JobParameters> parameters = new ArrayList<JobParameters>();
AtomicLong jobId = new AtomicLong();
public JobExecution run(Job job, JobParameters jobParameters){
jobs.add(job);
parameters.add(jobParameters);
return new JobExecution(new JobInstance(jobId.getAndIncrement(), jobParameters, job.getName()));
}
}
private static class StubMessageToJobStrategy implements MessageToJobStrategy {
public Job getJob(Message<?> message) {
String name = (String) message.getPayload();
return new JobSupport(name);
}
}
}

View File

@@ -0,0 +1,83 @@
package org.springframework.integration.batch.launch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.integration.batch.JobSupport;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.message.StringMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = { "/job-execution-context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class JobLaunchingPostReceiveChannelAdapterTests extends AbstractJUnit4SpringContextTests {
JobLaunchingPostReceiveChannelInterceptor interceptor;
StubJobLauncher jobLauncher;
JobSupport job;
// @Autowired
// @Qualifier("jobs") TODO: Qualifier seems to be broken here why ?????
public AbstractMessageChannel jobsChannel;
@Before
public void setUp() {
job = new JobSupport(getClass().getName());
jobLauncher = new StubJobLauncher();
interceptor = new JobLaunchingPostReceiveChannelInterceptor(job, jobLauncher);
jobsChannel = (AbstractMessageChannel) applicationContext.getBean("jobs");
jobsChannel.addInterceptor(interceptor);
}
@Test
public void testJobPassedToLauncherCalled() {
StringMessage message = new StringMessage("test payload");
jobsChannel.send(message);
assertTrue("Job launcher called before recevie", (jobLauncher.jobs.size() == 0));
jobsChannel.receive();
assertEquals(job, jobLauncher.jobs.get(0));
}
@Test
public void testMessagePropertiesPassedAsJobParameters() {
StringMessage message = new StringMessage("test payload");
message.getHeader().setProperty("testOne", "a");
message.getHeader().setProperty("testTwo", "b");
jobsChannel.send(message);
jobsChannel.receive();
JobParameters parameters = jobLauncher.parameters.get(0);
assertEquals("a", parameters.getString("testOne"));
assertEquals("b", parameters.getString("testTwo"));
}
private static class StubJobLauncher implements JobLauncher {
List<Job> jobs = new ArrayList<Job>();
List<JobParameters> parameters = new ArrayList<JobParameters>();
public JobExecution run(Job job, JobParameters jobParameters){
jobs.add(job);
parameters.add(jobParameters);
return null;
}
}
}

View File

@@ -0,0 +1,462 @@
package org.springframework.integration.batch.retry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.batch.item.ItemKeyGenerator;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemRecoverer;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.repeat.interceptor.RepeatOperationsInterceptor;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.batch.repeat.support.RepeatTemplate;
import org.springframework.batch.retry.interceptor.StatefulRetryOperationsInterceptor;
import org.springframework.batch.support.PropertiesConverter;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
import org.springframework.context.Lifecycle;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.dispatcher.DirectChannel;
import org.springframework.integration.endpoint.SourceEndpoint;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.Source;
import org.springframework.integration.message.Target;
import org.springframework.integration.scheduling.MessagingTaskScheduler;
import org.springframework.integration.scheduling.PollingSchedule;
import org.springframework.integration.scheduling.SimpleMessagingTaskScheduler;
import org.springframework.integration.util.ErrorHandler;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.StringUtils;
public class PollableSourceRetryTests {
private Log logger = LogFactory.getLog(getClass());
private List<String> processed = new ArrayList<String>();
protected List<String> recovered = new ArrayList<String>();
public void add(String str) {
logger.debug("Adding: " + str);
processed.add(str);
}
ItemKeyGenerator itemKeyGenerator = new ItemKeyGenerator() {
@SuppressWarnings("unchecked")
public Object getKey(Object item) {
if (item == null) {
return "NULL";
}
if (item.getClass().isArray()) {
item = ((Object[]) item)[0];
}
return ((Message<Object>) item).getPayload();
}
};
@SuppressWarnings("unchecked")
@Test
public void testSimpleTransactionalPolling() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,b,c,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
return processed.add((String) payload);
}
};
Source<Object> source = getPollableSource(list);
DirectChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
endpoint.setDispatchAdviceChain(Arrays.asList(new Advice[] { getTransactionInterceptor() }));
endpoint.initializeTask();
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 2, 40);
assertEquals(2, processed.size());
assertEquals(beforeCount - list.size(), processed.size());
assertEquals("a", processed.get(0));
}
@SuppressWarnings("unchecked")
@Test
public void testNonTransactionalPollingWithRollback() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,b,c,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
processed.add((String) payload);
throw new RuntimeException("Planned failure: " + payload);
}
};
Source<Object> source = getPollableSource(list);
DirectChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 2, 20);
assertEquals(2, processed.size());
// None rolled back because there was no transaction
assertEquals(beforeCount - list.size(), 2);
assertEquals("a", processed.get(0));
assertEquals("b", processed.get(1));
}
@SuppressWarnings("unchecked")
@Test
public void testTransactionalHandlingWithUnconditionalRollback() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,b,c,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
processed.add((String) payload);
throw new RuntimeException("Planned failure: " + payload);
}
};
Source<Object> source = getPollableSource(list);
DirectChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
endpoint.setTaskAdviceChain(Arrays.asList(new Advice[] { getTransactionInterceptor() }));
endpoint.initializeTask();
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 2, 20);
assertEquals(2, processed.size());
// TODO: this would fail if exception not propagated: INT-184.
// All rolled back
assertEquals(beforeCount - list.size(), 0);
assertEquals("a", processed.get(0));
// processed twice and rolled back both times
assertEquals("a", processed.get(1));
}
@SuppressWarnings("unchecked")
@Test
public void testTransactionalHandlingWithRollback() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,b,fail,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
boolean result = processed.add((String) payload);
if ("fail".equals(payload)) {
throw new RuntimeException("Planned failure: " + payload);
}
return result;
}
};
Source<Object> source = getPollableSource(list);
DirectChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
endpoint.setTaskAdviceChain(Arrays.asList(new Advice[] { getTransactionInterceptor() }));
endpoint.initializeTask();
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 5, 20);
assertEquals(5, processed.size());
assertFalse("No messages got to processor", processed.isEmpty());
// First two TX succeed, and the rest rolled back so list has had two
// elements popped off
assertEquals(beforeCount - 2, list.size());
assertEquals("a", processed.get(0));
assertEquals("b", processed.get(1));
// stuck in effectively an infinite loop - it fails every time...
assertEquals("fail", processed.get(2));
assertEquals("fail", processed.get(3));
assertEquals("fail", processed.get(4));
}
@SuppressWarnings("unchecked")
@Test
public void testTransactionalHandlingWithRepeat() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,b,fail,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
boolean result = processed.add((String) payload);
if ("fail".equals(payload)) {
throw new RuntimeException("Planned failure: " + payload);
}
return result;
}
};
Source<Object> source = getPollableSource(list);
DirectChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
endpoint.setTaskAdviceChain(Arrays.asList(new Advice[] { getTransactionInterceptor(),
getRepeatOperationsInterceptor(3) }));
endpoint.initializeTask();
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 6, 100);
assertEquals(6, processed.size());
assertFalse("No messages got to processor", processed.isEmpty());
// Two TX rolled back so list is same size as when it started
assertEquals(beforeCount, list.size());
assertEquals("a", processed.get(0));
assertEquals("b", processed.get(1));
// stuck in effectively an infinite loop - it fails every time with the
// same 3 records...
assertEquals("fail", processed.get(2));
assertEquals("a", processed.get(3));
assertEquals("b", processed.get(4));
}
@SuppressWarnings("unchecked")
@Test
public void testTransactionalHandlingWithRetry() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,b,fail,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
if (message == null) {
return false;
}
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
boolean result = processed.add((String) payload);
// INT-184 this won't work if it is a "real" handler that throws
// MessageHandlingException
if ("fail".equals(payload)) {
throw new RuntimeException("Planned failure: " + payload);
}
return result;
}
};
Source<Object> source = getPollableSource(list);
MessageChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
endpoint.setTaskAdviceChain(Arrays.asList(new Advice[] { getTransactionInterceptor() }));
endpoint
.setDispatchAdviceChain(Arrays.asList(new Advice[] { getRetryOperationsInterceptor(itemKeyGenerator) }));
endpoint.initializeTask();
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 4, 20);
assertEquals(4, processed.size());
assertEquals(1, recovered.size());
assertFalse("No messages got to processor", processed.isEmpty());
// 4 items from list should have been processed (with no repeats, since
// the failed item was recovered with no retry - NeverRetryPolicy)
assertEquals(beforeCount - 4, list.size());
assertEquals("a", processed.get(0));
assertEquals("b", processed.get(1));
// retry makes it fail once then recover...
assertEquals("fail", processed.get(2));
assertEquals("d", processed.get(3));
}
@SuppressWarnings("unchecked")
@Test
public void testTransactionalHandlingWithRepeatAndRetry() throws Exception {
List<String> list = TransactionAwareProxyFactory.createTransactionalList();
list.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray("a,fail,c,d,e,f,g,h,j,k")));
int beforeCount = list.size();
Target handler = new Target() {
public boolean send(Message<?> message) {
Object payload = message.getPayload();
logger.debug("Handling: " + payload);
boolean result = processed.add((String) payload);
if ("fail".equals(payload)) {
throw new RuntimeException("Planned failure: " + payload);
}
return result;
}
};
Source<Object> source = getPollableSource(list);
MessageChannel channel = getChannel(handler, source);
SourceEndpoint endpoint = getSourceEndpoint(source, channel);
endpoint.setTaskAdviceChain(Arrays.asList(new Advice[] { getTransactionInterceptor(),
getRepeatOperationsInterceptor(3) }));
endpoint
.setDispatchAdviceChain(Arrays.asList(new Advice[] { getRetryOperationsInterceptor(itemKeyGenerator) }));
endpoint.initializeTask();
MessagingTaskScheduler scheduler = getSchedulerWithErrorHandler(endpoint);
waitForResults(scheduler, 6, 100);
System.err.println(processed);
System.err.println(list);
assertEquals(6, processed.size());
assertFalse("No messages got to processor", processed.isEmpty());
// One roll back and then start again with a,b,d,e
assertEquals(beforeCount - 5, list.size());
assertEquals("a", processed.get(0));
assertEquals("fail", processed.get(1));
// retry makes it fail once then recover...
assertEquals("a", processed.get(2));
assertEquals("c", processed.get(3));
assertEquals("d", processed.get(4));
}
/**
* @param source
* @param channel
* @return
*/
private SourceEndpoint getSourceEndpoint(Source<Object> source, MessageChannel channel) {
PollingSchedule schedule = new PollingSchedule(50);
schedule.setFixedRate(true); // used to be the default
return new SourceEndpoint(source, channel, schedule);
}
/**
* @param handler
* @param source
* @return
*/
private DirectChannel getChannel(Target handler, Source<Object> source) {
DirectChannel channel = new DirectChannel(source);
channel.setName("input");
channel.subscribe(handler);
return channel;
}
private void waitForResults(Lifecycle lifecycle, int count, int maxTries) throws InterruptedException {
lifecycle.start();
int timeout = 0;
while (processed.size() < count && timeout++ < maxTries) {
Thread.sleep(10);
}
lifecycle.stop();
}
private Source<Object> getPollableSource(List<String> list) {
final ItemReader reader = new ListItemReader(list) {
public Object read() {
Object item = super.read();
logger.debug("Reading: " + item);
return item;
}
};
Source<Object> source = new Source<Object>() {
public Message<Object> receive() {
try {
return new GenericMessage<Object>(reader.read());
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
};
return source;
}
// Workaround for INT-182
private MessagingTaskScheduler getSchedulerWithErrorHandler(Runnable task) {
SimpleMessagingTaskScheduler scheduler = new SimpleMessagingTaskScheduler(Executors
.newSingleThreadScheduledExecutor());
scheduler.setErrorHandler(new ErrorHandler() {
public void handle(Throwable t) {
logger.error("Exception in scheduler", t);
// throw (RuntimeException)t;
}
});
scheduler.schedule(task);
return scheduler;
}
/**
* @param itemKeyGenerator
* @return
*/
private StatefulRetryOperationsInterceptor getRetryOperationsInterceptor(ItemKeyGenerator itemKeyGenerator) {
StatefulRetryOperationsInterceptor advice = new StatefulRetryOperationsInterceptor();
advice.setRecoverer(new ItemRecoverer() {
@SuppressWarnings("unchecked")
public Object recover(Object data, Throwable cause) {
if (data == null) {
return false;
}
if (data.getClass().isArray()) {
data = ((Object[]) data)[0];
}
recovered.add(((Message<String>) data).getPayload());
return true;
}
});
advice.setKeyGenerator(itemKeyGenerator);
return advice;
}
/**
* @return
*/
private TransactionInterceptor getTransactionInterceptor() {
return new TransactionInterceptor(new ResourcelessTransactionManager(), PropertiesConverter
.stringToProperties("*=PROPAGATION_REQUIRED"));
}
/**
* @param commitInterval
* @return
*/
private RepeatOperationsInterceptor getRepeatOperationsInterceptor(int commitInterval) {
RepeatOperationsInterceptor advice = new RepeatOperationsInterceptor();
RepeatTemplate repeatTemplate = new RepeatTemplate();
repeatTemplate.setCompletionPolicy(new SimpleCompletionPolicy(commitInterval));
advice.setRepeatOperations(repeatTemplate);
return advice;
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<message-bus auto-create-channels="true" />
<annotation-driven />
<direct-channel id="smokein"/>
<channel id="smokeout"/>
</beans:beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<message-bus auto-create-channels="true" />
<channel id="jobs" />
</beans:beans>

View File

@@ -0,0 +1,10 @@
log4j.rootCategory=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %5p %t [%c] - <%m>%n
log4j.logger.org.springframework.integration.batch=DEBUG
log4j.logger.org.springframework.batch=DEBUG
log4j.category.org.springframework.integration=DEBUG
log4j.category.org.springframework.transaction=DEBUG

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<message-bus auto-create-channels="true" />
<annotation-driven />
<channel id="requests" />
<channel id="replies" />
<beans:bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<tx:annotation-driven />
<beans:bean id="chunkHandler" class="org.springframework.integration.batch.chunk.ItemWriterChunkHandler">
<beans:property name="itemWriter">
<beans:bean class="org.springframework.integration.batch.chunk.TestItemWriter"></beans:bean>
</beans:property>
</beans:bean>
<handler-endpoint input-channel="requests" output-channel="replies" handler="chunkHandler" method="handleChunk" />
</beans:beans>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<integration:message-bus auto-create-channels="true" />
<integration:annotation-driven/>
<integration:channel id="resources" />
<integration:channel id="requests" />
</beans>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<integration:message-bus auto-create-channels="true" />
<integration:channel id="requests" />
<bean id="itemWriter" class="org.springframework.integration.batch.item.MessageChannelItemWriter">
<property name="channel" ref="requests"/>
</bean>
</beans>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<import resource="classpath:/simple-job-launcher-context.xml" />
<integration:annotation-driven/>
<integration:message-bus auto-create-channels="true" />
<integration:channel id="requests" />
<integration:channel id="replies" />
<bean id="job" parent="simpleJob">
<property name="steps">
<bean
class="org.springframework.integration.batch.job.MessageOrientedStep">
<property name="name" value="TODO: I shouldn't have to set this"/>
<property name="requestChannel" ref="requests" />
<property name="replyChannel" ref="replies" />
<property name="jobRepository" ref="jobRepository" />
</bean>
</property>
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="step"
class="org.springframework.integration.batch.job.StepExecutionMessageHandler">
<property name="step">
<bean parent="taskletStep">
<property name="tasklet">
<bean
class="org.springframework.integration.batch.job.TestTasklet" />
</property>
</bean>
</property>
<property name="jobRepository" ref="jobRepository" />
</bean>
<integration:handler-endpoint handler="step" method="handle" input-channel="requests"
output-channel="replies" />
</beans>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<import resource="classpath:simple-job-launcher-context.xml"/>
<integration:message-bus auto-create-channels="true" />
<integration:annotation-driven />
<integration:channel id="requests" />
<integration:channel id="response" />
<integration:handler-endpoint input-channel="requests" handler="jobLaunchingHandler" method="handle"/>
<bean id="jobLaunchingHandler" class="org.springframework.integration.batch.launch.JobLaunchingMessageHandler">
<constructor-arg ref="jobLauncher"/>
<constructor-arg ref="messageToJobStrategy"/>
</bean>
<bean id="messageToJobStrategy" class="org.springframework.integration.batch.launch.StringPayloadAsJobNameStrategy">
<constructor-arg ref="jobRegistry" />
</bean>
<bean id="testJob" parent="simpleJob">
<property name="steps" ref="step"/>
</bean>
<bean id="step" class="org.springframework.batch.core.step.tasklet.TaskletStep">
<property name="tasklet">
<bean class="org.springframework.integration.batch.job.TestTasklet"/>
</property>
<property name="jobRepository" ref="jobRepository"/>
</bean>
</beans>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="jobRegistryBeanPostProcessor"
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.SimpleJobRepository">
<constructor-arg ref="mapJobInstanceDao" />
<constructor-arg ref="mapJobExecutionDao" />
<constructor-arg ref="mapStepExecutionDao" />
</bean>
<bean id="mapJobInstanceDao" lazy-init="true"
class="org.springframework.batch.core.repository.dao.MapJobInstanceDao" />
<bean id="mapJobExecutionDao" lazy-init="true"
class="org.springframework.batch.core.repository.dao.MapJobExecutionDao" />
<bean id="mapStepExecutionDao" lazy-init="true"
class="org.springframework.batch.core.repository.dao.MapStepExecutionDao" />
<bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob"
abstract="true">
<property name="jobRepository" ref="jobRepository" />
<property name="restartable" value="true" />
</bean>
<bean id="taskletStep" class="org.springframework.batch.core.step.tasklet.TaskletStep"
abstract="true">
<property name="jobRepository" ref="jobRepository" />
<property name="allowStartIfComplete" value="true" />
</bean>
<bean id="simpleStep"
class="org.springframework.batch.core.step.item.SimpleStepFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="jobRepository" ref="jobRepository" />
<property name="startLimit" value="100" />
<property name="commitInterval" value="1" />
</bean>
<bean id="skipLimitStep"
class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean"
parent="simpleStep" abstract="true">
<property name="skipLimit" value="0" />
</bean>
<bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="int[]">
<bean class="org.springframework.batch.support.IntArrayPropertyEditor" />
</entry>
<entry key="org.springframework.batch.item.file.transform.Range[]">
<bean
class="org.springframework.batch.item.file.transform.RangeArrayPropertyEditor" />
</entry>
<entry key="java.util.Date">
<bean class="org.springframework.beans.propertyeditors.CustomDateEditor">
<constructor-arg>
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyyyMMdd" />
</bean>
</constructor-arg>
<constructor-arg value="false" />
</bean>
</entry>
</map>
</property>
</bean>
</beans>