Update namespace / 3.0 features in user guide

This commit is contained in:
dsyer
2010-01-18 08:28:36 +00:00
parent 228c54acfc
commit 863ea3b2ae
3 changed files with 123 additions and 45 deletions

View File

@@ -202,16 +202,21 @@
the PartitionStep is shown driving the execution. The PartitionStep
configuration looks like this:</para>
<para><programlisting>&lt;bean name="step1:master" class="org.sfw...PartitionStep"&gt;
&lt;property name="partitionHandler" ref="partitionHandler"/&gt;
&lt;property name="stepExecutionSplitter" ref="stepExecutionSplitter"/&gt;
&lt;property name="jobRepository" ref="jobRepository" /&gt;
&lt;/bean&gt;</programlisting></para>
<para><programlisting>&lt;step id="step1.master"&gt;
&lt;partition step="step1" partitioner="partitioner"&gt;
&lt;handler grid-size="10" task-executor="taskExecutor"/&gt;
&lt;/partition&gt;
&lt;/step&gt;</programlisting></para>
<para>There is a simple example which can be copied and extended in the
unit test suite for Spring Batch Core (see
<classname>org.springframework.batch.core.partition</classname>
package).</para>
unit test suite for Spring Batch Samples (see
<classname>*PartitionJob.xml</classname> configuration). </para>
<para>Spring Batch creates step executions for the partitions called
"step1:partition0", etc., so many people prefer to call the master step
"step1:master" for consistency. With Spring 3.0 you can do this using an
alias for the step (specifying the <literal>name</literal> attribute
instead of the <literal>id</literal>). </para>
<section id="partitionHandler">
<title>PartitionHandler</title>
@@ -240,10 +245,15 @@
separate threads of execution, using the
<classname>TaskExecutor</classname> strategy from Spring. The
implementation is called
<classname>TaskExecutorPartitionHandler</classname>, and it can be
configured like this:</para>
<classname>TaskExecutorPartitionHandler</classname>, and it is the
default for a step configured with the XML namespace as above. It can
also be configured explicitly like this:</para>
<para><programlisting>&lt;bean class="org.spr...TaskExecutorPartitionHandler"&gt;
<para><programlisting>&lt;step id="step1.master"&gt;
&lt;partition step="step1" handler="handler"/&gt;
&lt;/step&gt;
&lt;bean class="org.spr...TaskExecutorPartitionHandler"&gt;
&lt;property name="taskExecutor" ref="taskExecutor"/&gt;
&lt;property name="step" ref="step1" /&gt;
&lt;property name="gridSize" value="10" /&gt;
@@ -257,38 +267,17 @@
<para>The <classname>TaskExecutorPartitionHandler</classname> is quite
useful for IO intensive Steps, like copying large numbers of files or
replicating filesystems into content management systems.</para>
replicating filesystems into content management systems. It can also be
used for remote execution by providing a Step implementation that is a
proxy for a remote invocation (e.g. using Spring Remoting).</para>
</section>
<section id="stepExecutionSplitter">
<title>StepExecutionSplitter</title>
<title>Partitioner</title>
<para>The <classname>StepExecutionSplitter</classname> is responsible
for splitting up a <classname>StepExecution</classname> into blocks of
work, and providing input parameters for the remote Slaves in the form
of an <classname>ExecutionContext</classname> for each one. The
principal method for this in the interface is</para>
<programlisting>public interface StepExecutionSplitter {
...
Set&lt;StepExecution&gt; split(StepExecution stepExecution, int gridSize)
throws JobExecutionException;
}</programlisting>
<para>So an execution instance for the Master step is passed in, along
with a hint about the grid size, and the splitter has to create a set of
partitioned <classname>StepExecution</classname> instances, each with a
different <classname>ExecutionContext</classname>.</para>
<para>A convenient generic implementation of StepExecutionSplitter is
provided by Spring Batch, which handles concerns like interpreting the
grid size and handling restart. It is recommended that you use this
implementation (the <classname>SimpleStepExecutionSplitter</classname>)
and inject specific knowledge of the input data through its
<classname>Partitioner</classname> property. The Partitioner has a
simpler responsibility: to generate execution contexts as input
parameters for new step executions only (no need to worry about
restarts). It has a single method:</para>
<para>The Partitioner has a simpler responsibility: to generate
execution contexts as input parameters for new step executions only (no
need to worry about restarts). It has a single method:</para>
<programlisting>public interface Partitioner {
Map&lt;String, ExecutionContext&gt; partition(int gridSize);

View File

@@ -1647,7 +1647,7 @@ itemWriter.write(items);</programlisting>
<programlisting>&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobParameters[input.file.name]}</emphasis>" /&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobParameters['input.file.name']}</emphasis>" /&gt;
&lt;/bean&gt;</programlisting>
<para>Both the <classname>JobExecution</classname> and
@@ -1657,12 +1657,12 @@ itemWriter.write(items);</programlisting>
<programlisting>&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobExecutionContext[input.file.name]}</emphasis>" /&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobExecutionContext['input.file.name']}</emphasis>" /&gt;
&lt;/bean&gt;</programlisting>
<programlisting>&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{stepExecutionContext[input.file.name]}</emphasis>" /&gt;
&lt;property name="resource" value="<emphasis role="bold">#{stepExecutionContext['input.file.name']}</emphasis>" /&gt;
&lt;/bean&gt;</programlisting>
<note>
@@ -1671,6 +1671,17 @@ itemWriter.write(items);</programlisting>
information.</para>
</note>
<note>
<para>If you are using Spring 3.0 (or above) the expressions in
step-scoped beans are in the Spring Expression Language, a powerful
general purpose language with many interesting features. To provide
backward compatibility, if Spring Batch detects the presence of older
versions of Spring it uses a native expression language that is less
powerful, and has slightly different parsing rules. The main difference
is that the map keys in the example above do not need to be quoted with
Spring 2.5, but the quotes are mandatory in Spring 3.0.</para>
</note>
<section id="step-scope">
<title>Step Scope</title>

