BATCH-189: Created AbstractTransactionalIoSource that can be the base class of both input and output sources.

Additionally, reorganized input and output source packages.
This commit is contained in:
lucasward
2007-11-05 04:12:42 +00:00
parent 7f09f4214c
commit 9dc7981c5d
44 changed files with 453 additions and 226 deletions

View File

@@ -31,8 +31,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.io.support.AbstractTransactionalIoSource;
import org.springframework.batch.item.ResourceLifecycle;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
import org.springframework.batch.restart.GenericRestartData;
import org.springframework.batch.restart.RestartData;
import org.springframework.batch.restart.Restartable;
@@ -47,8 +47,6 @@ import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.util.Assert;
/**
@@ -117,7 +115,7 @@ import org.springframework.util.Assert;
* @author Lucas Ward
* @author Peter Zozom
*/
public class JdbcCursorInputSource implements InputSource, ResourceLifecycle, DisposableBean,
public class JdbcCursorInputSource extends AbstractTransactionalIoSource implements InputSource, ResourceLifecycle, DisposableBean,
InitializingBean, Restartable, StatisticsProvider, Skippable {
private static Log log = LogFactory.getLog(JdbcCursorInputSource.class);
@@ -159,8 +157,6 @@ public class JdbcCursorInputSource implements InputSource, ResourceLifecycle, Di
private int lastCommittedRow = 0;
private final SqlInputTransactionSynchronization transactionSynchronization = new SqlInputTransactionSynchronization();
private RowMapper mapper;
private boolean initialized = false;
@@ -242,7 +238,7 @@ public class JdbcCursorInputSource implements InputSource, ResourceLifecycle, Di
* Mark the current row. Calling reset will cause the result set to be set
* to the current row when mark was called.
*/
private void mark() {
protected void transactionCommitted() {
lastCommittedRow = currentProcessedRow;
skippedRows.clear();
}
@@ -252,7 +248,7 @@ public class JdbcCursorInputSource implements InputSource, ResourceLifecycle, Di
*
* @throws DataAccessException
*/
private void reset() {
protected void transactionRolledBack() {
try {
currentProcessedRow = lastCommittedRow;
if (currentProcessedRow > 0) {
@@ -334,8 +330,7 @@ public class JdbcCursorInputSource implements InputSource, ResourceLifecycle, Di
sql, se);
}
BatchTransactionSynchronizationManager
.registerSynchronization(transactionSynchronization);
super.registerSynchronization();
}
/*
@@ -554,22 +549,4 @@ public class JdbcCursorInputSource implements InputSource, ResourceLifecycle, Di
initialized = true;
}
private class SqlInputTransactionSynchronization extends TransactionSynchronizationAdapter {
/*
* @param status transaction status
*
* @see org.springframework.transaction.support.TransactionSynchronization#afterCompletion(int)
*/
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
reset();
} else if (status == TransactionSynchronization.STATUS_COMMITTED) {
mark();
}
}
}
}

View File

@@ -0,0 +1,8 @@
<html>
<body>
<p>
Infrastructure implementations of cursor based input sources. All input sources within this package
open a cursor against the database, and return back a mapped object for each row.
</p>
</body>
</html>

View File

@@ -19,6 +19,7 @@ import java.util.Iterator;
import java.util.List;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.support.AbstractTransactionalIoSource;
import org.springframework.batch.item.ResourceLifecycle;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
import org.springframework.batch.restart.RestartData;
@@ -44,10 +45,10 @@ import org.springframework.util.Assert;
*
*
* @author Lucas Ward
*
* @since 1.0
*/
public class DrivingQueryInputSource implements InputSource, ResourceLifecycle, InitializingBean,
DisposableBean, Restartable {
public class DrivingQueryInputSource extends AbstractTransactionalIoSource implements InputSource,
ResourceLifecycle, InitializingBean, DisposableBean, Restartable {
private boolean initialized = false;
@@ -61,9 +62,6 @@ public class DrivingQueryInputSource implements InputSource, ResourceLifecycle,
private KeyGenerator keyGenerator;
private TransactionSynchronization synchronization =
new DrivingQueryInputSourceTransactionSynchronization();
public DrivingQueryInputSource() {
}
@@ -140,7 +138,7 @@ public class DrivingQueryInputSource implements InputSource, ResourceLifecycle,
", call close() first.");
keys = keyGenerator.retrieveKeys();
keysIterator = keys.listIterator();
BatchTransactionSynchronizationManager.registerSynchronization(synchronization);
super.registerSynchronization();
initialized = true;
}
@@ -186,19 +184,6 @@ public class DrivingQueryInputSource implements InputSource, ResourceLifecycle,
return keyGenerator.getKeyAsRestartData(getCurrentKey());
}
/**
* Encapsulates transaction events handling.
*/
private class DrivingQueryInputSourceTransactionSynchronization extends TransactionSynchronizationAdapter {
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
keysIterator = keys.listIterator(lastCommitIndex);
} else if (status == TransactionSynchronization.STATUS_COMMITTED) {
lastCommitIndex = currentIndex;
}
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(keyGenerator, "The KeyGenerator must not be null.");
}
@@ -213,4 +198,12 @@ public class DrivingQueryInputSource implements InputSource, ResourceLifecycle,
this.keyGenerator = keyGenerator;
}
protected void transactionCommitted() {
lastCommitIndex = currentIndex;
}
protected void transactionRolledBack() {
keysIterator = keys.listIterator(lastCommitIndex);
}
}

