BATCH-335 and BATCH-316: As part of the documentation efforts of RC1, the DrivingQueryItemReader has had javadoc updates. Specifically, the MultipleColumnJdbcKeyGenerator, which has been a source of confusion, has had it's javadocs beefed up.
The KeyGenerator interface was also renamed to KeyCollector. The issue was raised that it's not actually generating keys, but rather collecting them from the database.
This commit is contained in:
@@ -28,10 +28,26 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* <p>
|
||||
* Convenience class for driving query input sources. Input Sources of this type
|
||||
* use a 'driving query' to return back a list of keys. Upon each call to read,
|
||||
* a new key is returned. To use the input source, inject a key generation
|
||||
* strategy.
|
||||
* </p>
|
||||
* use a 'driving query' to return back a list of keys. A key can be defined
|
||||
* as anything that can uniquely identify a record so that a more detailed
|
||||
* record can be retrieved for each object. This allows a much smaller footprint
|
||||
* to be stored in memory for processing. The following 'Customer' example table
|
||||
* will help illustrate this:
|
||||
*
|
||||
* <pre>
|
||||
* CREATE TABLE CUSTOMER (
|
||||
* ID BIGINT IDENTITY PRIMARY KEY,
|
||||
* NAME VARCHAR(45),
|
||||
* CREDIT FLOAT
|
||||
* );
|
||||
* </pre>
|
||||
*
|
||||
* <p>A cursor based solution would simply open up a cursor over ID, NAME, and CREDIT,
|
||||
* and move it from one to the next. This can cause issues on databases with
|
||||
* pessimistic locking strategies. A 'driving query' approach would be to
|
||||
* return only the ID of the customer, then use a separate DAO to retrieve the
|
||||
* name and credit for each ID. This means that there will be a call to a
|
||||
* separate DAO for each call to {@link ItemReader#read()}.</p>
|
||||
*
|
||||
* <p>
|
||||
* Mutability: Because this base class cannot guarantee that the keys returned
|
||||
@@ -45,7 +61,6 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 1.0
|
||||
*/
|
||||
public class DrivingQueryItemReader implements ItemReader, InitializingBean,
|
||||
ItemStream {
|
||||
@@ -60,7 +75,7 @@ public class DrivingQueryItemReader implements ItemReader, InitializingBean,
|
||||
|
||||
private int lastCommitIndex = 0;
|
||||
|
||||
private KeyGenerator keyGenerator;
|
||||
private KeyCollector keyGenerator;
|
||||
|
||||
private boolean saveState = false;
|
||||
|
||||
@@ -165,18 +180,10 @@ public class DrivingQueryItemReader implements ItemReader, InitializingBean,
|
||||
*
|
||||
* @param keyGenerator
|
||||
*/
|
||||
public void setKeyGenerator(KeyGenerator keyGenerator) {
|
||||
public void setKeyCollector(KeyCollector keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
protected void transactionCommitted() {
|
||||
mark();
|
||||
}
|
||||
|
||||
protected void transactionRolledBack() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark is supported as long as this {@link ItemStream} is used in a
|
||||
* single-threaded environment. The state backing the mark is a single
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import org.springframework.batch.item.database.support.IbatisKeyGenerator;
|
||||
import org.springframework.batch.item.database.support.IbatisKeyCollector;
|
||||
import org.springframework.orm.ibatis.SqlMapClientTemplate;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
@@ -25,7 +25,7 @@ import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
* objects. An iBatis query id must be set to map and return each 'detail record'.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @see IbatisKeyGenerator
|
||||
* @see IbatisKeyCollector
|
||||
*/
|
||||
public class IbatisDrivingQueryItemReader extends DrivingQueryItemReader {
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Strategy interface used to collect keys in driving query input.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @see DrivingQueryItemReader
|
||||
*/
|
||||
public interface KeyCollector {
|
||||
|
||||
/**
|
||||
* <p>Retrieve the keys to be iterated over. If the ExecutionContext
|
||||
* provided includes any state relevant to this collector (because it was
|
||||
* stored as part of saveState()) then it should start from the point
|
||||
* indicated by that state.</p>
|
||||
*
|
||||
* <p>In the case of a restart, (i.e. the ExecutionContext contains relevant state)
|
||||
* this method should return only the keys that are remaining to be processed. For
|
||||
* example, if the are 1,000 keys, and the 500th is processed before the batch job
|
||||
* terminates unexpectedly, upon restart keys 501 through 1,000 should be returned.
|
||||
* </p>
|
||||
*
|
||||
* @param executionContext ExecutionContext containing any potential initial state
|
||||
* that could potentially be used to retrieve the correct keys.
|
||||
* @return list of keys returned by the driving query
|
||||
*/
|
||||
List retrieveKeys(ExecutionContext executionContext);
|
||||
|
||||
/**
|
||||
* Given the provided key, store it in the provided ExecutionContext. This
|
||||
* is necessary because retrieveKeys() will be called with the ExecutionContext
|
||||
* that is provided as a result of this method. Since only the KeyCollector
|
||||
* can know what format the ExecutionContext should be in, it should also
|
||||
* save the state, as opposed to the {@link DrivingQueryItemReader} doing it
|
||||
* for all KeyCollector implementations.
|
||||
*
|
||||
* @param key to be converted to restart data.
|
||||
* @return {@link ExecutionContext} representation of the key.
|
||||
* @throws IllegalArgumentException if key is null.
|
||||
* @throws IllegalArgumentException if key is an incompatible type.
|
||||
*/
|
||||
void saveState(Object key, ExecutionContext executionContext);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.springframework.batch.item.database;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Strategy interface used to generate keys in driving query input.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface KeyGenerator {
|
||||
|
||||
/**
|
||||
* @param executionContext TODO
|
||||
* @return list of keys returned by the driving query
|
||||
*/
|
||||
List retrieveKeys(ExecutionContext executionContext);
|
||||
|
||||
/**
|
||||
* Return the provided key as restart data.
|
||||
*
|
||||
* @param key to be converted to restart data.
|
||||
* @return {@link ExecutionContext} representation of the key.
|
||||
* @throws IllegalArgumentException if key is null.
|
||||
* @throws IllegalArgumentException if key is an incompatible type.
|
||||
*/
|
||||
void saveState(Object key, ExecutionContext executionContext);
|
||||
}
|
||||
@@ -21,16 +21,17 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* </p>Extension of the ColumnMapRowMapper that converts a column map to {@link ExecutionContext} and allows
|
||||
* </p>Extension of the {@link ColumnMapRowMapper} that converts a column map to {@link ExecutionContext} and allows
|
||||
* {@link ExecutionContext} to be converted back as a PreparedStatementSetter. This is useful in a restart
|
||||
* scenario, as it allows for the standard functionality of the ColumnMapRowMapper to be used to
|
||||
* create a map representing the columns returned by a query. It should be noted that this column ordering
|
||||
* is preserved in the map using a link list version of Map.
|
||||
* is preserved in the map using a link list version of Map.</p>
|
||||
*
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Dave Syer
|
||||
* @see ExecutionContextRowMapper
|
||||
* @see ColumnMapRowMapper
|
||||
*/
|
||||
public class ColumnMapExecutionContextRowMapper extends ColumnMapRowMapper implements ExecutionContextRowMapper {
|
||||
|
||||
|
||||
@@ -16,38 +16,67 @@
|
||||
package org.springframework.batch.item.database.support;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.database.DrivingQueryItemReader;
|
||||
import org.springframework.batch.item.database.KeyCollector;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* {@link ExecutionContextRowMapper} extends the standard {@link RowMapper} interface to provide for
|
||||
* converting an object returned from a RowMapper to {@link ExecutionContext} and back again. One
|
||||
* of the most common use cases for this type of functionality is the DrivingQuery approach
|
||||
* to sql processing. Using a {@link ExecutionContextRowMapper}, developers can create each unique key
|
||||
* to suite their specific needs, and also describe how such a key would be converted to
|
||||
* {@link ExecutionContext}, so that it can be serialized and stored.
|
||||
*
|
||||
* <p>
|
||||
* Extension of the standard {@link RowMapper} interface to provide for
|
||||
* converting an object returned from a RowMapper to {@link ExecutionContext}
|
||||
* and back again. This interface serves three purposes:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>Map the results of a driving query to an object, thus fulfilling the
|
||||
* normal {@link RowMapper} contract. Generally, this will be used to take
|
||||
* multiple columns and condense them into one 'key' that can then be used as
|
||||
* part of the {@link DrivingQueryItemReader}
|
||||
* <li>Map a key to an ExecutionContext. Because implementations of this
|
||||
* interface defined what a key is, they are the only ones that know how to
|
||||
* store it in an {@link ExecutionContext}.
|
||||
* <li>Create a {@link PreparedStatementSetter} so that, in the case of restart
|
||||
* a query can be created to return only the keys remaining to be processed.
|
||||
* </ul>
|
||||
*
|
||||
* The most common use case for this interface is the need to map multiple
|
||||
* columns to a key, as is necessary by the
|
||||
* {@link MultipleColumnJdbcKeyCollector}. In general, it's much simpler to use
|
||||
* one key as a column, and if the data itself allows this, then it should be
|
||||
* used. However, in certain cases there is no choice and multiple columns must
|
||||
* be used. Using a {@link ExecutionContextRowMapper}, developers can create
|
||||
* each unique key to suite their specific needs, and also describe how such a
|
||||
* key would be converted to {@link ExecutionContext}, so that it can be
|
||||
* serialized and stored.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @see RowMapper
|
||||
* @see MultipleColumnJdbcKeyCollector
|
||||
* @see KeyCollector
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ExecutionContextRowMapper extends RowMapper {
|
||||
|
||||
/**
|
||||
* Given the provided composite key, return a {@link ExecutionContext} representation.
|
||||
*
|
||||
* Given the provided composite key, return a {@link ExecutionContext}
|
||||
* representation.
|
||||
*
|
||||
* @param key
|
||||
* @return ExecutionContext representing the composite key.
|
||||
* @throws IllegalArgumentException if key is null or of an unsupported type.
|
||||
* @throws IllegalArgumentException
|
||||
* if key is null or of an unsupported type.
|
||||
*/
|
||||
public void mapKeys(Object key, ExecutionContext executionContext);
|
||||
|
||||
/**
|
||||
* Given the provided restart data, return a PreparedStatementSeter that can
|
||||
* be used as parameters to a JdbcTemplate.
|
||||
*
|
||||
*
|
||||
* @param executionContext
|
||||
* @return an array of objects that can be used as arguments to a JdbcTemplate.
|
||||
* @return an array of objects that can be used as arguments to a
|
||||
* JdbcTemplate.
|
||||
*/
|
||||
public PreparedStatementSetter createSetter(ExecutionContext executionContext);
|
||||
public PreparedStatementSetter createSetter(
|
||||
ExecutionContext executionContext);
|
||||
}
|
||||
|
||||
@@ -5,22 +5,24 @@ import java.util.List;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ExecutionContextUserSupport;
|
||||
import org.springframework.batch.item.database.DrivingQueryItemReader;
|
||||
import org.springframework.batch.item.database.KeyGenerator;
|
||||
import org.springframework.batch.item.database.KeyCollector;
|
||||
import org.springframework.orm.ibatis.SqlMapClientTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.ibatis.sqlmap.client.SqlMapClient;
|
||||
|
||||
/**
|
||||
* {@link KeyGenerator} based on iBATIS ORM framework. It is functionally
|
||||
* similar to {@link SingleColumnJdbcKeyGenerator} but does not make assumptions
|
||||
* about the primary key structure.
|
||||
* {@link KeyCollector} based on iBATIS ORM framework. It is functionally
|
||||
* similar to {@link SingleColumnJdbcKeyCollector} but does not make assumptions
|
||||
* about the primary key structure. A separate restart query is necessary to
|
||||
* ensure that only the required keys remaining for processing are returned, rather
|
||||
* than the entire original list.</p>
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
* @author Lucas Ward
|
||||
* @see DrivingQueryItemReader
|
||||
*/
|
||||
public class IbatisKeyGenerator extends ExecutionContextUserSupport implements KeyGenerator {
|
||||
public class IbatisKeyCollector extends ExecutionContextUserSupport implements KeyCollector {
|
||||
|
||||
private static final String RESTART_KEY = "key.index";
|
||||
|
||||
@@ -30,18 +32,18 @@ public class IbatisKeyGenerator extends ExecutionContextUserSupport implements K
|
||||
|
||||
private String restartQueryId;
|
||||
|
||||
public IbatisKeyGenerator() {
|
||||
setName(IbatisKeyGenerator.class.getSimpleName());
|
||||
public IbatisKeyCollector() {
|
||||
setName(IbatisKeyCollector.class.getSimpleName());
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the keys using the provided driving query id.
|
||||
*
|
||||
* @see org.springframework.batch.io.support.AbstractDrivingQueryItemReader#retrieveKeys()
|
||||
* @see KeyCollector#retrieveKeys()
|
||||
*/
|
||||
public List retrieveKeys(ExecutionContext executionContext) {
|
||||
if (executionContext.containsKey(getKey(RESTART_KEY))) {
|
||||
Object key = executionContext.getString(getKey(RESTART_KEY));
|
||||
Object key = executionContext.get(getKey(RESTART_KEY));
|
||||
return sqlMapClientTemplate.queryForList(restartQueryId, key);
|
||||
}
|
||||
else {
|
||||
@@ -51,12 +53,12 @@ public class IbatisKeyGenerator extends ExecutionContextUserSupport implements K
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.driving.KeyGenerator#getKeyAsExecutionContext(java.lang.Object)
|
||||
* @see KeyCollector#saveState(Object, ExecutionContext)
|
||||
*/
|
||||
public void saveState(Object key, ExecutionContext executionContext) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(executionContext, "ExecutionContext must be null");
|
||||
executionContext.putString(getKey(RESTART_KEY), key.toString());
|
||||
executionContext.put(getKey(RESTART_KEY), key);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -19,14 +19,14 @@ import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.database.DrivingQueryItemReader;
|
||||
import org.springframework.batch.item.database.KeyGenerator;
|
||||
import org.springframework.batch.item.database.KeyCollector;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Jdbc implementation of the {@link KeyGenerator} interface that works for
|
||||
* Jdbc implementation of the {@link KeyCollector} interface that works for
|
||||
* composite keys. (i.e. keys represented by multiple columns) A sql query to be
|
||||
* used to return the keys and a {@link ExecutionContextRowMapper} to map each
|
||||
* row in the resultset to an Object must be set in order to work correctly.
|
||||
@@ -34,9 +34,9 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @see DrivingQueryItemReader
|
||||
* @since 1.0
|
||||
* @see ExecutionContextRowMapper
|
||||
*/
|
||||
public class MultipleColumnJdbcKeyGenerator implements KeyGenerator {
|
||||
public class MultipleColumnJdbcKeyCollector implements KeyCollector {
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@@ -46,7 +46,7 @@ public class MultipleColumnJdbcKeyGenerator implements KeyGenerator {
|
||||
|
||||
private String restartSql;
|
||||
|
||||
public MultipleColumnJdbcKeyGenerator() {
|
||||
public MultipleColumnJdbcKeyCollector() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,11 +57,10 @@ public class MultipleColumnJdbcKeyGenerator implements KeyGenerator {
|
||||
* @param keyMapper - RowMapper that maps each row of the ResultSet to an
|
||||
* object.
|
||||
*/
|
||||
public MultipleColumnJdbcKeyGenerator(JdbcTemplate jdbcTemplate, String sql) {
|
||||
public MultipleColumnJdbcKeyCollector(JdbcTemplate jdbcTemplate, String sql) {
|
||||
this();
|
||||
Assert.notNull(jdbcTemplate, "The JdbcTemplate must not be null.");
|
||||
Assert.hasText(sql, "The DrivingQuery must not be null or empty.");
|
||||
Assert.notNull(keyMapper, "The key RowMapper must not be null.");
|
||||
Assert.hasText(sql, "The sql statement must not be null or empty.");
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.sql = sql;
|
||||
}
|
||||
@@ -90,6 +89,7 @@ public class MultipleColumnJdbcKeyGenerator implements KeyGenerator {
|
||||
*/
|
||||
public void saveState(Object key, ExecutionContext executionContext) {
|
||||
Assert.state(keyMapper != null, "Key mapper must not be null.");
|
||||
Assert.notNull(key, "The key must not be null");
|
||||
keyMapper.mapKeys(key, executionContext);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ExecutionContextUserSupport;
|
||||
import org.springframework.batch.item.database.KeyGenerator;
|
||||
import org.springframework.batch.item.database.KeyCollector;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SingleColumnRowMapper;
|
||||
@@ -28,7 +28,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Jdbc {@link KeyGenerator} implementation that only works for a single column
|
||||
* Jdbc {@link KeyCollector} implementation that only works for a single column
|
||||
* key. A sql query must be passed in which will be used to return a list of
|
||||
* keys. Each key will be mapped by a {@link RowMapper} that returns a mapped
|
||||
* key. By default, the {@link SingleColumnRowMapper} is used, and will convert
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.StringUtils;
|
||||
* only one column should be mapped to an object and returned as a key. If
|
||||
* multiple columns are returned as a key in this strategy, then restart will
|
||||
* not function properly. Instead a strategy that supports keys comprised of
|
||||
* multiple columns should be used.
|
||||
* multiple columns should be used.</p>
|
||||
*
|
||||
* <p>
|
||||
* Restartability: Because the key is only one column, restart is made much more
|
||||
@@ -48,9 +48,9 @@ import org.springframework.util.StringUtils;
|
||||
* </p>
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 1.0
|
||||
* @see SingleColumnRowMapper
|
||||
*/
|
||||
public class SingleColumnJdbcKeyGenerator extends ExecutionContextUserSupport implements KeyGenerator {
|
||||
public class SingleColumnJdbcKeyCollector extends ExecutionContextUserSupport implements KeyCollector {
|
||||
|
||||
private static final String RESTART_KEY = "key";
|
||||
|
||||
@@ -62,8 +62,8 @@ public class SingleColumnJdbcKeyGenerator extends ExecutionContextUserSupport im
|
||||
|
||||
private RowMapper keyMapper = new SingleColumnRowMapper();
|
||||
|
||||
public SingleColumnJdbcKeyGenerator() {
|
||||
setName(SingleColumnJdbcKeyGenerator.class.getSimpleName());
|
||||
public SingleColumnJdbcKeyCollector() {
|
||||
setName(SingleColumnJdbcKeyCollector.class.getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ public class SingleColumnJdbcKeyGenerator extends ExecutionContextUserSupport im
|
||||
* @throws IllegalArgumentException if jdbcTemplate is null.
|
||||
* @throws IllegalArgumentException if sql string is empty or null.
|
||||
*/
|
||||
public SingleColumnJdbcKeyGenerator(JdbcTemplate jdbcTemplate, String sql) {
|
||||
public SingleColumnJdbcKeyCollector(JdbcTemplate jdbcTemplate, String sql) {
|
||||
this();
|
||||
Assert.notNull(jdbcTemplate, "JdbcTemplate must not be null.");
|
||||
Assert.hasText(sql, "The sql statement must not be null or empty.");
|
||||
@@ -89,12 +89,12 @@ public class SingleColumnJdbcKeyGenerator extends ExecutionContextUserSupport im
|
||||
*/
|
||||
public List retrieveKeys(ExecutionContext executionContext) {
|
||||
|
||||
Assert.notNull(executionContext, "The restart data must not be null.");
|
||||
Assert.notNull(executionContext, "The ExecutionContext must not be null");
|
||||
|
||||
if (executionContext.containsKey(RESTART_KEY)) {
|
||||
Assert.state(StringUtils.hasText(restartSql), "The RestartQuery must not be null or empty"
|
||||
Assert.state(StringUtils.hasText(restartSql), "The restart sql query must not be null or empty"
|
||||
+ " in order to restart.");
|
||||
return jdbcTemplate.query(restartSql, new Object[] { executionContext.getString(RESTART_KEY) }, keyMapper);
|
||||
return jdbcTemplate.query(restartSql, new Object[] { executionContext.get(RESTART_KEY) }, keyMapper);
|
||||
}
|
||||
else{
|
||||
return jdbcTemplate.query(sql, keyMapper);
|
||||
@@ -104,12 +104,13 @@ public class SingleColumnJdbcKeyGenerator extends ExecutionContextUserSupport im
|
||||
/**
|
||||
* Get the restart data representing the last processed key.
|
||||
*
|
||||
* @see KeyGenerator#saveState(Object)
|
||||
* @see KeyCollector#saveState(Object)
|
||||
* @throws IllegalArgumentException if key is null.
|
||||
*/
|
||||
public void saveState(Object key, ExecutionContext executionContext) {
|
||||
Assert.notNull(key, "The key must not be null.");
|
||||
executionContext.putString(RESTART_KEY, key.toString());
|
||||
Assert.notNull(executionContext, "The ExecutionContext must not be null");
|
||||
executionContext.put(RESTART_KEY, key);
|
||||
}
|
||||
|
||||
/*
|
||||
Reference in New Issue
Block a user