View File

@@ -62,7 +62,10 @@ public class SkipSampleFunctionalTests extends AbstractJobTests { ... }</program
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
"/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests extends AbstractJobTests {
public class SkipSampleFunctionalTests {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
private SimpleJdbcTemplate simpleJdbcTemplate;
@@ -80,7 +83,8 @@ public class SkipSampleFunctionalTests extends AbstractJobTests {
i, "customer" + i);
}
JobExecution jobExecution = <emphasis role="bold">this.launchJob()</emphasis>;
JobExecution jobExecution = jobLauncherTestUtils.launchJob().getStatus();
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus());
}
@@ -99,7 +103,81 @@ public class SkipSampleFunctionalTests extends AbstractJobTests {
targeted tests by allowing the test to set up data for just that step and
to validate its results directly.</para>
<programlisting>JobExecution jobExecution = this.launchStep("loadFileStep");</programlisting>
<programlisting>JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");</programlisting>
</section>
<section>
<title>Testing Step-Scoped Components</title>
<para>Often the components that are configured for your steps at runtime
use step scope and late binding to inject context from the step or job
execution. These are tricky to test as standalone components unless you
have a way to set the context as if they were in a step execution. That is
the goal of two components in Spring Batch: the
<classname>StepScopeTestExecutionListener</classname> and the
<classname>StepScopeTestUtils</classname>.</para>
<para>The listener is declared at the class level, and its job is to
create a step execution context for each test method. For example:</para>
<programlisting>@ContextConfiguration
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
StepScopeTestExecutionListener.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class StepScopeTestExecutionListenerIntegrationTests {
// This component is defined step-scoped, so it cannot be injected unless
// a step is active...
@Autowired
private ItemReader&lt;String&gt; reader;
public StepExecution getStepExection() {
StepExecution execution = MetaDataInstanceFactory.createStepExecution();
execution.getExecutionContext().putString("input.data", "foo,bar,spam");
return execution;
}
@Test
public void testReader() {
// The reader is initialized and bound to the input data
assertNotNull(reader.read());
}
}</programlisting>
<para>There are two <classname>TestExecutionListeners</classname>, one
from the regular Spring Test framework and handles dependency injection
from the configured application context, injecting the reader, and the
other is the Spring Batch
<classname>StepScopeTestExecutionListener</classname>. It works by looking
for a factory method in the test case for a
<classname>StepExecution</classname>, and using that as the context for
the test method, as if that execution was active in a Step at runtime. The
factory method is detected by its signature (it just has to return a
<classname>StepExecution</classname>). If a factory method is not provided
then a default <classname>StepExecution</classname> is created.</para>
<para>The listener approach is convenient if you want the duration of the
step scope to be the execution of the test method. For a more flexible,
but more invasive approach you can use the
<classname>StepScopeTestUtils</classname>. For example, to count the
number of items available in the reader above:</para>
<programlisting>int count = StepScopeTestUtils.doInStepScope(stepExecution,
new Callable&lt;Integer&gt;() {
public Integer call() throws Exception {
int count = 0;
while (reader.read() != null) {
count++;
}
return count;
}
});</programlisting>
</section>
<section id="validatingOutputFiles">