Add spring-batch- to module directory names (so folks can use mvn eclipse:eclipse if they want to).
BATCH-238: Remove hibernate support for the Daos.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.batch.config;
|
||||
|
||||
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
|
||||
|
||||
public class DatasourceTests extends AbstractTransactionalDataSourceSpringContextTests {
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
public void testTemplate() throws Exception {
|
||||
System.err.println(System.getProperty("java.class.path"));
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { Integer.valueOf(0),
|
||||
"foo" });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.batch.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
|
||||
|
||||
public class MessagingTests extends AbstractDependencyInjectionSpringContextTests {
|
||||
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
protected void onSetUp() throws Exception {
|
||||
super.onSetUp();
|
||||
Thread.sleep(100L);
|
||||
getMessages(); // drain queue
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
jmsTemplate.convertAndSend("queue", "bar");
|
||||
}
|
||||
|
||||
public void testMessaging() throws Exception {
|
||||
List list = getMessages();
|
||||
System.err.println(list);
|
||||
assertEquals(2, list.size());
|
||||
assertTrue(list.contains("foo"));
|
||||
}
|
||||
|
||||
private List getMessages() {
|
||||
String next = "";
|
||||
List msgs = new ArrayList();
|
||||
while (next != null) {
|
||||
next = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
if (next != null)
|
||||
msgs.add(next);
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.batch.container.jms;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageConsumer;
|
||||
import javax.jms.MessageListener;
|
||||
import javax.jms.Session;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.easymock.MockControl;
|
||||
import org.springframework.batch.container.jms.BatchMessageListenerContainer;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
public class BatchMessageListenerContainerTests extends TestCase {
|
||||
|
||||
BatchMessageListenerContainer container;
|
||||
|
||||
int count = 0;
|
||||
|
||||
public void testReceiveAndExecuteWithNoCallback() throws Exception {
|
||||
RepeatTemplate template = new RepeatTemplate() {
|
||||
public ExitStatus iterate(RepeatCallback callback) {
|
||||
count++;
|
||||
return ExitStatus.CONTINUABLE; // means we can continue to operate, but no message is received
|
||||
}
|
||||
};
|
||||
container = new BatchMessageListenerContainer(template);
|
||||
boolean received = doExecute(null, null);
|
||||
assertEquals(1, count);
|
||||
assertFalse("Message received", received);
|
||||
}
|
||||
|
||||
public void testReceiveAndExecuteWithCallback() throws Exception {
|
||||
RepeatTemplate template = new RepeatTemplate();
|
||||
template.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
container = new BatchMessageListenerContainer(template);
|
||||
|
||||
MockControl sessionControl = MockControl.createNiceControl(Session.class);
|
||||
MockControl consumerControl = MockControl.createControl(MessageConsumer.class);
|
||||
MockControl messageControl = MockControl.createControl(Message.class);
|
||||
|
||||
Session session = (Session) sessionControl.getMock();
|
||||
MessageConsumer consumer = (MessageConsumer) consumerControl.getMock();
|
||||
Message message = (Message) messageControl.getMock();
|
||||
|
||||
// Expect two calls to consumer (chunk size)...
|
||||
consumerControl.expectAndReturn(consumer.receive(1000), message);
|
||||
consumerControl.expectAndReturn(consumer.receive(1000), message);
|
||||
|
||||
sessionControl.replay();
|
||||
consumerControl.replay();
|
||||
messageControl.replay();
|
||||
|
||||
boolean received = doExecute(session, consumer);
|
||||
assertTrue("Message not received", received);
|
||||
|
||||
sessionControl.verify();
|
||||
consumerControl.verify();
|
||||
messageControl.verify();
|
||||
|
||||
}
|
||||
|
||||
public void testReceiveAndExecuteWithCallbackReturningNull() throws Exception {
|
||||
RepeatTemplate template = new RepeatTemplate();
|
||||
template.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
container = new BatchMessageListenerContainer(template);
|
||||
|
||||
MockControl sessionControl = MockControl.createNiceControl(Session.class);
|
||||
MockControl consumerControl = MockControl.createControl(MessageConsumer.class);
|
||||
|
||||
Session session = (Session) sessionControl.getMock();
|
||||
MessageConsumer consumer = (MessageConsumer) consumerControl.getMock();
|
||||
Message message = null;
|
||||
|
||||
// Expect one call to consumer (chunk size is 2 but terminates on
|
||||
// first)...
|
||||
consumerControl.expectAndReturn(consumer.receive(1000), message);
|
||||
|
||||
sessionControl.replay();
|
||||
consumerControl.replay();
|
||||
|
||||
boolean received = doExecute(session, consumer);
|
||||
assertFalse("Message not received", received);
|
||||
|
||||
sessionControl.verify();
|
||||
consumerControl.verify();
|
||||
|
||||
}
|
||||
|
||||
public void testTransactionalReceiveAndExecuteWithCallbackThrowingException() throws Exception {
|
||||
RepeatTemplate template = new RepeatTemplate();
|
||||
template.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
container = new BatchMessageListenerContainer(template);
|
||||
container.setSessionTransacted(true);
|
||||
boolean received = doTestWithException(new IllegalStateException("No way!"), true, 2);
|
||||
assertFalse("Message received", received);
|
||||
}
|
||||
|
||||
public void testNonTransactionalReceiveAndExecuteWithCallbackThrowingException() throws Exception {
|
||||
RepeatTemplate template = new RepeatTemplate();
|
||||
template.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
container = new BatchMessageListenerContainer(template);
|
||||
container.setSessionTransacted(false);
|
||||
boolean received = doTestWithException(new IllegalStateException("No way!"), false, 2);
|
||||
assertTrue("Message not received", received);
|
||||
}
|
||||
|
||||
public void testNonTransactionalReceiveAndExecuteWithCallbackThrowingError() throws Exception {
|
||||
RepeatTemplate template = new RepeatTemplate();
|
||||
template.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
container = new BatchMessageListenerContainer(template);
|
||||
container.setSessionTransacted(false);
|
||||
try {
|
||||
boolean received = doTestWithException(new RuntimeException("No way!"), false, 2);
|
||||
assertTrue("Message not received", received);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("No way!", e.getMessage());
|
||||
fail("Unexpected Error - should be swallowed");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doTestWithException(final Throwable t, boolean expectRollback, int expectGetTransactionCount)
|
||||
throws JMSException, IllegalAccessException {
|
||||
container.setAcceptMessagesWhileStopping(true);
|
||||
container.setMessageListener(new MessageListener() {
|
||||
public void onMessage(Message arg0) {
|
||||
if (t instanceof RuntimeException)
|
||||
throw (RuntimeException) t;
|
||||
else
|
||||
throw (Error) t;
|
||||
}
|
||||
});
|
||||
|
||||
MockControl sessionControl = MockControl.createNiceControl(Session.class);
|
||||
MockControl consumerControl = MockControl.createNiceControl(MessageConsumer.class);
|
||||
MockControl messageControl = MockControl.createNiceControl(Message.class);
|
||||
|
||||
Session session = (Session) sessionControl.getMock();
|
||||
MessageConsumer consumer = (MessageConsumer) consumerControl.getMock();
|
||||
Message message = (Message) messageControl.getMock();
|
||||
|
||||
sessionControl.expectAndReturn(session.getTransacted(), true, expectGetTransactionCount);
|
||||
|
||||
// Expect only one call to consumer (chunk size is 2, but first one
|
||||
// rolls back terminating batch)...
|
||||
consumerControl.expectAndReturn(consumer.receive(1000), message);
|
||||
if (expectRollback) {
|
||||
session.rollback();
|
||||
sessionControl.setVoidCallable();
|
||||
}
|
||||
|
||||
sessionControl.replay();
|
||||
consumerControl.replay();
|
||||
messageControl.replay();
|
||||
|
||||
boolean received = doExecute(session, consumer);
|
||||
|
||||
sessionControl.verify();
|
||||
consumerControl.verify();
|
||||
messageControl.verify();
|
||||
return received;
|
||||
}
|
||||
|
||||
private boolean doExecute(Session session, MessageConsumer consumer) throws IllegalAccessException {
|
||||
Method method = ReflectionUtils.findMethod(container.getClass(), "receiveAndExecute", new Class[] {
|
||||
Session.class, MessageConsumer.class });
|
||||
method.setAccessible(true);
|
||||
boolean received;
|
||||
try {
|
||||
received = ((Boolean) method.invoke(container, new Object[] { session, consumer })).booleanValue();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof RuntimeException) {
|
||||
throw (RuntimeException) e.getCause();
|
||||
} else {
|
||||
throw (Error) e.getCause();
|
||||
}
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.io.file.support.StaxEventReaderInputSource;
|
||||
import org.springframework.batch.io.file.support.oxm.UnmarshallingFragmentDeserializer;
|
||||
import org.springframework.batch.io.oxm.domain.Trade;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
|
||||
public abstract class AbstractStaxEventReaderInputSourceTests extends TestCase {
|
||||
|
||||
private StaxEventReaderInputSource source = new StaxEventReaderInputSource();
|
||||
|
||||
protected Resource resource = new ClassPathResource(
|
||||
"org/springframework/batch/io/oxm/input.xml");
|
||||
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
//TODO sensible resource allocation
|
||||
source.setResource(resource);
|
||||
|
||||
source.setFragmentRootElementName("trade");
|
||||
UnmarshallingFragmentDeserializer deserializer = new UnmarshallingFragmentDeserializer(getUnmarshaller());
|
||||
source.setFragmentDeserializer(deserializer);
|
||||
}
|
||||
|
||||
public void testRead() {
|
||||
Object result;
|
||||
List results = new ArrayList();
|
||||
while ((result = source.read()) != null) {
|
||||
results.add(result);
|
||||
}
|
||||
checkResults(results);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Unmarshaller specific to the OXM library used
|
||||
*/
|
||||
protected abstract Unmarshaller getUnmarshaller() throws Exception;
|
||||
|
||||
/**
|
||||
* @param results list of domain objects returned by input source
|
||||
*/
|
||||
protected void checkResults(List results){
|
||||
assertEquals(3, results.size());
|
||||
|
||||
Trade trade1 = (Trade) results.get(0);
|
||||
assertEquals("XYZ0001", trade1.getIsin());
|
||||
assertEquals(5, trade1.getQuantity());
|
||||
assertEquals(BigDecimal.valueOf(11.39), trade1.getPrice());
|
||||
assertEquals("Customer1", trade1.getCustomer());
|
||||
|
||||
Trade trade2 = (Trade) results.get(1);
|
||||
assertEquals("XYZ0002", trade2.getIsin());
|
||||
assertEquals(2, trade2.getQuantity());
|
||||
assertEquals(BigDecimal.valueOf(72.99), trade2.getPrice());
|
||||
assertEquals("Customer2", trade2.getCustomer());
|
||||
|
||||
Trade trade3 = (Trade) results.get(2);
|
||||
assertEquals("XYZ0003", trade3.getIsin());
|
||||
assertEquals(9, trade3.getQuantity());
|
||||
assertEquals(BigDecimal.valueOf(99.99), trade3.getPrice());
|
||||
assertEquals("Customer3", trade3.getCustomer());
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
source.close();
|
||||
}
|
||||
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.custommonkey.xmlunit.XMLAssert;
|
||||
import org.custommonkey.xmlunit.XMLUnit;
|
||||
import org.springframework.batch.io.file.support.StaxEventWriterItemWriter;
|
||||
import org.springframework.batch.io.file.support.oxm.MarshallingObjectToXmlSerializer;
|
||||
import org.springframework.batch.io.oxm.domain.Trade;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
|
||||
public abstract class AbstractStaxEventWriterItemWriterTests extends TestCase {
|
||||
|
||||
private StaxEventWriterItemWriter writer = new StaxEventWriterItemWriter();
|
||||
|
||||
private Resource resource;
|
||||
|
||||
File outputFile;
|
||||
|
||||
protected Resource expected = new ClassPathResource("expected-output.xml", getClass());
|
||||
|
||||
|
||||
protected List objects = new ArrayList() {{
|
||||
add(new Trade("isin1", 1, new BigDecimal(1.0), "customer1"));
|
||||
add(new Trade("isin2", 2, new BigDecimal(2.0), "customer2"));
|
||||
add(new Trade("isin3", 3, new BigDecimal(3.0), "customer3"));
|
||||
}};
|
||||
|
||||
/**
|
||||
* Write list of domain objects and check the output file.
|
||||
*/
|
||||
public void testWrite() throws Exception {
|
||||
for (Iterator iterator = objects.listIterator(); iterator.hasNext();) {
|
||||
writer.write(iterator.next());
|
||||
}
|
||||
writer.close();
|
||||
XMLUnit.setIgnoreWhitespace(true);
|
||||
XMLAssert.assertXMLEqual(new FileReader(expected.getFile()), new FileReader(resource.getFile()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
//File outputFile = File.createTempFile("AbstractStaxStreamWriterOutputSourceTests", "xml");
|
||||
outputFile = File.createTempFile(this.getClass().getSimpleName(), ".xml");
|
||||
resource = new FileSystemResource(outputFile);
|
||||
writer.setResource(resource);
|
||||
|
||||
MarshallingObjectToXmlSerializer mapper = new MarshallingObjectToXmlSerializer(getMarshaller());
|
||||
writer.setSerializer(mapper);
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
|
||||
outputFile.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Marshaller specific for the OXM technology being used.
|
||||
*/
|
||||
protected abstract Marshaller getMarshaller() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.castor.CastorMarshaller;
|
||||
|
||||
public class CastorMarshallingTests extends AbstractStaxEventWriterItemWriterTests {
|
||||
|
||||
protected Marshaller getMarshaller() throws Exception {
|
||||
|
||||
CastorMarshaller marshaller = new CastorMarshaller();
|
||||
// marshaller.setTargetClass(Trade.class);
|
||||
marshaller.setMappingLocation(new ClassPathResource("mapping-castor.xml", getClass()));
|
||||
// there is no way to call
|
||||
// org.exolab.castor.xml.Marshaller.setSupressXMLDeclaration();
|
||||
marshaller.afterPropertiesSet();
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
import org.springframework.oxm.castor.CastorMarshaller;
|
||||
|
||||
public class CastorUnmarshallingTests extends AbstractStaxEventReaderInputSourceTests {
|
||||
|
||||
protected Unmarshaller getUnmarshaller() throws Exception {
|
||||
CastorMarshaller unmarshaller = new CastorMarshaller();
|
||||
unmarshaller.setMappingLocation(new ClassPathResource("mapping-castor.xml", getClass()));
|
||||
// alternatively target class can be set
|
||||
//unmarshaller.setTargetClass(Trade.class);
|
||||
unmarshaller.afterPropertiesSet();
|
||||
return unmarshaller;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import org.springframework.batch.io.oxm.domain.Trade;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.xstream.XStreamMarshaller;
|
||||
|
||||
public class XStreamMarshallingTests extends
|
||||
AbstractStaxEventWriterItemWriterTests {
|
||||
|
||||
protected Marshaller getMarshaller() throws Exception {
|
||||
XStreamMarshaller marshaller = new XStreamMarshaller();
|
||||
marshaller.addAlias("trade", Trade.class);
|
||||
//in XStreamMarshaller.marshalSaxHandlers() method is used SaxWriter, which is configured
|
||||
//to include enclosing document (SaxWriter.includeEnclosingDocument is always set to TRUE)
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.batch.io.oxm.domain.Trade;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
import org.springframework.oxm.xstream.XStreamMarshaller;
|
||||
|
||||
public class XStreamUnmarshallingTests extends AbstractStaxEventReaderInputSourceTests {
|
||||
|
||||
protected Unmarshaller getUnmarshaller() throws Exception {
|
||||
XStreamMarshaller unmarshaller = new XStreamMarshaller();
|
||||
unmarshaller.addAlias("trade", Trade.class);
|
||||
unmarshaller.addAlias("isin", String.class);
|
||||
unmarshaller.addAlias("customer", String.class);
|
||||
unmarshaller.addAlias("price", BigDecimal.class);
|
||||
return unmarshaller;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.springframework.batch.io.oxm.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
*/
|
||||
public class Trade {
|
||||
private String isin = "";
|
||||
private long quantity = 0;
|
||||
private BigDecimal price = new BigDecimal(0);
|
||||
private String customer = "";
|
||||
|
||||
public Trade() {
|
||||
}
|
||||
|
||||
public Trade(String isin, long quantity, BigDecimal price, String customer){
|
||||
this.isin = isin;
|
||||
this.quantity = quantity;
|
||||
this.price = price;
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public void setCustomer(String customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public void setIsin(String isin) {
|
||||
this.isin = isin;
|
||||
}
|
||||
|
||||
public void setPrice(BigDecimal price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public void setQuantity(long quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public String getIsin() {
|
||||
return isin;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public long getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public String getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Trade: [isin=" + this.isin + ",quantity=" + this.quantity + ",price="
|
||||
+ this.price + ",customer=" + this.customer + "]";
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return EqualsBuilder.reflectionEquals(this, o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return HashCodeBuilder.reflectionHashCode(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.batch.jms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemProvider;
|
||||
import org.springframework.batch.item.provider.AbstractItemProvider;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
import org.springframework.batch.retry.callback.ItemProviderRetryCallback;
|
||||
import org.springframework.batch.retry.policy.ItemProviderRetryPolicy;
|
||||
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
public class ExternalRetryInBatchTests extends AbstractDependencyInjectionSpringContextTests {
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
private RetryTemplate retryTemplate;
|
||||
|
||||
private RepeatTemplate repeatTemplate;
|
||||
|
||||
private ItemProvider provider;
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setRepeatTemplate(RepeatTemplate repeatTemplate) {
|
||||
this.repeatTemplate = repeatTemplate;
|
||||
}
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
protected void onSetUp() throws Exception {
|
||||
super.onSetUp();
|
||||
getMessages(); // drain queue
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
jmsTemplate.convertAndSend("queue", "bar");
|
||||
provider = new AbstractItemProvider() {
|
||||
public Object next() {
|
||||
String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
public boolean recover(Object data, Throwable cause) {
|
||||
recovered.add(data);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
retryTemplate = new RetryTemplate();
|
||||
}
|
||||
|
||||
protected void onTearDown() throws Exception {
|
||||
getMessages(); // drain queue
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
}
|
||||
|
||||
private void assertInitialState() {
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
private List list = new ArrayList();
|
||||
|
||||
private List recovered = new ArrayList();
|
||||
|
||||
public void testExternalRetryRecoveryInBatch() throws Exception {
|
||||
assertInitialState();
|
||||
|
||||
retryTemplate.setRetryPolicy(new ItemProviderRetryPolicy(new SimpleRetryPolicy(1)));
|
||||
|
||||
final ItemProviderRetryCallback callback = new ItemProviderRetryCallback(provider, new ItemProcessor() {
|
||||
public void process(final Object text) {
|
||||
// No need for transaction here: the whole batch will roll
|
||||
// back. When it comes back for recovery this code is not
|
||||
// executed...
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
});
|
||||
|
||||
repeatTemplate.setCompletionPolicy(new SimpleCompletionPolicy(2));
|
||||
|
||||
// In a real container this could be an outer retry loop with an
|
||||
// *internal* retry policy.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
|
||||
repeatTemplate.iterate(new RepeatCallback() {
|
||||
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
return new ExitStatus(retryTemplate.execute(callback)!=null);
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
if (i == 0 || i == 2) {
|
||||
assertEquals("Rollback!", e.getMessage());
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
finally {
|
||||
System.err.println(i + ": " + recovered);
|
||||
}
|
||||
}
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
System.err.println(msgs);
|
||||
|
||||
assertEquals(2, recovered.size());
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
// Both messages were failed and recovered after last retry attempt:
|
||||
assertEquals("[]", msgs.toString());
|
||||
assertEquals("[foo, bar]", recovered.toString());
|
||||
|
||||
}
|
||||
|
||||
private List getMessages() {
|
||||
String next = "";
|
||||
List msgs = new ArrayList();
|
||||
while (next != null) {
|
||||
next = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
if (next != null)
|
||||
msgs.add(next);
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.batch.repeat.jms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.Session;
|
||||
import javax.jms.TextMessage;
|
||||
|
||||
import org.springframework.batch.container.jms.BatchMessageListenerContainer;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.jms.listener.SessionAwareMessageListener;
|
||||
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
|
||||
|
||||
public class AsynchronousTests extends AbstractDependencyInjectionSpringContextTests {
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
private BatchMessageListenerContainer container;
|
||||
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
public void setContainer(BatchMessageListenerContainer container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
protected void onSetUp() throws Exception {
|
||||
super.onSetUp();
|
||||
String foo = "";
|
||||
int count = 0;
|
||||
while (foo != null && count < 100) {
|
||||
foo = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
count++;
|
||||
}
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
|
||||
// Queue is now drained...
|
||||
assertNull(foo);
|
||||
|
||||
// Add a couple of messages...
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
jmsTemplate.convertAndSend("queue", "bar");
|
||||
}
|
||||
|
||||
protected void onTearDown() throws Exception {
|
||||
super.onTearDown();
|
||||
container.stop();
|
||||
// Need to give the container time to shutdown
|
||||
Thread.sleep(1000L);
|
||||
}
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
private void assertInitialState() {
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
public void testSunnyDay() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
container.setMessageListener(new SessionAwareMessageListener() {
|
||||
public void onMessage(Message message, Session session) throws JMSException {
|
||||
list.add(message.toString());
|
||||
String text = ((TextMessage) message).getText();
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
}
|
||||
});
|
||||
|
||||
container.start();
|
||||
|
||||
// Need to sleep for at least a second here...
|
||||
Thread.sleep(1000L);
|
||||
|
||||
System.err.println(jdbcTemplate.queryForList("select * from T_FOOS"));
|
||||
|
||||
assertEquals(2, list.size());
|
||||
|
||||
String foo = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
assertEquals(null, foo);
|
||||
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(2, count);
|
||||
|
||||
}
|
||||
|
||||
public void testRollback() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
container.setMessageListener(new SessionAwareMessageListener() {
|
||||
public void onMessage(Message message, Session session) throws JMSException {
|
||||
list.add(message.toString());
|
||||
final String text = ((TextMessage) message).getText();
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
new Integer(list.size()), text });
|
||||
// This causes the DB to rollback but not the message
|
||||
if (text.equals("bar")) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
container.start();
|
||||
|
||||
// Need to sleep for at least a second here...
|
||||
Thread.sleep(3000L);
|
||||
|
||||
// We rolled back so the messages might come in many times...
|
||||
assertTrue(list.size() >= 1);
|
||||
|
||||
System.err.println(jdbcTemplate.queryForList("select * from T_FOOS"));
|
||||
|
||||
String text = "";
|
||||
List msgs = new ArrayList();
|
||||
while (text != null) {
|
||||
text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
msgs.add(text);
|
||||
}
|
||||
System.err.println(msgs);
|
||||
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
assertTrue("Foo not on queue", msgs.contains("foo"));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.batch.repeat.jms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.jms.ConnectionFactory;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
import org.springframework.jms.connection.SessionProxy;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.jms.core.SessionCallback;
|
||||
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
public class SynchronousTests extends AbstractTransactionalDataSourceSpringContextTests {
|
||||
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
private RepeatTemplate repeatTemplate;
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
public void setRepeatTemplate(RepeatTemplate repeatTemplate) {
|
||||
this.repeatTemplate = repeatTemplate;
|
||||
}
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
protected void onSetUpBeforeTransaction() throws Exception {
|
||||
super.onSetUpBeforeTransaction();
|
||||
String foo = "";
|
||||
int count = 0;
|
||||
while (foo != null && count < 100) {
|
||||
foo = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
count++;
|
||||
}
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
jmsTemplate.convertAndSend("queue", "bar");
|
||||
}
|
||||
|
||||
protected void onSetUpInTransaction() throws Exception {
|
||||
super.onSetUpInTransaction();
|
||||
}
|
||||
|
||||
private void assertInitialState() {
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
public void testCommit() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
repeatTemplate.iterate(new RepeatCallback() {
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
return new ExitStatus(text != null);
|
||||
}
|
||||
});
|
||||
|
||||
// force commit...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
startNewTransaction();
|
||||
|
||||
System.err.println(jdbcTemplate.queryForList("select * from T_FOOS"));
|
||||
|
||||
// Database committed so this resord should be there...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(2, count);
|
||||
|
||||
// ... the commit should also have cleared the queue, so this should now
|
||||
// be null
|
||||
String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
assertEquals(null, text);
|
||||
|
||||
}
|
||||
|
||||
public void testFullRollback() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
repeatTemplate.iterate(new RepeatCallback() {
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
return new ExitStatus(text != null);
|
||||
}
|
||||
});
|
||||
|
||||
// force rollback...
|
||||
endTransaction();
|
||||
startNewTransaction();
|
||||
|
||||
String text = "";
|
||||
List msgs = new ArrayList();
|
||||
while (text != null) {
|
||||
text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
msgs.add(text);
|
||||
}
|
||||
|
||||
// The database portion rolled back...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session. The rollback should have restored
|
||||
// the queue, so this should now be non-null
|
||||
assertTrue("Foo not on queue", msgs.contains("foo"));
|
||||
}
|
||||
|
||||
public void testPartialRollback() throws Exception {
|
||||
|
||||
// The JmsTemplate is used elsewhere outside a transaction, so
|
||||
// we need to use one here that is transaction aware.
|
||||
final JmsTemplate jmsTemplate = new JmsTemplate((ConnectionFactory) applicationContext
|
||||
.getBean("txAwareConnectionFactory"));
|
||||
jmsTemplate.setReceiveTimeout(100L);
|
||||
jmsTemplate.setSessionTransacted(true);
|
||||
|
||||
assertInitialState();
|
||||
repeatTemplate.iterate(new RepeatCallback() {
|
||||
public ExitStatus doInIteration(RepeatContext context) throws Exception {
|
||||
String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
return new ExitStatus(text != null);
|
||||
}
|
||||
});
|
||||
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
public void beforeCommit(boolean readOnly) {
|
||||
// Simulate a message system failure before the main transaction
|
||||
// commits...
|
||||
jmsTemplate.execute(new SessionCallback() {
|
||||
public Object doInJms(Session session) throws JMSException {
|
||||
try {
|
||||
assertTrue("Not a SessionProxy - wrong spring version?", session instanceof SessionProxy);
|
||||
((SessionProxy) session).getTargetSession().rollback();
|
||||
}
|
||||
catch (JMSException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// swallow it
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// force commit...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
startNewTransaction();
|
||||
|
||||
String text = "";
|
||||
List msgs = new ArrayList();
|
||||
while (text != null) {
|
||||
text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
msgs.add(text);
|
||||
}
|
||||
|
||||
// The database portion committed...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(2, count);
|
||||
|
||||
// ...but the JMS session rolled back, so the message is still there
|
||||
assertTrue("Foo not on queue", msgs.contains("foo"));
|
||||
assertTrue("Bar not on queue", msgs.contains("bar"));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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.batch.retry.jms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemProvider;
|
||||
import org.springframework.batch.item.provider.AbstractItemProvider;
|
||||
import org.springframework.batch.retry.callback.ItemProviderRetryCallback;
|
||||
import org.springframework.batch.retry.policy.ItemProviderRetryPolicy;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
public class ExternalRetryTests extends AbstractDependencyInjectionSpringContextTests {
|
||||
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
private RetryTemplate retryTemplate;
|
||||
|
||||
private ItemProvider provider;
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
protected void onSetUp() throws Exception {
|
||||
super.onSetUp();
|
||||
getMessages(); // drain queue
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
provider = new AbstractItemProvider() {
|
||||
public Object next() {
|
||||
String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
public boolean recover(Object data, Throwable cause) {
|
||||
recovered.add(data);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
retryTemplate = new RetryTemplate();
|
||||
}
|
||||
|
||||
private void assertInitialState() {
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
private List list = new ArrayList();
|
||||
|
||||
private List recovered = new ArrayList();
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt but must receive
|
||||
* the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testExternalRetrySuccessOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
retryTemplate.setRetryPolicy(new ItemProviderRetryPolicy());
|
||||
|
||||
final ItemProviderRetryCallback callback = new ItemProviderRetryCallback(provider, new ItemProcessor() {
|
||||
public void process(final Object text) {
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
return retryTemplate.execute(callback);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
assertEquals("Rollback!", e.getMessage());
|
||||
|
||||
// Client of retry template has to take care of rollback. This would
|
||||
// be a message listener container in the MDP case.
|
||||
|
||||
}
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
return retryTemplate.execute(callback);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing fails on both attempts.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testExternalRetryWithRecovery() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
retryTemplate.setRetryPolicy(new ItemProviderRetryPolicy());
|
||||
|
||||
final ItemProviderRetryCallback callback = new ItemProviderRetryCallback(provider, new ItemProcessor() {
|
||||
public void process(final Object text) {
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
});
|
||||
|
||||
Object result = "start";
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
result = new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
return retryTemplate.execute(callback);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
if (i < 3)
|
||||
assertEquals("Rollback!", e.getMessage());
|
||||
|
||||
// Client of retry template has to take care of rollback. This
|
||||
// would
|
||||
// be a message listener container in the MDP case.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Last attempt should return last item.
|
||||
assertEquals("foo", result);
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
assertEquals(1, recovered.size());
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
|
||||
}
|
||||
|
||||
private List getMessages() {
|
||||
String next = "";
|
||||
List msgs = new ArrayList();
|
||||
while (next != null) {
|
||||
next = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
if (next != null)
|
||||
msgs.add(next);
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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.batch.retry.jms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.provider.JmsItemProvider;
|
||||
import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.callback.ItemProviderRetryCallback;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
public class SynchronousTests extends AbstractTransactionalDataSourceSpringContextTests {
|
||||
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
private RetryTemplate retryTemplate;
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
protected void onSetUpBeforeTransaction() throws Exception {
|
||||
super.onSetUpBeforeTransaction();
|
||||
String foo = "";
|
||||
int count = 0;
|
||||
while (foo != null && count < 100) {
|
||||
foo = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
count++;
|
||||
}
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
assertNotNull(text);
|
||||
retryTemplate = new RetryTemplate();
|
||||
}
|
||||
|
||||
protected void onSetUpInTransaction() throws Exception {
|
||||
super.onSetUpInTransaction();
|
||||
}
|
||||
|
||||
private void assertInitialState() {
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt without having to
|
||||
* receive the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInternalRetrySuccessOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
/*
|
||||
* We either want the JMS receive to be outside a transaction, or we
|
||||
* need the database transaction in the retry to be PROPAGATION_NESTED.
|
||||
* Otherwise JMS will roll back when the retry callback is eventually
|
||||
* successful because of the previous exception.
|
||||
* PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer
|
||||
* transaction to fail and rollback the inner one.
|
||||
*/
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
assertNotNull(text);
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_NESTED);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
list.add(text);
|
||||
System.err.println("Inserting: [" + list.size() + "," + text + "]");
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// force commit...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt without having to
|
||||
* receive the message again - uses JmsItemProvider internally.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInternalRetrySuccessOnSecondAttemptWithItemProvider() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
JmsItemProvider provider = new JmsItemProvider();
|
||||
// provider.setItemType(Message.class);
|
||||
provider.setJmsTemplate(jmsTemplate);
|
||||
jmsTemplate.setDefaultDestinationName("queue");
|
||||
|
||||
retryTemplate.execute(new ItemProviderRetryCallback(provider, new ItemProcessor() {
|
||||
public void process(final Object text) {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_NESTED);
|
||||
transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
list.add(text);
|
||||
System.err.println("Inserting: [" + list.size() + "," + text + "]");
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
// force commit...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt without having to
|
||||
* receive the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInternalRetrySuccessOnFirstAttemptRollbackOuter() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
/*
|
||||
* We either want the JMS receive to be outside a transaction, or we
|
||||
* need the database transaction in the retry to be PROPAGATION_NESTED.
|
||||
* Otherwise JMS will roll back when the retry callback is eventually
|
||||
* successful because of the previous exception.
|
||||
* PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer
|
||||
* transaction to fail and rollback the inner one.
|
||||
*/
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_NESTED);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
list.add(text);
|
||||
System.err.println("Inserting: [" + list.size() + "," + text + "]");
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// The database transaction has committed...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// force rollback...
|
||||
endTransaction();
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion rolled back...
|
||||
count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[foo]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt but must receive
|
||||
* the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testExternalRetrySuccessOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
// force commit so that the retry executes in its own transaction (not
|
||||
// nested)...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
// The receive is inside the retry and the
|
||||
// transaction...
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing fails.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testExternalRetryFailOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
// force commit so that the retry executes in its own transaction (not
|
||||
// nested)...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
try {
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
// The receieve is inside the retry and the
|
||||
// transaction...
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)",
|
||||
new Object[] { Integer.valueOf(list.size()), text });
|
||||
throw new RuntimeException("Rollback!");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* N.B. the message can be re-directed to an error queue by setting
|
||||
* an error destination in a JmsItemProvider.
|
||||
*/
|
||||
fail("Expected RuntimeException");
|
||||
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("Rollback!", e.getMessage());
|
||||
// expected
|
||||
}
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion rolled back...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertTrue(msgs.contains("foo"));
|
||||
}
|
||||
|
||||
private List getMessages() {
|
||||
String next = "";
|
||||
List msgs = new ArrayList();
|
||||
while (next != null) {
|
||||
next = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
if (next != null)
|
||||
msgs.add(next);
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* 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.retry.jms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.provider.JmsItemProvider;
|
||||
import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.callback.ItemProviderRetryCallback;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
public class SynchronousTests extends AbstractTransactionalDataSourceSpringContextTests {
|
||||
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
private RetryTemplate retryTemplate;
|
||||
|
||||
public void setJmsTemplate(JmsTemplate jmsTemplate) {
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
}
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] { "/org/springframework/batch/jms/jms-context.xml" };
|
||||
}
|
||||
|
||||
protected void onSetUpBeforeTransaction() throws Exception {
|
||||
super.onSetUpBeforeTransaction();
|
||||
String foo = "";
|
||||
int count = 0;
|
||||
while (foo != null && count < 100) {
|
||||
foo = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
count++;
|
||||
}
|
||||
jdbcTemplate.execute("delete from T_FOOS");
|
||||
jmsTemplate.convertAndSend("queue", "foo");
|
||||
retryTemplate = new RetryTemplate();
|
||||
}
|
||||
|
||||
protected void onSetUpInTransaction() throws Exception {
|
||||
super.onSetUpInTransaction();
|
||||
}
|
||||
|
||||
private void assertInitialState() {
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt without having to
|
||||
* receive the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInternalRetrySuccessOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
/*
|
||||
* We either want the JMS receive to be outside a transaction, or we
|
||||
* need the database transaction in the retry to be PROPAGATION_NESTED.
|
||||
* Otherwise JMS will roll back when the retry callback is eventually
|
||||
* successful because of the previous exception.
|
||||
* PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer
|
||||
* transaction to fail and rollback the inner one.
|
||||
*/
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_NESTED);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
list.add(text);
|
||||
System.err.println("Inserting: [" + list.size() + "," + text + "]");
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// force commit...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt without having to
|
||||
* receive the message again - uses JmsItemProvider internally.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInternalRetrySuccessOnSecondAttemptWithItemProvider() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
JmsItemProvider provider = new JmsItemProvider();
|
||||
// provider.setItemType(Message.class);
|
||||
provider.setJmsTemplate(jmsTemplate);
|
||||
jmsTemplate.setDefaultDestinationName("queue");
|
||||
|
||||
retryTemplate.execute(new ItemProviderRetryCallback(provider, new ItemProcessor() {
|
||||
public void process(final Object text) {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_NESTED);
|
||||
transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
list.add(text);
|
||||
System.err.println("Inserting: [" + list.size() + "," + text + "]");
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
// force commit...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt without having to
|
||||
* receive the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testInternalRetrySuccessOnFirstAttemptRollbackOuter() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
/*
|
||||
* We either want the JMS receive to be outside a transaction, or we
|
||||
* need the database transaction in the retry to be PROPAGATION_NESTED.
|
||||
* Otherwise JMS will roll back when the retry callback is eventually
|
||||
* successful because of the previous exception.
|
||||
* PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer
|
||||
* transaction to fail and rollback the inner one.
|
||||
*/
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_NESTED);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
list.add(text);
|
||||
System.err.println("Inserting: [" + list.size() + "," + text + "]");
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// The database transaction has committed...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// force rollback...
|
||||
endTransaction();
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion rolled back...
|
||||
count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[foo]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing is successful on the second attempt but must receive
|
||||
* the message again.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testExternalRetrySuccessOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
// force commit so that the retry executes in its own transaction (not
|
||||
// nested)...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
// The receieve is inside the retry and the
|
||||
// transaction...
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] {
|
||||
Integer.valueOf(list.size()), text });
|
||||
if (list.size() == 1) {
|
||||
throw new RuntimeException("Rollback!");
|
||||
}
|
||||
return text;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion committed once...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(1, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertEquals("[]", msgs.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message processing fails.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testExternalRetryFailOnSecondAttempt() throws Exception {
|
||||
|
||||
assertInitialState();
|
||||
|
||||
// force commit so that the retry executes in its own transaction (not
|
||||
// nested)...
|
||||
setComplete();
|
||||
endTransaction();
|
||||
|
||||
try {
|
||||
|
||||
retryTemplate.execute(new RetryCallback() {
|
||||
public Object doWithRetry(RetryContext status) throws Throwable {
|
||||
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
return transactionTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
|
||||
// The receieve is inside the retry and the
|
||||
// transaction...
|
||||
final String text = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
list.add(text);
|
||||
jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)",
|
||||
new Object[] { Integer.valueOf(list.size()), text });
|
||||
throw new RuntimeException("Rollback!");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
fail("Expected RuntimeException");
|
||||
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("Rollback!", e.getMessage());
|
||||
// expected
|
||||
}
|
||||
|
||||
startNewTransaction();
|
||||
|
||||
List msgs = getMessages();
|
||||
|
||||
// The database portion rolled back...
|
||||
int count = jdbcTemplate.queryForInt("select count(*) from T_FOOS");
|
||||
assertEquals(0, count);
|
||||
|
||||
// ... and so did the message session.
|
||||
assertTrue(msgs.contains("foo"));
|
||||
}
|
||||
|
||||
private List getMessages() {
|
||||
String next = "";
|
||||
List msgs = new ArrayList();
|
||||
while (next != null) {
|
||||
next = (String) jmsTemplate.receiveAndConvert("queue");
|
||||
if (next != null)
|
||||
msgs.add(next);
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package test.jdbc.datasource;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.derby.jdbc.EmbeddedDataSource;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
|
||||
public class DerbyDataSourceFactoryBean extends AbstractFactoryBean {
|
||||
|
||||
private String dataDirectory = "derby-home";
|
||||
|
||||
DataSource dataSource;
|
||||
|
||||
public void setDataDirectory(String dataDirectory) {
|
||||
this.dataDirectory = dataDirectory;
|
||||
}
|
||||
|
||||
protected Object createInstance() throws Exception {
|
||||
File directory = new File(dataDirectory);
|
||||
System.setProperty("derby.system.home", directory.getCanonicalPath());
|
||||
System.setProperty("derby.storage.fileSyncTransactionLog", "true");
|
||||
System.setProperty("derby.storage.pageCacheSize", "100");
|
||||
|
||||
final EmbeddedDataSource ds = new EmbeddedDataSource();
|
||||
ds.setDatabaseName("derbydb");
|
||||
ds.setCreateDatabase("create");
|
||||
dataSource = ds;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return DataSource.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 test.jdbc.datasource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
public class InitializingDataSourceFactoryBean extends AbstractFactoryBean {
|
||||
|
||||
private Resource initScript;
|
||||
|
||||
private Resource destroyScript;
|
||||
|
||||
DataSource dataSource;
|
||||
|
||||
public void destroy() throws Exception {
|
||||
super.destroy();
|
||||
|
||||
try {
|
||||
doExecuteScript(destroyScript);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("Could not execute destroy script [" + destroyScript + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(dataSource);
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
protected Object createInstance() throws Exception {
|
||||
Assert.notNull(dataSource);
|
||||
try {
|
||||
doExecuteScript(destroyScript);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.debug("Could not execute destroy script [" + destroyScript + "]", e);
|
||||
}
|
||||
doExecuteScript(initScript);
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private void doExecuteScript(final Resource scriptResource) {
|
||||
if (scriptResource == null || !scriptResource.exists())
|
||||
return;
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(dataSource));
|
||||
if (initScript != null) {
|
||||
transactionTemplate.execute(new TransactionCallback() {
|
||||
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
String[] scripts;
|
||||
try {
|
||||
scripts = StringUtils.delimitedListToStringArray(stripComments(IOUtils.readLines(scriptResource
|
||||
.getInputStream())), ";");
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new BeanInitializationException("Cannot load script from [" + initScript + "]", e);
|
||||
}
|
||||
for (int i = 0; i < scripts.length; i++) {
|
||||
String script = scripts[i].trim();
|
||||
if (StringUtils.hasText(script)) {
|
||||
jdbcTemplate.execute(scripts[i]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private String stripComments(List list) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (Iterator iter = list.iterator(); iter.hasNext();) {
|
||||
String line = (String) iter.next();
|
||||
if (!line.startsWith("//") && !line.startsWith("--")) {
|
||||
buffer.append(line + "\n");
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return DataSource.class;
|
||||
}
|
||||
|
||||
public void setInitScript(Resource initScript) {
|
||||
this.initScript = initScript;
|
||||
}
|
||||
|
||||
public void setDestroyScript(Resource destroyScript) {
|
||||
this.destroyScript = destroyScript;
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
40
spring-batch-integration/src/test/resources/data-source.xml
Normal file
40
spring-batch-integration/src/test/resources/data-source.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" 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.0.xsd">
|
||||
|
||||
<bean id="dataSource" class="test.jdbc.datasource.InitializingDataSourceFactoryBean">
|
||||
<property name="dataSource" ref="hsql" />
|
||||
<property name="initScript" value="org/springframework/batch/jms/init.sql" />
|
||||
<property name="destroyScript" value="org/springframework/batch/jms/destroy.sql" />
|
||||
</bean>
|
||||
|
||||
<bean id="hsql" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true"
|
||||
autowire-candidate="false">
|
||||
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
|
||||
<property name="url" value="jdbc:hsqldb:mem:testdb" />
|
||||
</bean>
|
||||
|
||||
<bean id="derby" class="test.jdbc.datasource.DerbyDataSourceFactoryBean" lazy-init="true" destroy-method="destroy"
|
||||
autowire-candidate="false">
|
||||
<property name="dataDirectory" value="derby-home" />
|
||||
</bean>
|
||||
|
||||
<bean id="brokerService" class="org.apache.activemq.broker.BrokerService" init-method="start"
|
||||
destroy-method="stop">
|
||||
<property name="brokerName" value="broker" />
|
||||
<property name="useJmx" value="false"/>
|
||||
<property name="transportConnectorURIs">
|
||||
<list>
|
||||
<value>vm://localhost</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="persistenceAdapter">
|
||||
<bean class="org.apache.activemq.store.memory.MemoryPersistenceAdapter"/>
|
||||
<!-- bean class="org.apache.activemq.store.jdbc.JDBCPersistenceAdapter">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="createTablesOnStartup" value="true" />
|
||||
</bean-->
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,8 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.category.org.apache.activemq=ERROR
|
||||
# log4j.category.org.springframework=DEBUG
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<trade>
|
||||
<isin>isin1</isin>
|
||||
<quantity>1</quantity>
|
||||
<price>1</price>
|
||||
<customer>customer1</customer>
|
||||
</trade>
|
||||
|
||||
<trade>
|
||||
<isin>isin2</isin>
|
||||
<quantity>2</quantity>
|
||||
<price>2</price>
|
||||
<customer>customer2</customer>
|
||||
</trade>
|
||||
|
||||
<trade>
|
||||
<isin>isin3</isin>
|
||||
<quantity>3</quantity>
|
||||
<price>3</price>
|
||||
<customer>customer3</customer>
|
||||
</trade>
|
||||
|
||||
</root>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<records>
|
||||
<trade>
|
||||
<isin>XYZ0001</isin>
|
||||
<quantity>5</quantity>
|
||||
<price>11.39</price>
|
||||
<customer>Customer1</customer>
|
||||
</trade>
|
||||
<trade>
|
||||
<isin>XYZ0002</isin>
|
||||
<quantity>2</quantity>
|
||||
<price>72.99</price>
|
||||
<customer>Customer2</customer>
|
||||
</trade>
|
||||
<trade>
|
||||
<isin>XYZ0003</isin>
|
||||
<quantity>9</quantity>
|
||||
<price>99.99</price>
|
||||
<customer>Customer3</customer>
|
||||
</trade>
|
||||
</records>
|
||||
@@ -0,0 +1,23 @@
|
||||
<mapping>
|
||||
<class name="org.springframework.batch.io.oxm.domain.Trade">
|
||||
|
||||
<map-to xml="trade" />
|
||||
|
||||
<field name="isin">
|
||||
<bind-xml name="isin" node="element" />
|
||||
</field>
|
||||
|
||||
<field name="quantity">
|
||||
<bind-xml name="quantity" node="element" />
|
||||
</field>
|
||||
|
||||
<field name="price">
|
||||
<bind-xml name="price" node="element" />
|
||||
</field>
|
||||
|
||||
<field name="customer">
|
||||
<bind-xml name="customer" node="element" />
|
||||
</field>
|
||||
|
||||
</class>
|
||||
</mapping>
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE T_FOOS;
|
||||
@@ -0,0 +1,5 @@
|
||||
create table T_FOOS (
|
||||
id integer not null primary key,
|
||||
name varchar(80),
|
||||
foo_date timestamp
|
||||
);
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
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.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
|
||||
|
||||
<import resource="classpath:/data-source.xml"/>
|
||||
|
||||
<!-- Transaction manager for a datasource -->
|
||||
<bean id="transactionManager"
|
||||
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
</bean>
|
||||
|
||||
<!-- Transaction manager for jms -->
|
||||
<bean id="jmsTransactionManager" autowire-candidate="false"
|
||||
class="org.springframework.jms.connection.JmsTransactionManager">
|
||||
<property name="connectionFactory" ref="connectionFactory" />
|
||||
</bean>
|
||||
|
||||
<bean id="jmsTemplate"
|
||||
class="org.springframework.jms.core.JmsTemplate">
|
||||
<property name="connectionFactory" ref="connectionFactory" />
|
||||
<property name="receiveTimeout" value="100" />
|
||||
<!-- This is important... -->
|
||||
<property name="sessionTransacted" value="true" />
|
||||
</bean>
|
||||
|
||||
<bean id="jdbcTemplate"
|
||||
class="org.springframework.jdbc.core.JdbcTemplate">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="connectionFactory"
|
||||
class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="brokerService">
|
||||
<property name="brokerURL">
|
||||
<value>vm://localhost</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="txAwareConnectionFactory"
|
||||
class="org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy">
|
||||
<property name="targetConnectionFactory" ref="connectionFactory"/>
|
||||
<property name="synchedLocalTransactionAllowed" value="true" />
|
||||
</bean>
|
||||
|
||||
<bean id="container"
|
||||
class="org.springframework.batch.container.jms.BatchMessageListenerContainer">
|
||||
<property name="transactionManager" ref="transactionManager" />
|
||||
<property name="connectionFactory"
|
||||
ref="txAwareConnectionFactory" />
|
||||
<property name="destinationName" value="queue" />
|
||||
<!-- This is important... it forces the container to acknowledge message receipt,
|
||||
and avoid duplicate messages in the sunny day case -->
|
||||
<property name="sessionTransacted" value="true" />
|
||||
<constructor-arg ref="transactionalBatchTemplate" />
|
||||
</bean>
|
||||
|
||||
<bean id="batchTemplate"
|
||||
class="org.springframework.batch.repeat.support.RepeatTemplate">
|
||||
<property name="completionPolicy">
|
||||
<bean
|
||||
class="org.springframework.batch.repeat.policy.SimpleCompletionPolicy">
|
||||
<constructor-arg value="2" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionalBatchTemplate"
|
||||
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
|
||||
<property name="transactionManager" ref="transactionManager" />
|
||||
<property name="proxyInterfaces">
|
||||
<value>
|
||||
org.springframework.batch.repeat.RepeatOperations
|
||||
</value>
|
||||
</property>
|
||||
<property name="proxyTargetClass" value="false" />
|
||||
<property name="transactionAttributes"
|
||||
value="*=PROPAGATION_REQUIRED">
|
||||
</property>
|
||||
<property name="target">
|
||||
<bean
|
||||
class="org.springframework.batch.repeat.support.RepeatTemplate">
|
||||
<property name="completionPolicy">
|
||||
<bean
|
||||
class="org.springframework.batch.repeat.policy.SimpleCompletionPolicy">
|
||||
<constructor-arg value="2" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user