IN PROGRESS - issue BATCH-7: Remove transaction synchronization and state management from input/output sources (formerly buffering)

http://jira.springframework.org/browse/BATCH-7

Added mark()/reset() to ItemStream, and implementations.
This commit is contained in:
dsyer
2008-01-31 16:31:31 +00:00
parent 4a10c64b2e
commit 74d4ec612c
18 changed files with 388 additions and 81 deletions

View File

@@ -56,15 +56,13 @@ import org.springframework.util.StringUtils;
* @author Robert Kasanicky
* @author Dave Syer
*/
public class HibernateCursorItemReader extends AbstractItemReader implements ItemReader, ItemStream,
Skippable, InitializingBean, DisposableBean {
public class HibernateCursorItemReader extends AbstractItemReader implements ItemReader, ItemStream, Skippable,
InitializingBean, DisposableBean {
private static final String RESTART_DATA_ROW_NUMBER_KEY = ClassUtils
.getShortName(HibernateCursorItemReader.class)
private static final String RESTART_DATA_ROW_NUMBER_KEY = ClassUtils.getShortName(HibernateCursorItemReader.class)
+ ".rowNumber";
private static final String SKIPPED_ROWS = ClassUtils
.getShortName(HibernateCursorItemReader.class)
private static final String SKIPPED_ROWS = ClassUtils.getShortName(HibernateCursorItemReader.class)
+ ".skippedRows";;
private SessionFactory sessionFactory;
@@ -124,7 +122,8 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
skipCount = 0;
if (useStatelessSession) {
statelessSession.close();
} else {
}
else {
statefulSession.close();
}
}
@@ -136,19 +135,18 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
if (useStatelessSession) {
statelessSession = sessionFactory.openStatelessSession();
cursor = statelessSession.createQuery(queryString).scroll();
} else {
}
else {
statefulSession = sessionFactory.openSession();
cursor = statefulSession.createQuery(queryString).scroll();
}
BatchTransactionSynchronizationManager
.registerSynchronization(synchronization);
BatchTransactionSynchronizationManager.registerSynchronization(synchronization);
initialized = true;
}
/**
* @param sessionFactory
* hibernate session factory
* @param sessionFactory hibernate session factory
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
@@ -164,8 +162,7 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
}
/**
* @param queryString
* HQL query string
* @param queryString HQL query string
*/
public void setQueryString(String queryString) {
this.queryString = queryString;
@@ -174,9 +171,9 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
/**
* Can be set only in uninitialized state.
*
* @param useStatelessSession
* <code>true</code> to use {@link StatelessSession}
* <code>false</code> to use standard hibernate {@link Session}
* @param useStatelessSession <code>true</code> to use
* {@link StatelessSession} <code>false</code> to use standard hibernate
* {@link Session}
*/
public void setUseStatelessSession(boolean useStatelessSession) {
Assert.state(!initialized);
@@ -188,10 +185,9 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
*/
public StreamContext getStreamContext() {
Properties props = new Properties();
props.setProperty(RESTART_DATA_ROW_NUMBER_KEY, ""+currentProcessedRow);
props.setProperty(RESTART_DATA_ROW_NUMBER_KEY, "" + currentProcessedRow);
String skipped = skippedRows.toString();
props.setProperty(SKIPPED_ROWS, skipped.substring(1,
skipped.length() - 1));
props.setProperty(SKIPPED_ROWS, skipped.substring(1, skipped.length() - 1));
return new GenericStreamContext(props);
}
@@ -200,25 +196,21 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
* Sets the cursor to the received row number.
*/
public void restoreFrom(StreamContext data) {
Assert
.state(!initialized,
"Cannot restore when already intialized. Call close() first before restore()");
Assert.state(!initialized, "Cannot restore when already intialized. Call close() first before restore()");
Properties props = data.getProperties();
if (props.getProperty(RESTART_DATA_ROW_NUMBER_KEY) == null) {
return;
}
currentProcessedRow = Integer.parseInt(props
.getProperty(RESTART_DATA_ROW_NUMBER_KEY));
currentProcessedRow = Integer.parseInt(props.getProperty(RESTART_DATA_ROW_NUMBER_KEY));
open();
cursor.setRowNumber(currentProcessedRow-1);
cursor.setRowNumber(currentProcessedRow - 1);
if (!props.containsKey(SKIPPED_ROWS)) {
return;
}
String[] skipped = StringUtils.commaDelimitedListToStringArray(props
.getProperty(SKIPPED_ROWS));
String[] skipped = StringUtils.commaDelimitedListToStringArray(props.getProperty(SKIPPED_ROWS));
for (int i = 0; i < skipped.length; i++) {
this.skippedRows.add(new Integer(skipped[i]));
}
@@ -238,26 +230,53 @@ public class HibernateCursorItemReader extends AbstractItemReader implements Ite
/**
* Encapsulates transaction events handling.
*/
private class HibernateItemReaderTransactionSynchronization extends
TransactionSynchronizationAdapter {
private class HibernateItemReaderTransactionSynchronization extends TransactionSynchronizationAdapter {
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
currentProcessedRow = lastCommitRowNumber;
if (lastCommitRowNumber == 0) {
cursor.beforeFirst();
} else {
// Set the cursor so that next time it is advanced it will
// come back to the committed row.
cursor.setRowNumber(lastCommitRowNumber-1);
}
} else if (status == TransactionSynchronization.STATUS_COMMITTED) {
lastCommitRowNumber = currentProcessedRow;
if (!useStatelessSession) {
statefulSession.clear();
}
mark(null);
}
else if (status == TransactionSynchronization.STATUS_COMMITTED) {
reset(null);
}
}
}
/**
* Always true, but only supported through a single processed row count, so
* do not use in an asynchronous setting.
*
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
currentProcessedRow = lastCommitRowNumber;
if (lastCommitRowNumber == 0) {
cursor.beforeFirst();
}
else {
// Set the cursor so that next time it is advanced it will
// come back to the committed row.
cursor.setRowNumber(lastCommitRowNumber - 1);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
lastCommitRowNumber = currentProcessedRow;
if (!useStatelessSession) {
statefulSession.clear();
}
}
}

View File

@@ -233,7 +233,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource implemen
* Mark the current row. Calling reset will cause the result set to be set
* to the current row when mark was called.
*/
protected void transactionCommitted() {
public void mark(StreamContext streamContext) {
lastCommittedRow = currentProcessedRow;
skippedRows.clear();
}
@@ -243,7 +243,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource implemen
*
* @throws DataAccessException
*/
protected void transactionRolledBack() {
public void reset(StreamContext streamContext) {
try {
currentProcessedRow = lastCommittedRow;
if (currentProcessedRow > 0) {

View File

@@ -209,11 +209,11 @@ public class DrivingQueryItemReader extends AbstractTransactionalIoSource
}
protected void transactionCommitted() {
lastCommitIndex = currentIndex;
mark(null);
}
protected void transactionRolledBack() {
keysIterator = keys.listIterator(lastCommitIndex);
reset(null);
}
/**
@@ -224,4 +224,27 @@ public class DrivingQueryItemReader extends AbstractTransactionalIoSource
return item;
}
/**
* Returns true.
*
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
lastCommitIndex = currentIndex;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
keysIterator = keys.listIterator(lastCommitIndex);
}
}

View File

@@ -39,6 +39,7 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter
* @author Waseem Malik
* @author Tomas Slanina
* @author Robert Kasanicky
* @author Dave Syer
*/
public class DefaultFlatFileItemReader extends SimpleFlatFileItemReader implements Skippable, ItemStream {
@@ -109,13 +110,34 @@ public class DefaultFlatFileItemReader extends SimpleFlatFileItemReader implemen
return streamContext;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
getReader().mark();
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
getReader().reset();
}
/**
* This method marks the start of a transaction. It marks the InputBuffer
* Reader, so that in case of rollback it can position the file to start of
* the transaction.
*/
private void transactionStarted() {
getReader().mark();
mark(null);
}
/**
@@ -130,7 +152,7 @@ public class DefaultFlatFileItemReader extends SimpleFlatFileItemReader implemen
* Rollback the transaction.
*/
private void transactionRolledback() {
getReader().reset();
reset(null);
}
/**

View File

@@ -119,15 +119,14 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
* Commit the transaction.
*/
protected void transactionCommitted() {
getOutputState().mark();
mark(null);
}
/**
* Rollback the transaction.
*/
protected void transactionRolledBack() {
getOutputState().checkFileSize();
resetPositionForRestart();
reset(null);
}
// This method removes any information in the file before this reset point.
@@ -539,4 +538,27 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
}
}
/**
* Returns true.
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
getOutputState().mark();
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
getOutputState().checkFileSize();
resetPositionForRestart();
}
}

View File

@@ -17,6 +17,8 @@ package org.springframework.batch.io.support;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.ItemStreamAdapter;
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.support.TransactionSynchronization;
@@ -54,7 +56,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
* @see TransactionSynchronization
* @see TransactionSynchronizationManager
*/
public abstract class AbstractTransactionalIoSource {
public abstract class AbstractTransactionalIoSource extends ItemStreamAdapter {
private final TransactionSynchronization synchronization = new AbstractTransactionalIoSourceTransactionSynchronization();
@@ -74,14 +76,14 @@ public abstract class AbstractTransactionalIoSource {
*
* @see TransactionSynchronization#afterCompletion
*/
protected abstract void transactionCommitted();
public abstract void mark(StreamContext streamContext);
/*
* Called when a transaction has been rolled back.
*
* @see TransactionSynchronization#afterCompletion
*/
protected abstract void transactionRolledBack();
public abstract void reset(StreamContext streamContext);
/**
* Encapsulates transaction events handling.
@@ -90,10 +92,18 @@ public abstract class AbstractTransactionalIoSource {
TransactionSynchronizationAdapter {
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
transactionRolledBack();
reset(null);
} else if (status == TransactionSynchronization.STATUS_COMMITTED) {
transactionCommitted();
mark(null);
}
}
}
/* (non-Javadoc)
* @see org.springframework.batch.item.stream.ItemStreamAdapter#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
}

View File

@@ -269,14 +269,10 @@ public class StaxEventItemReader extends AbstractItemReader implements ItemReade
*/
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_COMMITTED) {
lastCommitPointRecordCount = currentRecordCount;
txReader.onCommit();
skipRecords = new ArrayList();
mark(null);
}
else if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
currentRecordCount = lastCommitPointRecordCount;
txReader.onRollback();
fragmentReader.reset();
reset(null);
}
}
@@ -290,4 +286,30 @@ public class StaxEventItemReader extends AbstractItemReader implements ItemReade
BatchTransactionSynchronizationManager.registerSynchronization(synchronization);
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
lastCommitPointRecordCount = currentRecordCount;
txReader.onCommit();
skipRecords = new ArrayList();
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
currentRecordCount = lastCommitPointRecordCount;
txReader.onRollback();
fragmentReader.reset();
}
}

View File

@@ -464,20 +464,11 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream, Initializing
}
private void transactionComitted() {
lastCommitPointPosition = getPosition();
lastCommitPointRecordCount = currentRecordCount;
mark(null);
}
private void transactionRolledback() {
currentRecordCount = lastCommitPointRecordCount;
// close output
close();
// and reopen it - we do this because we need to reopen stream
// reader at specified position - calling setPosition() is not
// enough!
restarted = true;
open(lastCommitPointPosition);
reset(null);
}
}
@@ -486,4 +477,33 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream, Initializing
return synchronization;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
lastCommitPointPosition = getPosition();
lastCommitPointRecordCount = currentRecordCount;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
currentRecordCount = lastCommitPointRecordCount;
// close output
close();
// and reopen it - we do this because we need to reopen stream
// reader at specified position - calling setPosition() is not
// enough!
restarted = true;
open(lastCommitPointPosition);
}
}