View File

@@ -0,0 +1,8 @@
<html>
<body>
<p>
Infrastructure implementations of driving query based input sources. All input sources within this package
query a database to return a list of keys, and return back one key per call to read().
</p>
</body>
</html>

View File

@@ -32,6 +32,7 @@ import org.springframework.batch.io.OutputSource;
import org.springframework.batch.io.exception.BatchCriticalException;
import org.springframework.batch.io.exception.BatchEnvironmentException;
import org.springframework.batch.io.file.support.transform.Converter;
import org.springframework.batch.io.support.AbstractTransactionalIoSource;
import org.springframework.batch.item.ResourceLifecycle;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
import org.springframework.batch.restart.GenericRestartData;
@@ -62,8 +63,9 @@ import org.springframework.util.Assert;
* @author Robert Kasanicky
* @author Dave Syer
*/
public class FlatFileOutputSource implements OutputSource, ResourceLifecycle, Restartable, StatisticsProvider, InitializingBean,
DisposableBean {
public class FlatFileOutputSource extends AbstractTransactionalIoSource implements
OutputSource, ResourceLifecycle, Restartable, StatisticsProvider, InitializingBean,
DisposableBean {
/**
* @author dsyer
@@ -89,8 +91,6 @@ public class FlatFileOutputSource implements OutputSource, ResourceLifecycle, Re
private RestartData restartData = new GenericRestartData(new Properties());
private TransactionSynchronization transactionSynchronization = new FlatFileOutputTemplateTransactionSynchronization();
private OutputState state = new OutputState();
private Converter converter = new Converter() {
@@ -125,16 +125,16 @@ public class FlatFileOutputSource implements OutputSource, ResourceLifecycle, Re
}
/**
* Commit the transaction.
* Commit the transaction.
*/
private void transactionComitted() {
protected void transactionCommitted() {
getOutputState().mark();
}
/**
* Rollback the transaction.
*/
private void transactionRolledback() {
protected void transactionRolledBack() {
getOutputState().checkFileSize();
resetPositionForRestart();
}
@@ -246,7 +246,7 @@ public class FlatFileOutputSource implements OutputSource, ResourceLifecycle, Re
* @see ResourceLifecycle#open()
*/
public void open() {
registerSynchronization();
super.registerSynchronization();
}
/**
@@ -281,22 +281,11 @@ public class FlatFileOutputSource implements OutputSource, ResourceLifecycle, Re
}
// Registers a new transaction synchronization for the current thread.
private void registerSynchronization() {
BatchTransactionSynchronizationManager.registerSynchronization(this.transactionSynchronization);
}
// Returns object representing state.
private OutputState getOutputState() {
return (OutputState) state;
}
// added package visibility method so that tests can invoke transaction
// events
TransactionSynchronization getTransactionSynchronization() {
return this.transactionSynchronization;
}
/**
* Encapsulates the runtime state of the output source. All state changing
* operations on the output source go through this class.
@@ -554,25 +543,4 @@ public class FlatFileOutputSource implements OutputSource, ResourceLifecycle, Re
}
}
/**
* Encapsulates transaction events.
*/
private class FlatFileOutputTemplateTransactionSynchronization extends TransactionSynchronizationAdapter {
/**
* TransactionSynchronization method indicating that a transaction has
* completed.
*
* @param status indicates whether it was a rollback or commit
*/
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_COMMITTED) {
transactionComitted();
}
else if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
transactionRolledback();
}
}
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support;
import java.io.IOException;
import java.io.InputStream;
@@ -14,6 +14,11 @@ import javax.xml.stream.events.StartElement;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.io.file.support.stax.DefaultFragmentEventReader;
import org.springframework.batch.io.file.support.stax.DefaultTransactionalEventReader;
import org.springframework.batch.io.file.support.stax.FragmentDeserializer;
import org.springframework.batch.io.file.support.stax.FragmentEventReader;
import org.springframework.batch.io.file.support.stax.TransactionalEventReader;
import org.springframework.batch.item.ResourceLifecycle;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
import org.springframework.batch.restart.GenericRestartData;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support;
import java.io.File;
import java.io.FileOutputStream;
@@ -15,6 +15,8 @@ import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import org.springframework.batch.io.OutputSource;
import org.springframework.batch.io.file.support.stax.NoStartEndDocumentStreamWriter;
import org.springframework.batch.io.file.support.stax.ObjectToXmlSerializer;
import org.springframework.batch.io.support.FileUtils;
import org.springframework.batch.item.ResourceLifecycle;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;

View File

@@ -1,11 +1,11 @@
package org.springframework.batch.io.oxm;
package org.springframework.batch.io.file.support.oxm;
import java.io.IOException;
import javax.xml.stream.XMLEventWriter;
import javax.xml.transform.Result;
import org.springframework.batch.io.stax.ObjectToXmlSerializer;
import org.springframework.batch.io.file.support.stax.ObjectToXmlSerializer;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.oxm.Marshaller;
import org.springframework.xml.transform.StaxResult;

View File

@@ -1,10 +1,10 @@
package org.springframework.batch.io.oxm;
package org.springframework.batch.io.file.support.oxm;
import java.io.IOException;
import javax.xml.stream.XMLEventReader;
import org.springframework.batch.io.stax.FragmentDeserializer;
import org.springframework.batch.io.file.support.stax.FragmentDeserializer;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.oxm.Unmarshaller;
import org.springframework.util.Assert;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLEventReader;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import java.util.NoSuchElementException;
@@ -19,7 +19,7 @@ import org.springframework.dao.DataAccessResourceFailureException;
*
* @author Robert Kasanicky
*/
class DefaultFragmentEventReader extends AbstractEventReaderWrapper implements FragmentEventReader {
public class DefaultFragmentEventReader extends AbstractEventReaderWrapper implements FragmentEventReader {
// true when the next event is the StartElement of next fragment
private boolean startFragmentFollows = false;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import java.util.NoSuchElementException;
@@ -16,7 +16,7 @@ import org.springframework.util.Assert;
* @author Tomas Slanina
* @author Robert Kasanicky
*/
class DefaultTransactionalEventReader extends AbstractEventReaderWrapper implements TransactionalEventReader, InitializingBean {
public class DefaultTransactionalEventReader extends AbstractEventReaderWrapper implements TransactionalEventReader, InitializingBean {
private EventSequence recorder = new EventSequence();

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import java.util.ArrayList;
import java.util.List;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventReader;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventReader;
@@ -9,7 +9,7 @@ import javax.xml.stream.XMLEventReader;
*
* @author Robert Kasanicky
*/
interface FragmentEventReader extends XMLEventReader {
public interface FragmentEventReader extends XMLEventReader {
/**
* Tells the event reader its cursor position is exactly before the fragment.

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
@@ -11,7 +11,7 @@ import javax.xml.stream.events.XMLEvent;
* @author peter.zozom
* @author Robert Kasanicky
*/
class NoStartEndDocumentStreamWriter extends AbstractEventWriterWrapper {
public class NoStartEndDocumentStreamWriter extends AbstractEventWriterWrapper {
public NoStartEndDocumentStreamWriter(XMLEventWriter wrappedEventWriter) {
super(wrappedEventWriter);

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventWriter;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventReader;
@@ -7,7 +7,7 @@ import javax.xml.stream.XMLEventReader;
*
* @author Robert Kasanicky
*/
interface TransactionalEventReader extends XMLEventReader{
public interface TransactionalEventReader extends XMLEventReader{
/**
* Callback on transaction rollback.

View File

@@ -0,0 +1,89 @@
/*
* 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.io.support;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.OutputSource;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* <p>Abstract class that abstracts away transaction handling from input and output sources.
* Since every {@link InputSource} or {@link OutputSource by nature wants to be notified of
* transaction events to maintain the contract that all calls to read or write will ensure
* that correct ordering is maintained regardless of rollbacks.</p>
*
* <p>This class is primarily useful because it allows its subclasses to implement a single
* method to be notified of a commit or rollback, rather than having an inner class that
* implements {@link TransactionSyncrhonization} and likely calls another method with similar
* symantics as commit and rollback.</p>
*
* <p>It should be noted that this implementation will only register for synchronization if
* a call to registerSynchronization() has been made. This is less than ideal, however, it
* is the best solution until {@link StepScope} is modified to handle registering synchronizations
* in a scoped manner. Otherwise, registering at instantiation or initialization (such as via the
* Spring {@link InitializingBean} interface) would cause commits to be called on input sources
* for all steps, rather than the currently running step.</p>
*
* @author Lucas Ward
* @since 1.0
* @see TransactionSynchronization
* @see TransactionSynchronizationManager
*/
public abstract class AbstractTransactionalIoSource {
private final TransactionSynchronization synchronization =
new InputSourceTransactionSynchronization();
/**
* Register for Synchronization. This method is left protected because clients of
* this class should not be registering for synchronization, but rather only
* subclasses, at the appropriate time, i.e. when they are not initialized.
*/
protected void registerSynchronization(){
BatchTransactionSynchronizationManager.registerSynchronization(synchronization);
}
/*
* Called when a transaction has been committed.
*
* @see TransactionSynchronization#afterCompletion
*/
protected abstract void transactionCommitted();
/*
* Called when a transaction has been rolled back.
*
* @see TransactionSynchronization#afterCompletion
*/
protected abstract void transactionRolledBack();
/**
* Encapsulates transaction events handling.
*/
private class InputSourceTransactionSynchronization extends TransactionSynchronizationAdapter {
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
transactionRolledBack();
} else if (status == TransactionSynchronization.STATUS_COMMITTED) {
transactionCommitted();
}
}
}
}

View File

@@ -3,14 +3,14 @@ package org.springframework.batch.io.cursor;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.cursor.JdbcCursorInputSource;
import org.springframework.batch.io.driving.FooRowMapper;
import org.springframework.batch.io.sql.AbstractSqlInputSourceIntegrationTests;
import org.springframework.batch.io.sql.AbstractJdbcInputSourceIntegrationTests;
/**
* Tests for {@link JdbcCursorInputSource}
*
* @author Robert Kasanicky
*/
public class SqlCursorInputSourceIntegrationTests extends AbstractSqlInputSourceIntegrationTests{
public class JdbcCursorInputSourceIntegrationTests extends AbstractJdbcInputSourceIntegrationTests{
protected InputSource createInputSource() throws Exception {
JdbcCursorInputSource result = new JdbcCursorInputSource();

View File

@@ -17,14 +17,14 @@ package org.springframework.batch.io.driving;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.driving.support.MultipleColumnJdbcKeyGenerator;
import org.springframework.batch.io.sql.AbstractSqlInputSourceIntegrationTests;
import org.springframework.batch.io.sql.AbstractJdbcInputSourceIntegrationTests;
/**
* @author Lucas Ward
*
*/
public class MultipleColumnJdbcDrivingQueryInputSourceIntegrationTests extends
AbstractSqlInputSourceIntegrationTests {
AbstractJdbcInputSourceIntegrationTests {
protected InputSource createInputSource() throws Exception {

View File

@@ -2,9 +2,9 @@ package org.springframework.batch.io.driving;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.driving.support.SingleColumnJdbcKeyGenerator;
import org.springframework.batch.io.sql.AbstractSqlInputSourceIntegrationTests;
import org.springframework.batch.io.sql.AbstractJdbcInputSourceIntegrationTests;
public class SingleColumnJdbcDrivingQueryInputSourceIntegrationTests extends AbstractSqlInputSourceIntegrationTests {
public class SingleColumnJdbcDrivingQueryInputSourceIntegrationTests extends AbstractJdbcInputSourceIntegrationTests {
protected InputSource source;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.batch.io.file.support;
package org.springframework.batch.io.file;
import java.math.BigDecimal;
import java.text.ParseException;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
@@ -9,7 +9,7 @@ import javax.xml.stream.events.XMLEvent;
*
* @author Robert Kasanicky
*/
class EventHelper {
public class EventHelper {
//utility class
private EventHelper() {}

View File

@@ -28,6 +28,8 @@ import org.springframework.batch.io.file.support.transform.Converter;
import org.springframework.batch.restart.RestartData;
import org.springframework.core.io.FileSystemResource;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionSynchronizationUtils;
/**
* Tests of regular usage for {@link FlatFileOutputSource} Exception cases will
@@ -41,7 +43,7 @@ import org.springframework.transaction.support.TransactionSynchronization;
public class FlatFileOutputSourceTests extends TestCase {
// object under test
private FlatFileOutputSource template = new FlatFileOutputSource();
private FlatFileOutputSource inputSource = new FlatFileOutputSource();
// String to be written into file by the FlatFileInputTemplate
private static final String TEST_STRING = "FlatFileOutputTemplateTest-OutputData";
@@ -58,12 +60,17 @@ public class FlatFileOutputSourceTests extends TestCase {
*/
protected void setUp() throws Exception {
if(TransactionSynchronizationManager.isSynchronizationActive()){
TransactionSynchronizationManager.clearSynchronization();
}
TransactionSynchronizationManager.initSynchronization();
outputFile = File.createTempFile("flatfile-output-", ".tmp");
template.setResource(new FileSystemResource(outputFile));
template.afterPropertiesSet();
inputSource.setResource(new FileSystemResource(outputFile));
inputSource.afterPropertiesSet();
template.open();
inputSource.open();
}
@@ -74,7 +81,7 @@ public class FlatFileOutputSourceTests extends TestCase {
if( reader != null){
reader.close();
}
template.close();
inputSource.close();
outputFile.delete();
}
@@ -97,8 +104,8 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteString() throws IOException {
template.write(TEST_STRING);
template.close();
inputSource.write(TEST_STRING);
inputSource.close();
String lineFromFile = readLine();
assertEquals(TEST_STRING, lineFromFile);
@@ -108,8 +115,8 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteCollection() throws IOException {
template.write(Collections.singleton(TEST_STRING));
template.close();
inputSource.write(Collections.singleton(TEST_STRING));
inputSource.close();
String lineFromFile = readLine();
assertEquals(TEST_STRING, lineFromFile);
}
@@ -118,14 +125,14 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteWithConverter() throws IOException {
template.setConverter(new Converter() {
inputSource.setConverter(new Converter() {
public Object convert(Object input) {
return "FOO:" + input;
}
});
Object data = new Object();
template.write(data);
template.close();
inputSource.write(data);
inputSource.close();
String lineFromFile = readLine();
// converter not used if input is String
assertEquals("FOO:" + data.toString(), lineFromFile);
@@ -135,14 +142,14 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteWithConverterAndInfiniteLoop() throws IOException {
template.setConverter(new Converter() {
inputSource.setConverter(new Converter() {
public Object convert(Object input) {
return "FOO:" + input;
}
});
Object data = new Object();
template.write(data);
template.close();
inputSource.write(data);
inputSource.close();
String lineFromFile = readLine();
// converter not used if input is String
assertEquals("FOO:" + data.toString(), lineFromFile);
@@ -152,14 +159,14 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteWithConverterAndInfiniteLoopInCollection() throws IOException {
template.setConverter(new Converter() {
inputSource.setConverter(new Converter() {
public Object convert(Object input) {
return "FOO:" + input;
}
});
Object data = new Object();
template.write(new Object[] {data, data});
template.close();
inputSource.write(new Object[] {data, data});
inputSource.close();
String lineFromFile = readLine();
assertEquals("FOO:" + data.toString(), lineFromFile);
lineFromFile = readLine();
@@ -170,7 +177,7 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteWithConverterAndInfiniteLoopInConvertedCollection() throws IOException {
template.setConverter(new Converter() {
inputSource.setConverter(new Converter() {
boolean converted = false;
public Object convert(Object input) {
if (converted) {
@@ -182,13 +189,13 @@ public class FlatFileOutputSourceTests extends TestCase {
});
Object data = new Object();
try {
template.write(data);
inputSource.write(data);
fail("Expected IllegalStateException");
} catch (IllegalStateException e) {
// expected
assertTrue("Wrong message: "+e, e.getMessage().toLowerCase().indexOf("infinite")>=0);
}
template.close();
inputSource.close();
String lineFromFile = readLine();
assertNull(lineFromFile);
}
@@ -197,13 +204,13 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteWithConverterAndString() throws IOException {
template.setConverter(new Converter() {
inputSource.setConverter(new Converter() {
public Object convert(Object input) {
return "FOO:" + input;
}
});
template.write(Collections.singleton(TEST_STRING));
template.close();
inputSource.write(Collections.singleton(TEST_STRING));
inputSource.close();
String lineFromFile = readLine();
// converter not used if input is String
assertEquals(TEST_STRING, lineFromFile);
@@ -213,13 +220,13 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteWithConverterAndCollectionOfString() throws IOException {
template.setConverter(new Converter() {
inputSource.setConverter(new Converter() {
public Object convert(Object input) {
return "FOO:" + input;
}
});
template.write(TEST_STRING);
template.close();
inputSource.write(TEST_STRING);
inputSource.close();
String lineFromFile = readLine();
// converter not used if input is String
assertEquals(TEST_STRING, lineFromFile);
@@ -229,8 +236,8 @@ public class FlatFileOutputSourceTests extends TestCase {
* Regular usage of <code>write(String)</code> method
*/
public void testWriteArray() throws IOException {
template.write(new String[] { TEST_STRING, TEST_STRING });
template.close();
inputSource.write(new String[] { TEST_STRING, TEST_STRING });
inputSource.close();
String lineFromFile = readLine();
assertEquals(TEST_STRING, lineFromFile);
lineFromFile = readLine();
@@ -244,35 +251,35 @@ public class FlatFileOutputSourceTests extends TestCase {
String args = "1";
// AggregatorStub ignores the LineDescriptor, so we pass null
template.write(args);
template.close();
inputSource.write(args);
inputSource.close();
String lineFromFile = readLine();
assertEquals(args, lineFromFile);
}
public void testRollback() throws Exception {
template.write("testLine1");
inputSource.write("testLine1");
// rollback
template.getTransactionSynchronization().afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
template.close();
rollback();
inputSource.close();
String lineFromFile = readLine();
assertEquals(null, lineFromFile);
}
public void testCommit() throws Exception {
template.write("testLine1");
inputSource.write("testLine1");
// rollback
template.getTransactionSynchronization().afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
template.close();
commit();
inputSource.close();
String lineFromFile = readLine();
assertEquals("testLine1", lineFromFile);
}
public void testUnknown() throws Exception {
template.write("testLine1");
inputSource.write("testLine1");
// rollback
template.getTransactionSynchronization().afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
template.close();
unknown();
inputSource.close();
String lineFromFile = readLine();
assertEquals("testLine1", lineFromFile);
}
@@ -280,38 +287,38 @@ public class FlatFileOutputSourceTests extends TestCase {
public void testRestart() throws IOException {
// write some lines
template.write("testLine1");
template.write("testLine2");
template.write("testLine3");
inputSource.write("testLine1");
inputSource.write("testLine2");
inputSource.write("testLine3");
// commit
template.getTransactionSynchronization().afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
commit();
// this will be rolled back...
template.write("this will be rolled back");
inputSource.write("this will be rolled back");
// rollback
template.getTransactionSynchronization().afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
rollback();
// write more lines
template.write("testLine4");
template.write("testLine5");
inputSource.write("testLine4");
inputSource.write("testLine5");
// commit
template.getTransactionSynchronization().afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
commit();
// get restart data
RestartData restartData = template.getRestartData();
RestartData restartData = inputSource.getRestartData();
// close template
template.close();
inputSource.close();
// init for restart
template.setBufferSize(0);
template.open();
inputSource.setBufferSize(0);
inputSource.open();
// try empty restart data...
try {
template.restoreFrom(null);
inputSource.restoreFrom(null);
assertTrue(true);
}
catch (IllegalArgumentException iae) {
@@ -319,15 +326,15 @@ public class FlatFileOutputSourceTests extends TestCase {
}
// init with correct data
template.restoreFrom(restartData);
inputSource.restoreFrom(restartData);
// write more lines
template.write("testLine6");
template.write("testLine7");
template.write("testLine8");
inputSource.write("testLine6");
inputSource.write("testLine7");
inputSource.write("testLine8");
// close template
template.close();
inputSource.close();
// verify what was written to the file
for (int i = 1; i < 9; i++) {
@@ -344,9 +351,9 @@ public class FlatFileOutputSourceTests extends TestCase {
}
public void testAfterPropertiesSetChecksMandatory() throws Exception {
template = new FlatFileOutputSource();
inputSource = new FlatFileOutputSource();
try {
template.afterPropertiesSet();
inputSource.afterPropertiesSet();
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException e) {
@@ -355,10 +362,28 @@ public class FlatFileOutputSourceTests extends TestCase {
}
public void testDefaultRestartData() throws Exception {
template = new FlatFileOutputSource();
RestartData restartData = template.getRestartData();
inputSource = new FlatFileOutputSource();
RestartData restartData = inputSource.getRestartData();
assertNotNull(restartData);
// TODO: assert the properties of the default restart data
assertEquals(1, restartData.getProperties().size());
}
private void commit() {
TransactionSynchronizationUtils.invokeAfterCompletion(
TransactionSynchronizationManager.getSynchronizations(),
TransactionSynchronization.STATUS_COMMITTED);
}
private void rollback() {
TransactionSynchronizationUtils.invokeAfterCompletion(
TransactionSynchronizationManager.getSynchronizations(),
TransactionSynchronization.STATUS_ROLLED_BACK);
}
private void unknown() {
TransactionSynchronizationUtils.invokeAfterCompletion(
TransactionSynchronizationManager.getSynchronizations(),
TransactionSynchronization.STATUS_UNKNOWN);
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support;
import java.io.IOException;
import java.io.InputStream;
@@ -15,6 +15,8 @@ import javax.xml.stream.events.XMLEvent;
import junit.framework.TestCase;
import org.springframework.batch.io.file.support.StaxEventReaderInputSource;
import org.springframework.batch.io.file.support.stax.FragmentDeserializer;
import org.springframework.batch.restart.GenericRestartData;
import org.springframework.batch.restart.RestartData;
import org.springframework.core.io.AbstractResource;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support;
import java.io.File;
import java.io.IOException;
@@ -11,7 +11,8 @@ import javax.xml.transform.Result;
import junit.framework.TestCase;
import org.apache.commons.io.FileUtils;
import org.springframework.batch.io.oxm.MarshallingObjectToXmlSerializer;
import org.springframework.batch.io.file.support.StaxEventWriterOutputSource;
import org.springframework.batch.io.file.support.oxm.MarshallingObjectToXmlSerializer;
import org.springframework.batch.restart.RestartData;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.io.oxm;
package org.springframework.batch.io.file.support.oxm;
import java.io.IOException;
@@ -26,6 +26,7 @@ import javax.xml.transform.Result;
import junit.framework.TestCase;
import org.springframework.batch.io.file.support.oxm.MarshallingObjectToXmlSerializer;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.XmlMappingException;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.oxm;
package org.springframework.batch.io.file.support.oxm;
import java.io.IOException;
@@ -8,6 +8,7 @@ import javax.xml.stream.XMLInputFactory;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.io.file.support.oxm.UnmarshallingFragmentDeserializer;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
@@ -22,6 +22,7 @@ import javax.xml.stream.events.XMLEvent;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.io.file.support.stax.AbstractEventReaderWrapper;
import com.bea.xml.stream.events.StartDocumentEvent;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLEventReader;
@@ -24,6 +24,7 @@ import javax.xml.stream.events.XMLEvent;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.io.file.support.stax.AbstractEventWriterWrapper;
import com.bea.xml.stream.events.StartDocumentEvent;
import com.bea.xml.stream.util.NamespaceContextImpl;

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import java.util.NoSuchElementException;
@@ -9,6 +9,10 @@ import javax.xml.stream.events.XMLEvent;
import junit.framework.TestCase;
import org.springframework.batch.io.file.support.EventHelper;
import org.springframework.batch.io.file.support.stax.DefaultFragmentEventReader;
import org.springframework.batch.io.file.support.stax.DefaultTransactionalEventReader;
import org.springframework.batch.io.file.support.stax.FragmentEventReader;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;

View File

@@ -1,10 +1,13 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import junit.framework.TestCase;
import org.springframework.batch.io.file.support.EventHelper;
import org.springframework.batch.io.file.support.stax.DefaultTransactionalEventReader;
import org.springframework.batch.io.file.support.stax.TransactionalEventReader;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;

View File

@@ -1,8 +1,10 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.events.XMLEvent;
import org.springframework.batch.io.file.support.stax.EventSequence;
import junit.framework.TestCase;
/**

View File

@@ -1,4 +1,4 @@
package org.springframework.batch.io.stax;
package org.springframework.batch.io.file.support.stax;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
@@ -7,6 +7,7 @@ import javax.xml.stream.events.XMLEvent;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.io.file.support.stax.NoStartEndDocumentStreamWriter;
/**
* Tests for {@link NoStartEndDocumentStreamWriter}

View File

@@ -22,7 +22,7 @@ import org.springframework.util.Assert;
* @author Lucas Ward
* @author Robert Kasanicky
*/
public abstract class AbstractSqlInputSourceIntegrationTests extends AbstractTransactionalDataSourceSpringContextTests {
public abstract class AbstractJdbcInputSourceIntegrationTests extends AbstractTransactionalDataSourceSpringContextTests {
protected InputSource source;

View File

@@ -0,0 +1,133 @@
/*
* 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.io.support;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionSynchronizationUtils;
import org.springframework.util.Assert;
/**
* @author Lucas Ward
*
*/
public class AbstractTransactionalIoSourceTests extends TestCase {
private MockIoSource source;
protected void setUp() throws Exception {
super.setUp();
source = new MockIoSource();
if(TransactionSynchronizationManager.isSynchronizationActive()){
TransactionSynchronizationManager.clearSynchronization();
}
TransactionSynchronizationManager.initSynchronization();
}
//AbstractInputSource should synchronize on first call to read.
public void testSynchronizationRegistration(){
source.registerSynchronization();
List synchronizations = (List)TransactionSynchronizationManager.getSynchronizations();
assertEquals(1, synchronizations.size());
}
public void testCommit(){
source.registerSynchronization();
commit();
assertTrue(source.commitCalled);
assertFalse(source.rollbackCalled);
}
public void testRollback(){
source.registerSynchronization();
rollback();
assertFalse(source.commitCalled);
assertTrue(source.rollbackCalled);
}
public void testCommitUnsynchronizedSource(){
commit();
assertFalse(source.commitCalled);
assertFalse(source.rollbackCalled);
}
public void testMultipleSynchronizations(){
source.registerSynchronization();
source.registerSynchronization();
//multiple calls to read should result in only one synchronization
List synchronizations = (List)TransactionSynchronizationManager.getSynchronizations();
assertEquals(1, synchronizations.size());
}
public void testUnknownStatus(){
invokeUnknown();
assertFalse(source.commitCalled);
assertFalse(source.rollbackCalled);
}
private static class MockIoSource extends AbstractTransactionalIoSource{
private boolean commitCalled = false;
private boolean rollbackCalled = false;
protected void transactionCommitted() {
Assert.isTrue(!commitCalled, "Commit aleady called");
commitCalled = true;
}
protected void transactionRolledBack() {
Assert.isTrue(!rollbackCalled, "Rollback aleady called");
rollbackCalled = true;
}
}
private void commit() {
TransactionSynchronizationUtils.invokeAfterCompletion(
TransactionSynchronizationManager.getSynchronizations(),
TransactionSynchronization.STATUS_COMMITTED);
}
private void rollback() {
TransactionSynchronizationUtils.invokeAfterCompletion(
TransactionSynchronizationManager.getSynchronizations(),
TransactionSynchronization.STATUS_ROLLED_BACK);
}
private void invokeUnknown() {
TransactionSynchronizationUtils.invokeAfterCompletion(
TransactionSynchronizationManager.getSynchronizations(),
TransactionSynchronization.STATUS_UNKNOWN);
}
}

View File

@@ -6,8 +6,9 @@ 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.batch.io.stax.StaxEventReaderInputSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.oxm.Unmarshaller;

View File

@@ -11,8 +11,9 @@ import junit.framework.TestCase;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.springframework.batch.io.file.support.StaxEventWriterOutputSource;
import org.springframework.batch.io.file.support.oxm.MarshallingObjectToXmlSerializer;
import org.springframework.batch.io.oxm.domain.Trade;
import org.springframework.batch.io.stax.StaxEventWriterOutputSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

View File

@@ -1,5 +1,5 @@
Trade: [isin=UK21341EAH41,quantity=211,price=31.11,customer=customer1]
Trade: [isin=UK21341EAH42,quantity=212,price=32.11,customer=customer2]
Trade: [isin=UK21341EAH43,quantity=213,price=33.11,customer=customer3]
Trade: [isin=UK21341EAH44,quantity=214,price=34.11,customer=customer4]
Trade: [isin=UK21341EAH45,quantity=215,price=35.11,customer=customer5]
Trade: [isin=UK21341EAH41,quantity=211,price=31.11,customer=customer1]
Trade: [isin=UK21341EAH42,quantity=212,price=32.11,customer=customer2]
Trade: [isin=UK21341EAH43,quantity=213,price=33.11,customer=customer3]
Trade: [isin=UK21341EAH44,quantity=214,price=34.11,customer=customer4]
Trade: [isin=UK21341EAH45,quantity=215,price=35.11,customer=customer5]

View File

@@ -1,2 +1,2 @@
[Trade: [isin=UK21341EAH45,quantity=978,price=98.34,customer=customer1], Trade: [isin=UK21341EAH46,quantity=112,price=18.12,customer=customer2]]
[Trade: [isin=UK21341EAH47,quantity=245,price=12.78,customer=customer2], Trade: [isin=UK21341EAH48,quantity=108,price=9.25,customer=customer3], Trade: [isin=UK21341EAH49,quantity=854,price=23.39,customer=customer4]]
[Trade: [isin=UK21341EAH45,quantity=978,price=98.34,customer=customer1], Trade: [isin=UK21341EAH46,quantity=112,price=18.12,customer=customer2]]
[Trade: [isin=UK21341EAH47,quantity=245,price=12.78,customer=customer2], Trade: [isin=UK21341EAH48,quantity=108,price=9.25,customer=customer3], Trade: [isin=UK21341EAH49,quantity=854,price=23.39,customer=customer4]]

View File

@@ -1,17 +1,17 @@
BEGIN_ORDER:13100345 2007/02/15
CUSTOMER:20014539 Peter Smith
ADDRESS:Oak Street 31/A Small Town00235
BILLING:VISA VISA-12345678903
ITEM:104439104137.49
ITEM:2134776319221.99
END_ORDER:267.34
BEGIN_ORDER:13100346 2007/02/15
CUSTOMER:72155919
ADDRESS:St. Andrews Road 31 London 55342
BILLING:AMEX AMEX-72345678903
ITEM:10443191011070.50
ITEM:213472721921.79
ITEM:104433930179.95
ITEM:213474731955.29
ITEM:1044359501339.99
END_ORDER:14043.74
BEGIN_ORDER:13100345 2007/02/15
CUSTOMER:20014539 Peter Smith
ADDRESS:Oak Street 31/A Small Town00235
BILLING:VISA VISA-12345678903
ITEM:104439104137.49
ITEM:2134776319221.99
END_ORDER:267.34
BEGIN_ORDER:13100346 2007/02/15
CUSTOMER:72155919
ADDRESS:St. Andrews Road 31 London 55342
BILLING:AMEX AMEX-72345678903
ITEM:10443191011070.50
ITEM:213472721921.79
ITEM:104433930179.95
ITEM:213474731955.29
ITEM:1044359501339.99
END_ORDER:14043.74

View File

@@ -22,12 +22,12 @@
<property name="itemProvider">
<bean class="org.springframework.batch.item.provider.InputSourceItemProvider">
<property name="inputSource">
<bean class="org.springframework.batch.io.stax.StaxEventReaderInputSource" scope="step">
<bean class="org.springframework.batch.io.file.support.StaxEventReaderInputSource" scope="step">
<aop:scoped-proxy/>
<property name="fragmentRootElementName" value="trade" />
<property name="resource" value="data/staxJob/input/20070918.testStream.xmlFileStep.xml" />
<property name="fragmentDeserializer">
<bean class="org.springframework.batch.io.oxm.UnmarshallingFragmentDeserializer">
<bean class="org.springframework.batch.io.file.support.oxm.UnmarshallingFragmentDeserializer">
<constructor-arg>
<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases" ref="aliases" />
@@ -50,14 +50,14 @@
</property>
</bean>
<bean class="org.springframework.batch.io.stax.StaxEventWriterOutputSource" id="tradeStaxWriter">
<bean class="org.springframework.batch.io.file.support.StaxEventWriterOutputSource" id="tradeStaxWriter">
<property name="resource" value="file:20070918.testStream.xmlFileStep.output.xml" />
<property name="serializer" ref="tradeMarshallingSerializer" />
<property name="rootTagName" value="trades" />
<property name="overwriteOutput" value="true" />
</bean>
<bean class="org.springframework.batch.io.oxm.MarshallingObjectToXmlSerializer" id="tradeMarshallingSerializer">
<bean class="org.springframework.batch.io.file.support.oxm.MarshallingObjectToXmlSerializer" id="tradeMarshallingSerializer">
<constructor-arg>
<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases" ref="aliases" />