IN PROGRESS - issue BATCH-7: Remove transaction synchronization and state management from input/output sources (formerly buffering)
http://jira.springframework.org/browse/BATCH-7 Remove Statistics* - use StreamContext instead.
This commit is contained in:
@@ -23,7 +23,6 @@ import java.sql.SQLWarning;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -35,7 +34,6 @@ import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.KeyedItemReader;
|
||||
import org.springframework.batch.item.StreamContext;
|
||||
import org.springframework.batch.item.stream.GenericStreamContext;
|
||||
import org.springframework.batch.statistics.StatisticsProvider;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@@ -115,9 +113,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Lucas Ward
|
||||
* @author Peter Zozom
|
||||
*/
|
||||
public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
implements KeyedItemReader, DisposableBean,
|
||||
InitializingBean, ItemStream, StatisticsProvider, Skippable {
|
||||
public class JdbcCursorItemReader extends AbstractTransactionalIoSource implements KeyedItemReader, DisposableBean,
|
||||
InitializingBean, ItemStream, Skippable {
|
||||
|
||||
private static Log log = LogFactory.getLog(JdbcCursorItemReader.class);
|
||||
|
||||
@@ -156,9 +153,9 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
private SQLExceptionTranslator exceptionTranslator;
|
||||
|
||||
/* Current count of processed records. */
|
||||
private int currentProcessedRow = 0;
|
||||
private long currentProcessedRow = 0;
|
||||
|
||||
private int lastCommittedRow = 0;
|
||||
private long lastCommittedRow = 0;
|
||||
|
||||
private RowMapper mapper;
|
||||
|
||||
@@ -167,8 +164,8 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
/**
|
||||
* Assert that mandatory properties are set.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if either data source or sql properties not set.
|
||||
* @throws IllegalArgumentException if either data source or sql properties
|
||||
* not set.
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(dataSource, "DataSOurce must be provided");
|
||||
@@ -193,8 +190,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
*
|
||||
* @returns Object returned by RowMapper
|
||||
* @throws DataAccessException
|
||||
* @throws IllegalStateExceptino
|
||||
* if mapper is null.
|
||||
* @throws IllegalStateExceptino if mapper is null.
|
||||
*/
|
||||
public Object read() {
|
||||
|
||||
@@ -207,12 +203,12 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
try {
|
||||
if (!rs.next()) {
|
||||
return null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
currentProcessedRow++;
|
||||
if (!skippedRows.isEmpty()) {
|
||||
// while is necessary to handle successive skips.
|
||||
while (skippedRows
|
||||
.contains(new Integer(currentProcessedRow))) {
|
||||
while (skippedRows.contains(new Long(currentProcessedRow))) {
|
||||
if (!rs.next()) {
|
||||
return null;
|
||||
}
|
||||
@@ -220,20 +216,20 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
}
|
||||
}
|
||||
|
||||
Object mappedResult = mapper.mapRow(rs, currentProcessedRow);
|
||||
Object mappedResult = mapper.mapRow(rs, (int)currentProcessedRow);
|
||||
|
||||
verifyCursorPosition(currentProcessedRow);
|
||||
|
||||
return mappedResult;
|
||||
}
|
||||
} catch (SQLException se) {
|
||||
throw getExceptionTranslator().translate(
|
||||
"Trying to process next row", sql, se);
|
||||
}
|
||||
catch (SQLException se) {
|
||||
throw getExceptionTranslator().translate("Trying to process next row", sql, se);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getCurrentProcessedRow() {
|
||||
public long getCurrentProcessedRow() {
|
||||
return currentProcessedRow;
|
||||
}
|
||||
|
||||
@@ -255,15 +251,15 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
try {
|
||||
currentProcessedRow = lastCommittedRow;
|
||||
if (currentProcessedRow > 0) {
|
||||
rs.absolute(currentProcessedRow);
|
||||
} else {
|
||||
rs.absolute((int)currentProcessedRow);
|
||||
}
|
||||
else {
|
||||
rs.beforeFirst();
|
||||
}
|
||||
|
||||
} catch (SQLException se) {
|
||||
throw getExceptionTranslator().translate(
|
||||
"Attempted to move ResultSet to last committed row", sql,
|
||||
se);
|
||||
}
|
||||
catch (SQLException se) {
|
||||
throw getExceptionTranslator().translate("Attempted to move ResultSet to last committed row", sql, se);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,12 +293,10 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
// Check the result set is in synch with the currentRow attribute. This is
|
||||
// important
|
||||
// to ensure that the user hasn't modified the current row.
|
||||
private void verifyCursorPosition(int expectedCurrentRow)
|
||||
throws SQLException {
|
||||
private void verifyCursorPosition(long expectedCurrentRow) throws SQLException {
|
||||
if (verifyCursorPosition) {
|
||||
if (expectedCurrentRow != this.rs.getRow()) {
|
||||
throw new InvalidDataAccessResourceUsageException(
|
||||
"Unexpected cursor position change.");
|
||||
throw new InvalidDataAccessResourceUsageException("Unexpected cursor position change.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,17 +314,15 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
|
||||
try {
|
||||
this.con = dataSource.getConnection();
|
||||
this.stmt = this.con.createStatement(
|
||||
ResultSet.TYPE_SCROLL_INSENSITIVE,
|
||||
ResultSet.CONCUR_READ_ONLY,
|
||||
this.stmt = this.con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY,
|
||||
ResultSet.HOLD_CURSORS_OVER_COMMIT);
|
||||
applyStatementSettings(this.stmt);
|
||||
this.rs = this.stmt.executeQuery(sql);
|
||||
handleWarnings(this.stmt.getWarnings());
|
||||
} catch (SQLException se) {
|
||||
}
|
||||
catch (SQLException se) {
|
||||
close();
|
||||
throw getExceptionTranslator()
|
||||
.translate("Executing query", sql, se);
|
||||
throw getExceptionTranslator().translate("Executing query", sql, se);
|
||||
}
|
||||
|
||||
super.registerSynchronization();
|
||||
@@ -367,9 +359,9 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
protected SQLExceptionTranslator getExceptionTranslator() {
|
||||
if (exceptionTranslator == null) {
|
||||
if (dataSource != null) {
|
||||
exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(
|
||||
dataSource);
|
||||
} else {
|
||||
exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
|
||||
}
|
||||
else {
|
||||
exceptionTranslator = new SQLStateSQLExceptionTranslator();
|
||||
}
|
||||
}
|
||||
@@ -389,13 +381,12 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
if (ignoreWarnings) {
|
||||
SQLWarning warningToLog = warnings;
|
||||
while (warningToLog != null) {
|
||||
log.debug("SQLWarning ignored: SQL state '"
|
||||
+ warningToLog.getSQLState() + "', error code '"
|
||||
+ warningToLog.getErrorCode() + "', message ["
|
||||
+ warningToLog.getMessage() + "]");
|
||||
log.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '"
|
||||
+ warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");
|
||||
warningToLog = warningToLog.getNextWarning();
|
||||
}
|
||||
} else if (warnings != null) {
|
||||
}
|
||||
else if (warnings != null) {
|
||||
throw new SQLWarningException("Warning not ignored", warnings);
|
||||
}
|
||||
}
|
||||
@@ -406,12 +397,12 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* @see org.springframework.batch.restart.Restartable#getRestartData()
|
||||
*/
|
||||
public StreamContext getStreamContext() {
|
||||
StreamContext streamContext = new StreamContext();
|
||||
|
||||
String skipped = skippedRows.toString();
|
||||
Properties statistics = getStatistics();
|
||||
statistics.setProperty(SKIPPED_ROWS, skipped.substring(1,skipped.length()-1));
|
||||
return new GenericStreamContext(statistics);
|
||||
StreamContext context = new GenericStreamContext();
|
||||
context.putString(SKIPPED_ROWS, skipped.substring(1, skipped.length() - 1));
|
||||
context.putLong(CURRENT_PROCESSED_ROW, currentProcessedRow);
|
||||
context.putLong(SKIP_COUNT, skipCount);
|
||||
return context;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -427,18 +418,17 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
|
||||
open();
|
||||
|
||||
// Properties restartProperties = data.getProperties();
|
||||
// Properties restartProperties = data.getProperties();
|
||||
if (!data.containsKey(CURRENT_PROCESSED_ROW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.currentProcessedRow = new Long(data.getLong(CURRENT_PROCESSED_ROW)).intValue();
|
||||
rs.absolute(currentProcessedRow);
|
||||
} catch (SQLException se) {
|
||||
throw getExceptionTranslator().translate(
|
||||
"Attempted to move ResultSet to last committed row", sql,
|
||||
se);
|
||||
this.currentProcessedRow = data.getLong(CURRENT_PROCESSED_ROW);
|
||||
rs.absolute((int)currentProcessedRow);
|
||||
}
|
||||
catch (SQLException se) {
|
||||
throw getExceptionTranslator().translate("Attempted to move ResultSet to last committed row", sql, se);
|
||||
}
|
||||
|
||||
if (!data.containsKey(SKIPPED_ROWS)) {
|
||||
@@ -447,24 +437,10 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
|
||||
String[] skipped = StringUtils.commaDelimitedListToStringArray(data.getString(SKIPPED_ROWS));
|
||||
for (int i = 0; i < skipped.length; i++) {
|
||||
this.skippedRows.add(new Integer(skipped[i]));
|
||||
this.skippedRows.add(new Long(skipped[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.batch.statistics.StatisticsProvider#getStatistics()
|
||||
*/
|
||||
public Properties getStatistics() {
|
||||
|
||||
Properties props = new Properties();
|
||||
props.setProperty(CURRENT_PROCESSED_ROW, new Integer(
|
||||
currentProcessedRow).toString());
|
||||
props.setProperty(SKIP_COUNT, new Integer(skipCount).toString());
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the current row. If the transaction is rolled back, this row will
|
||||
* not be represented to the RowMapper when read() is called. For example,
|
||||
@@ -472,7 +448,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* continue processing and find
|
||||
*/
|
||||
public void skip() {
|
||||
skippedRows.add(new Integer(currentProcessedRow));
|
||||
skippedRows.add(new Long(currentProcessedRow));
|
||||
skipCount++;
|
||||
}
|
||||
|
||||
@@ -482,8 +458,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* <code>ResultSet</code> object. If the fetch size specified is zero, the
|
||||
* JDBC driver ignores the value.
|
||||
*
|
||||
* @param fetchSize
|
||||
* the number of rows to fetch
|
||||
* @param fetchSize the number of rows to fetch
|
||||
* @see ResultSet#setFetchSize(int)
|
||||
*/
|
||||
public void setFetchSize(int fetchSize) {
|
||||
@@ -494,8 +469,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* Sets the limit for the maximum number of rows that any
|
||||
* <code>ResultSet</code> object can contain to the given number.
|
||||
*
|
||||
* @param maxRows
|
||||
* the new max rows limit; zero means there is no limit
|
||||
* @param maxRows the new max rows limit; zero means there is no limit
|
||||
* @see Statement#setMaxRows(int)
|
||||
*/
|
||||
public void setMaxRows(int maxRows) {
|
||||
@@ -508,9 +482,8 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* seconds. If the limit is exceeded, an <code>SQLException</code> is
|
||||
* thrown.
|
||||
*
|
||||
* @param queryTimeout
|
||||
* seconds the new query timeout limit in seconds; zero means
|
||||
* there is no limit
|
||||
* @param queryTimeout seconds the new query timeout limit in seconds; zero
|
||||
* means there is no limit
|
||||
* @see Statement#setQueryTimeout(int)
|
||||
*/
|
||||
public void setQueryTimeout(int queryTimeout) {
|
||||
@@ -521,8 +494,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* Set whether SQLWarnings should be ignored (only logged) or exception
|
||||
* should be thrown.
|
||||
*
|
||||
* @param ignoreWarnings
|
||||
* if TRUE, warnings are ignored
|
||||
* @param ignoreWarnings if TRUE, warnings are ignored
|
||||
*/
|
||||
public void setIgnoreWarnings(boolean ignoreWarnings) {
|
||||
this.ignoreWarnings = ignoreWarnings;
|
||||
@@ -532,8 +504,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
* Allow verification of cursor position after current row is processed by
|
||||
* RowMapper or RowCallbackHandler. Default value is TRUE.
|
||||
*
|
||||
* @param verifyCursorPosition
|
||||
* if true, cursor position is verified
|
||||
* @param verifyCursorPosition if true, cursor position is verified
|
||||
*/
|
||||
public void setVerifyCursorPosition(boolean verifyCursorPosition) {
|
||||
this.verifyCursorPosition = verifyCursorPosition;
|
||||
@@ -565,7 +536,7 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the item itself (which is already a key).
|
||||
* @see org.springframework.batch.item.ItemReader#getKey(java.lang.Object)
|
||||
@@ -573,5 +544,5 @@ public class JdbcCursorItemReader extends AbstractTransactionalIoSource
|
||||
public Object getKey(Object item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -37,14 +37,14 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
public class ColumnMapStreamContextRowMapper extends ColumnMapRowMapper implements RestartDataRowMapper{
|
||||
|
||||
static final String KEY = ClassUtils.getQualifiedName(ColumnMapStreamContextRowMapper.class) + ".KEY.";
|
||||
public static final String KEY_PREFIX = ClassUtils.getQualifiedName(ColumnMapStreamContextRowMapper.class) + ".KEY.";
|
||||
|
||||
public PreparedStatementSetter createSetter(StreamContext streamContext) {
|
||||
|
||||
ColumnMapRestartData columnData = new ColumnMapRestartData(streamContext.getProperties());
|
||||
|
||||
List columns = new ArrayList();
|
||||
for (Iterator iterator = columnData.entrySet().iterator(); iterator.hasNext();) {
|
||||
for (Iterator iterator = columnData.keys.entrySet().iterator(); iterator.hasNext();) {
|
||||
Entry entry = (Entry)iterator.next();
|
||||
Object column = entry.getValue();
|
||||
columns.add(column);
|
||||
@@ -53,27 +53,51 @@ public class ColumnMapStreamContextRowMapper extends ColumnMapRowMapper implemen
|
||||
return new ArgPreparedStatementSetter(columns.toArray());
|
||||
}
|
||||
|
||||
public StreamContext createRestartData(Object key) {
|
||||
|
||||
Assert.isInstanceOf(Map.class, key, "Key must be of type Map.");
|
||||
public StreamContext createStreamContext(Object key) {
|
||||
Assert.isInstanceOf(Map.class, key, "Input to create StreamContext must be of type Map.");
|
||||
Map keys = (Map)key;
|
||||
|
||||
return new ColumnMapRestartData(keys);
|
||||
}
|
||||
|
||||
|
||||
private static class ColumnMapRestartData extends GenericStreamContext{
|
||||
private static class ColumnMapRestartData extends GenericStreamContext {
|
||||
|
||||
private final Map keys;
|
||||
|
||||
public ColumnMapRestartData(Map keys) {
|
||||
super();
|
||||
for(Iterator it = keys.entrySet().iterator();it.hasNext();){
|
||||
Entry entry = (Entry)it.next();
|
||||
putString(entry.getKey().toString(), entry.getValue().toString());
|
||||
}
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public ColumnMapRestartData(Properties props) {
|
||||
super(props);
|
||||
|
||||
keys = CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(props.size());
|
||||
|
||||
for(int counter = 0; counter < props.size(); counter++){
|
||||
|
||||
String key = KEY_PREFIX + counter;
|
||||
String column = props.getProperty(key);
|
||||
|
||||
if(column != null){
|
||||
keys.put(key, column);
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
Properties props = new Properties();
|
||||
|
||||
int counter = 0;
|
||||
for (Iterator iterator = keys.entrySet().iterator(); iterator.hasNext();) {
|
||||
Entry entry = (Entry) iterator.next();
|
||||
props.setProperty(KEY_PREFIX + counter, entry.getValue().toString());
|
||||
counter++;
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class MultipleColumnJdbcKeyGenerator implements
|
||||
*/
|
||||
public StreamContext getKeyAsStreamContext(Object key) {
|
||||
Assert.state(keyMapper != null, "RestartDataConverter must not be null.");
|
||||
return keyMapper.createRestartData(key);
|
||||
return keyMapper.createStreamContext(key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,7 @@ public interface RestartDataRowMapper extends RowMapper {
|
||||
* @return ResartData representing the composite key.
|
||||
* @throws IllegalArgumentException if key is null or of an unsupported type.
|
||||
*/
|
||||
public StreamContext createRestartData(Object key);
|
||||
public StreamContext createStreamContext(Object key);
|
||||
|
||||
/**
|
||||
* Given the provided restart data, return a PreparedStatementSeter that can
|
||||
|
||||
@@ -26,9 +26,9 @@ import org.springframework.batch.io.Skippable;
|
||||
import org.springframework.batch.io.file.separator.LineReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.StreamContext;
|
||||
import org.springframework.batch.item.StreamException;
|
||||
import org.springframework.batch.item.stream.GenericStreamContext;
|
||||
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
|
||||
import org.springframework.batch.statistics.StatisticsProvider;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
|
||||
@@ -42,8 +42,7 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter
|
||||
* @author Tomas Slanina
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class DefaultFlatFileItemReader extends SimpleFlatFileItemReader implements Skippable, ItemStream,
|
||||
StatisticsProvider {
|
||||
public class DefaultFlatFileItemReader extends SimpleFlatFileItemReader implements Skippable, ItemStream {
|
||||
|
||||
private static Log log = LogFactory.getLog(DefaultFlatFileItemReader.class);
|
||||
|
||||
@@ -113,8 +112,10 @@ public class DefaultFlatFileItemReader extends SimpleFlatFileItemReader implemen
|
||||
* @see org.springframework.batch.statistics.StatisticsProvider#getStatistics()
|
||||
*/
|
||||
public Properties getStatistics() {
|
||||
LineReader is = getReader();
|
||||
statistics.setProperty(READ_STATISTICS_NAME, String.valueOf(is.getCurrentLineCount()));
|
||||
if (reader==null) {
|
||||
throw new StreamException("ItemStream not open or already closed.");
|
||||
}
|
||||
statistics.setProperty(READ_STATISTICS_NAME, String.valueOf(reader.getCurrentLineCount()));
|
||||
return statistics;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.StreamContext;
|
||||
import org.springframework.batch.item.stream.GenericStreamContext;
|
||||
import org.springframework.batch.item.writer.ItemTransformer;
|
||||
import org.springframework.batch.statistics.StatisticsProvider;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -59,9 +58,8 @@ import org.springframework.util.Assert;
|
||||
* @author Robert Kasanicky
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
|
||||
ItemWriter, ItemStream, StatisticsProvider, InitializingBean,
|
||||
DisposableBean {
|
||||
public class FlatFileItemWriter extends AbstractTransactionalIoSource implements ItemWriter, ItemStream,
|
||||
InitializingBean, DisposableBean {
|
||||
|
||||
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
|
||||
@@ -88,7 +86,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
|
||||
private static class BooleanHolder {
|
||||
public boolean value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assert that mandatory properties (resource) are set.
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
@@ -119,7 +117,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the transaction.
|
||||
* Commit the transaction.
|
||||
*/
|
||||
protected void transactionCommitted() {
|
||||
getOutputState().mark();
|
||||
@@ -159,7 +157,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
|
||||
* Convert the date to a format that can be output and then write it out.
|
||||
* @param data
|
||||
* @param converted
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
private void transformAndWrite(Object data, BooleanHolder converted) throws Exception {
|
||||
|
||||
@@ -262,7 +260,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
|
||||
public StreamContext getStreamContext() {
|
||||
final OutputState os = getOutputState();
|
||||
|
||||
streamContext.getProperties().setProperty(RESTART_DATA_NAME, String.valueOf(os.position()));
|
||||
streamContext.putString(RESTART_DATA_NAME, String.valueOf(os.position()));
|
||||
return streamContext;
|
||||
}
|
||||
|
||||
@@ -453,7 +451,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements
|
||||
}
|
||||
}
|
||||
String parent = file.getParent();
|
||||
if (parent!=null) {
|
||||
if (parent != null) {
|
||||
new File(parent).mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
|
||||
@@ -68,7 +68,7 @@ public class SimpleFlatFileItemReader extends AbstractItemReader implements Item
|
||||
* Encapsulates the state of the input source. If it is null then we are
|
||||
* uninitialized.
|
||||
*/
|
||||
private LineReader reader;
|
||||
protected LineReader reader;
|
||||
|
||||
private RecordSeparatorPolicy recordSeparatorPolicy;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.springframework.batch.item.StreamContext;
|
||||
import org.springframework.batch.item.reader.AbstractItemReader;
|
||||
import org.springframework.batch.item.stream.GenericStreamContext;
|
||||
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
|
||||
import org.springframework.batch.statistics.StatisticsProvider;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -43,7 +42,7 @@ import org.springframework.util.Assert;
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class StaxEventItemReader extends AbstractItemReader implements ItemReader,
|
||||
Skippable, ItemStream, StatisticsProvider, InitializingBean, DisposableBean {
|
||||
Skippable, ItemStream, InitializingBean, DisposableBean {
|
||||
|
||||
public static final String READ_COUNT_STATISTICS_NAME = "StaxEventReaderItemReader.readCount";
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.StreamContext;
|
||||
import org.springframework.batch.item.stream.GenericStreamContext;
|
||||
import org.springframework.batch.repeat.synch.BatchTransactionSynchronizationManager;
|
||||
import org.springframework.batch.statistics.StatisticsProvider;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -32,17 +31,16 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ItemWriter} which uses
|
||||
* StAX and {@link EventWriterSerializer} for serializing object to XML.
|
||||
*
|
||||
* This output source also provides restart, statistics and transaction
|
||||
* features by implementing corresponding interfaces.
|
||||
*
|
||||
* An implementation of {@link ItemWriter} which uses StAX and
|
||||
* {@link EventWriterSerializer} for serializing object to XML.
|
||||
*
|
||||
* This output source also provides restart, statistics and transaction features
|
||||
* by implementing corresponding interfaces.
|
||||
*
|
||||
* @author Peter Zozom
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
StatisticsProvider, InitializingBean, DisposableBean {
|
||||
public class StaxEventItemWriter implements ItemWriter, ItemStream, InitializingBean, DisposableBean {
|
||||
|
||||
// default encoding
|
||||
private static final String DEFAULT_ENCODING = "UTF-8";
|
||||
@@ -83,13 +81,15 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
// signalizes that marshalling was restarted
|
||||
private boolean restarted = false;
|
||||
|
||||
// TRUE means, that output file will be overwritten if exists - default is TRUE
|
||||
// TRUE means, that output file will be overwritten if exists - default is
|
||||
// TRUE
|
||||
private boolean overwriteOutput = true;
|
||||
|
||||
// file channel
|
||||
private FileChannel channel;
|
||||
|
||||
// wrapper for XML event writer that swallows StartDocument and EndDocument events
|
||||
// wrapper for XML event writer that swallows StartDocument and EndDocument
|
||||
// events
|
||||
private XMLEventWriter eventWriter;
|
||||
|
||||
// XML event writer
|
||||
@@ -107,10 +107,9 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
// current count of processed records
|
||||
private long currentRecordCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Set output file.
|
||||
*
|
||||
*
|
||||
* @param resource the output file
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
@@ -119,7 +118,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Set Object to XML serializer.
|
||||
*
|
||||
*
|
||||
* @param serializer the Object to XML serializer
|
||||
*/
|
||||
public void setSerializer(EventWriterSerializer serializer) {
|
||||
@@ -128,7 +127,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Get used encoding.
|
||||
*
|
||||
*
|
||||
* @return the encoding used
|
||||
*/
|
||||
public String getEncoding() {
|
||||
@@ -137,7 +136,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Set encoding to be used for output file.
|
||||
*
|
||||
*
|
||||
* @param encoding the encoding to be used
|
||||
*/
|
||||
public void setEncoding(String encoding) {
|
||||
@@ -146,7 +145,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Get XML version.
|
||||
*
|
||||
*
|
||||
* @return the XML version used
|
||||
*/
|
||||
public String getVersion() {
|
||||
@@ -155,7 +154,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Set XML version to be used for output XML.
|
||||
*
|
||||
*
|
||||
* @param version the XML version to be used
|
||||
*/
|
||||
public void setVersion(String version) {
|
||||
@@ -164,7 +163,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Get the tag name of the root element.
|
||||
*
|
||||
*
|
||||
* @return the root element tag name
|
||||
*/
|
||||
public String getRootTagName() {
|
||||
@@ -172,8 +171,9 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tag name of the root element. If not set, default name is used ("root").
|
||||
*
|
||||
* Set the tag name of the root element. If not set, default name is used
|
||||
* ("root").
|
||||
*
|
||||
* @param rootTagName the tag name to be used for the root element
|
||||
*/
|
||||
public void setRootTagName(String rootTagName) {
|
||||
@@ -182,7 +182,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Get attributes of the root element.
|
||||
*
|
||||
*
|
||||
* @return attributes of the root element
|
||||
*/
|
||||
public Map getRootElementAttributes() {
|
||||
@@ -191,7 +191,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Set the root element attributes to be written.
|
||||
*
|
||||
*
|
||||
* @param rootElementAttributes attributes of the root element
|
||||
*/
|
||||
public void setRootElementAttributes(Map rootElementAttributes) {
|
||||
@@ -199,8 +199,9 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
}
|
||||
|
||||
/**
|
||||
* Set "overwrite" flag for the output file. Flag is ignored when output file processing is restarted.
|
||||
*
|
||||
* Set "overwrite" flag for the output file. Flag is ignored when output
|
||||
* file processing is restarted.
|
||||
*
|
||||
* @param shouldDeleteIfExists
|
||||
*/
|
||||
public void setOverwriteOutput(boolean overwriteOutput) {
|
||||
@@ -237,7 +238,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/**
|
||||
* Open the output source
|
||||
*
|
||||
*
|
||||
* @see org.springframework.batch.item.ResourceLifecycle#open()
|
||||
*/
|
||||
public void open() {
|
||||
@@ -253,16 +254,16 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
File file;
|
||||
FileOutputStream os = null;
|
||||
|
||||
|
||||
try {
|
||||
file = resource.getFile();
|
||||
FileUtils.setUpOutputFile(file, restarted, overwriteOutput);
|
||||
os = new FileOutputStream(file, true);
|
||||
channel = os.getChannel();
|
||||
setPosition(position);
|
||||
} catch (IOException ioe) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to write to file resource: [" + resource + "]", ioe);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", ioe);
|
||||
}
|
||||
|
||||
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
|
||||
@@ -273,9 +274,9 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
if (!restarted) {
|
||||
startDocument(delegateEventWriter);
|
||||
}
|
||||
} catch (XMLStreamException xse) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to write to file resource: [" + resource + "]", xse);
|
||||
}
|
||||
catch (XMLStreamException xse) {
|
||||
throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", xse);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
@@ -289,27 +290,26 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
* </ul>
|
||||
* If this is not sufficient for you, simply override this method. Encoding,
|
||||
* version and root tag name can be retrieved with corresponding getters.
|
||||
*
|
||||
* @param writer
|
||||
* XML event writer
|
||||
*
|
||||
* @param writer XML event writer
|
||||
* @throws XMLStreamException
|
||||
*/
|
||||
protected void startDocument(XMLEventWriter writer) throws XMLStreamException {
|
||||
|
||||
XMLEventFactory factory = XMLEventFactory.newInstance();
|
||||
|
||||
//write start document
|
||||
// write start document
|
||||
writer.add(factory.createStartDocument(getEncoding(), getVersion()));
|
||||
|
||||
//write root tag
|
||||
// write root tag
|
||||
writer.add(factory.createStartElement("", "", getRootTagName()));
|
||||
|
||||
//write root tag attributes
|
||||
// write root tag attributes
|
||||
if (!CollectionUtils.isEmpty(getRootElementAttributes())) {
|
||||
|
||||
for (Iterator i = getRootElementAttributes().entrySet().iterator(); i.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry)i.next();
|
||||
writer.add(factory.createAttribute((String)entry.getKey(), (String)entry.getValue()));
|
||||
Map.Entry entry = (Map.Entry) i.next();
|
||||
writer.add(factory.createAttribute((String) entry.getKey(), (String) entry.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -319,29 +319,27 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
/**
|
||||
* Finishes the XML document. It closes any start tag and writes
|
||||
* corresponding end tags.
|
||||
*
|
||||
* @param writer
|
||||
* XML event writer
|
||||
*
|
||||
* @param writer XML event writer
|
||||
* @throws XMLStreamException
|
||||
*/
|
||||
protected void endDocument(XMLEventWriter writer)
|
||||
throws XMLStreamException {
|
||||
protected void endDocument(XMLEventWriter writer) throws XMLStreamException {
|
||||
|
||||
//writer.writeEndDocument(); <- this doesn't work after restart
|
||||
//we need to write end tag of the root element manually
|
||||
// writer.writeEndDocument(); <- this doesn't work after restart
|
||||
// we need to write end tag of the root element manually
|
||||
writer.flush();
|
||||
ByteBuffer bbuf = ByteBuffer.wrap(("</" + getRootTagName() + ">").getBytes());
|
||||
try {
|
||||
getChannel().write(bbuf);
|
||||
} catch (IOException ioe) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to close file resource: [" + resource + "]", ioe);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new DataAccessResourceFailureException("Unable to close file resource: [" + resource + "]", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the output source.
|
||||
*
|
||||
*
|
||||
* @see org.springframework.batch.item.ResourceLifecycle#close()
|
||||
*/
|
||||
public void close() {
|
||||
@@ -350,19 +348,18 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
endDocument(delegateEventWriter);
|
||||
eventWriter.close();
|
||||
channel.close();
|
||||
} catch (XMLStreamException xse) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to close file resource: [" + resource + "]", xse);
|
||||
}
|
||||
catch (XMLStreamException xse) {
|
||||
throw new DataAccessResourceFailureException("Unable to close file resource: [" + resource + "]", xse);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to close file resource: [" + resource + "]", ioe);
|
||||
throw new DataAccessResourceFailureException("Unable to close file resource: [" + resource + "]", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the value object to XML stream.
|
||||
*
|
||||
*
|
||||
* @param output the value object
|
||||
* @see org.springframework.batch.item.ItemWriter#write(java.lang.Object)
|
||||
*/
|
||||
@@ -399,12 +396,10 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
long startAtPosition = 0;
|
||||
|
||||
//if restart data is provided, restart from provided offset
|
||||
//otherwise start from beginning
|
||||
if (data != null && data.getProperties() != null
|
||||
&& data.getProperties().getProperty(RESTART_DATA_NAME) != null) {
|
||||
startAtPosition = Long.parseLong(data.getProperties().getProperty(
|
||||
RESTART_DATA_NAME));
|
||||
// if restart data is provided, restart from provided offset
|
||||
// otherwise start from beginning
|
||||
if (data != null && data.getProperties() != null && data.getProperties().getProperty(RESTART_DATA_NAME) != null) {
|
||||
startAtPosition = Long.parseLong(data.getProperties().getProperty(RESTART_DATA_NAME));
|
||||
restarted = true;
|
||||
}
|
||||
|
||||
@@ -426,9 +421,9 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the actual position in file channel.
|
||||
* This method flushes any buffered data before position is read.
|
||||
*
|
||||
* Get the actual position in file channel. This method flushes any buffered
|
||||
* data before position is read.
|
||||
*
|
||||
* @return byte offset in file channel
|
||||
*/
|
||||
private long getPosition() {
|
||||
@@ -438,9 +433,9 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
try {
|
||||
eventWriter.flush();
|
||||
position = channel.position();
|
||||
} catch (Exception e) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to write to file resource: [" + resource + "]", e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", e);
|
||||
}
|
||||
|
||||
return position;
|
||||
@@ -448,7 +443,7 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
|
||||
/*
|
||||
* Set the file channel position.
|
||||
*
|
||||
*
|
||||
* @param newPosition new file channel position
|
||||
*/
|
||||
private void setPosition(long newPosition) {
|
||||
@@ -458,24 +453,23 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
"Current file size is smaller than size at last commit");
|
||||
channel.truncate(newPosition);
|
||||
channel.position(newPosition);
|
||||
} catch (IOException e) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to write to file resource: [" + resource + "]", e);
|
||||
}
|
||||
|
||||
catch (IOException e) {
|
||||
throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates transaction events for the StaxEventWriterOutputSource.
|
||||
*/
|
||||
private class StaxEventWriterItemWriterTransactionSychronization extends
|
||||
TransactionSynchronizationAdapter {
|
||||
private class StaxEventWriterItemWriterTransactionSychronization extends TransactionSynchronizationAdapter {
|
||||
|
||||
public void afterCompletion(int status) {
|
||||
if (status == TransactionSynchronization.STATUS_COMMITTED) {
|
||||
transactionComitted();
|
||||
} else if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
|
||||
}
|
||||
else if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
|
||||
transactionRolledback();
|
||||
}
|
||||
}
|
||||
@@ -488,10 +482,11 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
private void transactionRolledback() {
|
||||
currentRecordCount = lastCommitPointRecordCount;
|
||||
|
||||
//close output
|
||||
// close output
|
||||
close();
|
||||
//and reopen it - we do this because we need to reopen stream
|
||||
//reader at specified position - calling setPosition() is not enough!
|
||||
// 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);
|
||||
}
|
||||
@@ -502,5 +497,4 @@ public class StaxEventItemWriter implements ItemWriter, ItemStream,
|
||||
return synchronization;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ package org.springframework.batch.item;
|
||||
* The state that is stored is represented as {@link StreamContext} which
|
||||
* enforces a requirement that any restart data can be represented by a
|
||||
* Properties object. In general, the contract is that {@link StreamContext}
|
||||
* that is returned via the {@link #getStreamContext()} method will be given back
|
||||
* to the {@link #restoreFrom(StreamContext)} method, exactly as it was
|
||||
* that is returned via the {@link #getStreamContext()} method will be given
|
||||
* back to the {@link #restoreFrom(StreamContext)} method, exactly as it was
|
||||
* provided.
|
||||
* </p>
|
||||
*
|
||||
@@ -51,7 +51,8 @@ public interface ItemStream extends StreamContextProvider {
|
||||
|
||||
/**
|
||||
* If any resources are needed for the stream to operate they need to be
|
||||
* destroyed here.
|
||||
* destroyed here. Once this method has been called all other methods
|
||||
* (except open) may throw an exception.
|
||||
*/
|
||||
void close() throws StreamException;
|
||||
}
|
||||
|
||||
@@ -24,102 +24,104 @@ import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Value object representing a context for an {@link ItemStream}. It is
|
||||
* essentially a thin wrapper for a map that allows for type safety
|
||||
* on reads. It also allows for dirty checking by setting a 'dirty'
|
||||
* flag whenever any put is called.
|
||||
* Value object representing a context for an {@link ItemStream}. It is
|
||||
* essentially a thin wrapper for a map that allows for type safety on reads. It
|
||||
* also allows for dirty checking by setting a 'dirty' flag whenever any put is
|
||||
* called.
|
||||
*/
|
||||
public class StreamContext {
|
||||
|
||||
private boolean dirty = false;
|
||||
|
||||
private final Map map;
|
||||
|
||||
public StreamContext(){
|
||||
|
||||
public StreamContext() {
|
||||
map = new HashMap();
|
||||
}
|
||||
|
||||
public void putString(String key, String value){
|
||||
|
||||
|
||||
public void putString(String key, String value) {
|
||||
|
||||
Assert.notNull(value);
|
||||
put(key, value);
|
||||
}
|
||||
|
||||
public void putLong(String key, long value){
|
||||
|
||||
|
||||
public void putLong(String key, long value) {
|
||||
|
||||
put(key, new Long(value));
|
||||
}
|
||||
|
||||
public void putDouble(String key, double value){
|
||||
|
||||
|
||||
public void putDouble(String key, double value) {
|
||||
|
||||
put(key, new Double(value));
|
||||
}
|
||||
|
||||
public void put(String key, Object value){
|
||||
|
||||
public void put(String key, Object value) {
|
||||
dirty = true;
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
public String getString(String key){
|
||||
|
||||
return (String)readAndValidate(key, String.class);
|
||||
|
||||
public String getString(String key) {
|
||||
|
||||
return (String) readAndValidate(key, String.class);
|
||||
}
|
||||
|
||||
public long getLong(String key){
|
||||
|
||||
return ((Long)readAndValidate(key, Long.class)).longValue();
|
||||
|
||||
public long getLong(String key) {
|
||||
|
||||
return ((Long) readAndValidate(key, Long.class)).longValue();
|
||||
}
|
||||
|
||||
public Object get(String key){
|
||||
|
||||
|
||||
public Object get(String key) {
|
||||
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
private Object readAndValidate(String key, Class type){
|
||||
|
||||
|
||||
private Object readAndValidate(String key, Class type) {
|
||||
|
||||
Object value = map.get(key);
|
||||
|
||||
if(!type.isInstance(key)){
|
||||
throw new ClassCastException("Value is not of type: [" + type + "]");
|
||||
}
|
||||
|
||||
|
||||
// if (!type.isInstance(key)) {
|
||||
// throw new ClassCastException("Value for key=[" + key + "] is not of type: [" + ClassUtils.getShortName(type)
|
||||
// + "], it is [" + (value == null ? null : ClassUtils.getShortName(value.getClass())) + "]");
|
||||
// }
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
public void clearDirtyFlag(){
|
||||
|
||||
public void clearDirtyFlag() {
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public Set entrySet(){
|
||||
|
||||
public Set entrySet() {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key){
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value){
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
public Properties getProperties(){
|
||||
|
||||
|
||||
public Properties getProperties() {
|
||||
|
||||
Properties props = new Properties();
|
||||
for(Iterator it = map.entrySet().iterator();it.hasNext();){
|
||||
Entry entry = (Entry)it.next();
|
||||
for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
|
||||
Entry entry = (Entry) it.next();
|
||||
props.setProperty(entry.getKey().toString(), entry.getValue().toString());
|
||||
}
|
||||
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,11 @@ import org.springframework.batch.io.exception.BatchCriticalException;
|
||||
*/
|
||||
public class StreamException extends BatchCriticalException {
|
||||
|
||||
/**
|
||||
* @param message
|
||||
*/
|
||||
public StreamException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ public class GenericStreamContext extends StreamContext {
|
||||
super();
|
||||
}
|
||||
|
||||
public GenericStreamContext(Properties data){
|
||||
public GenericStreamContext(Properties data) {
|
||||
super();
|
||||
for(Iterator it = data.entrySet().iterator();it.hasNext();){
|
||||
Entry entry = (Entry)it.next();
|
||||
putString(entry.getKey().toString(), entry.getValue().toString());
|
||||
if (data != null) {
|
||||
for (Iterator it = data.entrySet().iterator(); it.hasNext();) {
|
||||
Entry entry = (Entry) it.next();
|
||||
putString(entry.getKey().toString(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* 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.statistics;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simple {@link StatisticsService} that makes no attempt to aggregate or
|
||||
* resolve conflicts between key names. All the contributions registered are
|
||||
* simply polled and added "as is" to the aggregate properties.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SimpleStatisticsService implements StatisticsService {
|
||||
|
||||
private Map registry = new HashMap();
|
||||
|
||||
/**
|
||||
* Simple aggregate statistics provider for the contributions registered
|
||||
* under the given key.
|
||||
*
|
||||
* @see org.springframework.batch.statistics.StatisticsService#getStatistics(java.lang.Object)
|
||||
*/
|
||||
public Properties getStatistics(Object key) {
|
||||
Set set = new LinkedHashSet();
|
||||
synchronized (registry) {
|
||||
Collection collection = (Collection) registry.get(key);
|
||||
if (collection != null) {
|
||||
set = new LinkedHashSet(collection);
|
||||
}
|
||||
}
|
||||
return aggregate(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list a list of {@link StatisticsProvider}s
|
||||
* @return aggregated statistics
|
||||
*/
|
||||
private Properties aggregate(Collection list) {
|
||||
Properties result = new Properties();
|
||||
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
|
||||
StatisticsProvider provider = (StatisticsProvider) iterator.next();
|
||||
Properties properties = provider.getStatistics();
|
||||
if (properties != null) {
|
||||
result.putAll(properties);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link StatisticsProvider} as one of the interesting providers
|
||||
* under the provided key.
|
||||
*
|
||||
* @see org.springframework.batch.statistics.StatisticsService#register(java.lang.Object,
|
||||
* org.springframework.batch.statistics.StatisticsProvider)
|
||||
*/
|
||||
public void register(Object key, StatisticsProvider provider) {
|
||||
synchronized (registry) {
|
||||
Set set = (Set) registry.get(key);
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet();
|
||||
registry.put(key, set);
|
||||
}
|
||||
set.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.statistics;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Provides statistics for a given module run. Any class that implements
|
||||
* this interface is guaranteeing that it will provide Statistics.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public interface StatisticsProvider {
|
||||
|
||||
Properties getStatistics();
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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.statistics;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Generalised statistics aggregation strategy. Clients register
|
||||
* {@link StatisticsProvider} instances under a well-known key, and then when
|
||||
* they ask for statistics by that key, they receive an aggregate of all the
|
||||
* values given by registered providers.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface StatisticsService {
|
||||
|
||||
/**
|
||||
* Register the {@link StatisticsProvider} instance as one of possibly
|
||||
* several that are associated with the given key.
|
||||
*
|
||||
* @param key the key under which to add the provider
|
||||
* @param provider a {@link StatisticsProvider}
|
||||
*/
|
||||
void register(Object key, StatisticsProvider provider);
|
||||
|
||||
/**
|
||||
* Extract and aggregate the statistics from all providers under this key.
|
||||
*
|
||||
* @param key the key under which {@link StatisticsProvider} instances might
|
||||
* have been registered.
|
||||
* @return {@link Properties} summarising the statistics of all providers
|
||||
* registered under this key, or empty otherwise.
|
||||
*/
|
||||
Properties getStatistics(Object key);
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Infrastructure implementations of statistics concerns.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user