View File

@@ -31,7 +31,7 @@ package org.springframework.batch.item;
* provided.
* </p>
*
* @author Lucas Ward
* @author Dave Syer
*
*/
public interface ItemStream extends StreamContextProvider {
@@ -55,4 +55,34 @@ public interface ItemStream extends StreamContextProvider {
* (except open) may throw an exception.
*/
void close() throws StreamException;
/**
* Clients are expected to check this flag before calling mark or reset.
*
* @return true if mark and reset are supported by the {@link ItemStream}
*/
boolean isMarkSupported();
/**
* Mark the stream so that it can be reset later and the items backed out.
* Implementations may use the information in the provided context to make
* calculations that account for things like multiple open cursors. The
* context should also be updated with any information of this nature that
* might be needed by a reset or by future calls to mark.
*
* @param the context which might contain information needed to determine
* what action to take, and into which the current mark information can go.
*
* @throws UnsupportedOperationException if the operation is not supported
*/
void mark(StreamContext streamContext);
/**
* Reset the stream to the last mark. After a reset the stream state will be
* such that changes (items read or written) since the last call to mark
* with the same context will not be visible after a call to close.
*
* @throws UnsupportedOperationException if the operation is not supported
*/
void reset(StreamContext streamContext);
}

View File

@@ -0,0 +1,29 @@
/*
* 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.item.reader;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.stream.ItemStreamAdapter;
/**
* Base class for {@link ItemReader} implementations.
* @author Dave Syer
*
*/
public abstract class AbstractItemStreamItemReader extends ItemStreamAdapter implements ItemReader {
}

View File

@@ -103,4 +103,32 @@ public class DelegatingItemReader extends AbstractItemReader implements Skippabl
((ItemStream) inputSource).close();
}
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
if (inputSource instanceof ItemStream) {
return ((ItemStream) inputSource).isMarkSupported();
}
return false;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
if (inputSource instanceof ItemStream) {
((ItemStream) inputSource).mark(streamContext);
}
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
if (inputSource instanceof ItemStream) {
((ItemStream) inputSource).reset(streamContext);
}
}
}

View File

@@ -56,4 +56,25 @@ public class ItemStreamAdapter implements ItemStream {
return new GenericStreamContext(new Properties());
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
*/
public boolean isMarkSupported() {
return false;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#mark(org.springframework.batch.item.StreamContext)
*/
public void mark(StreamContext streamContext) {
throw new UnsupportedOperationException("Mark operation not supported.");
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#reset(org.springframework.batch.item.StreamContext)
*/
public void reset(StreamContext streamContext) {
throw new UnsupportedOperationException("Reset operation not supported.");
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.item.writer;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.stream.ItemStreamAdapter;
/**
* Base class for {@link ItemWriter} implementations.
* @author Dave Syer
*
*/
public abstract class AbstractItemStreamItemWriter extends ItemStreamAdapter implements ItemWriter {
}