Sunday, October 5, 2008

Spring Batch "Hello World" 2

Last time we implemented a simple PrintTasklet that prints a "Hello World!" message. In this part we will improve our tasklet a little bit by passing the message to print as a parameter through the command line. Thus, we will start using JobParameters.

Each batch job is started with a JobParameters object; it is what Spring Batch uses to distinguish between different instances of the same job. It also serves as a holder for runtime data, just as we will be using it in this example.
JobParameters are typed. They support properties of numerical, date and String types. We will pass our message as a String parameter aptly named "message".

Let's see how we will pass our message from the command line down to the tasklet. When a job is started with CommandLineJobRunner, this latter can interpret appropriately formatted arguments and transform them to JobParameters. The basic format is paramaterName(type)=value. The type can be omitted for String parameters.
Now we need to read the message from within our tasklet. Spring Batch provides a collection of listener interfaces that enable hooking custom code at different points of the execution of a job.
In order to access JobParameters from our tasklet, we will implement the StepExecutionListener interface and read the message before executing the step; that is in the beforeStep method:
As a convenience, we will subclass StepExecutionListenerSupport, which is an empty implementation of StepExecutionListener, and redefine the method we need:

public class ParameterPrintTasklet
      extends StepExecutionListenerSupport
      implements Tasklet {

  private String message;

  public ExitStatus execute() throws Exception {
      System.out.println(message);
      return ExitStatus.FINISHED;
  }

  public void beforeStep(StepExecution stepExecution) {
      JobParameters jobParameters = stepExecution.getJobParameters();
      message = jobParameters.getString("message");
  }
}
The XML configuration is straightforward:

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
  <import resource="applicationContext.xml" />

  <bean id="print" class="helloworld.ParameterPrintTasklet"/>

  <bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob">
    <property name="name" value="simpleJob" />
    <property name="steps">
      <list>
        <bean class="org.springframework.batch.core.step.tasklet.TaskletStep">
          <property name="tasklet" ref="print"/>
          <property name="jobRepository" ref="jobRepository"/>
        </bean>
      </list>
    </property>
    <property name="jobRepository" ref="jobRepository"/>
  </bean>
</beans>
Usually, we would register StepExecutionListeners at the TaskletStep level, but TaskletStep is smart enough to detect that its Tasklet is also a listener and registers it automatically.

Running the Job

Here's the Maven command to launch the job with CommandLineJobRunner. Notice how we pass the message as an argument this time:

mvn exec:java -Dexec.mainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dexec.args="simpleJob.xml simpleJob message=Hello_World!"
The code source can be downloaded here.

What's Next?

The next and final part will focus on an area where Spring Batch shines: item oriented jobs.