27
Dec
13

Hello World With Spring Batch 3.0.x with Pure Annotations


This page describes how to get a Spring Batch application to print hello world to the console. This page provides a stepping stone to help you get up and running quickly. Since this is a quick and dirty method of getting up and running with spring batch, it does not cover the fundamental concepts. For further information please visit the Spring Batch project documentation site.

This page shows the latest techniques of configuring spring batch using pure java annotations. This results in a significant reduction in work necessary to get a spring batch job running.

An older version of the page is available here. https://numberformat.wordpress.com/2010/02/05/hello-world-with-spring-batch/

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

This page takes about 10 minutes to complete and have a working spring batch application.

Background

The following spring batch example program is the simplest way you could setup a job to run in Spring batch. As such there are some limitations with the following approach.

Use of an In Memory Database

The following program uses an uses in memory database to store information about batch execution runs. This means that there is no protection against duplicate job runs and it does not store when a job was started or completed. Since spring is a pluggable architecture you can always change to use a persistent database like mysql or oracle to store job information. I will describe this process in a future blog entry.

If you use scheduling tools like Autosys you should already have a system that maintains information about job executions. These tools would maintain the history of past runs and if the job succeeded or not etc…so using an in-memory database should not big deal.

Library Versions

  • Spring Batch 3.0.0-M3 or above

Modify the pom.xml

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>spring-batch-helloworld</artifactId>
	<version>20131227</version>
	<name>spring batch hello world</name>
	<packaging>jar</packaging>

	<pluginRepositories>
		<pluginRepository> <!-- Ignore this repository. Its only used for document publication. -->
			<id>numberformat-releases</id>
			<url>https://raw.github.com/numberformat/20130213/master/repo</url>
		</pluginRepository>
	</pluginRepositories>

	<properties>
		<spring.framework.version>3.2.1.RELEASE</spring.framework.version>
		<spring.batch.version>3.0.0.M2</spring.batch.version>
	</properties>

	<repositories>
		<repository>
			<id>spring-s3</id>
			<name>Spring Maven MILESTONE Repository</name>
			<url>http://maven.springframework.org/milestone</url>
		</repository>
	</repositories>
	<dependencies>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-core</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-infrastructure</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.framework.version}</version>
		</dependency>
		<dependency>
			<groupId>hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>1.8.0.7</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin> <!-- Ignore this plugin. Its only used for document publication. -->
				<groupId>github.numberformat</groupId>
				<artifactId>blog-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<configuration>
					<gitUrl>https://github.com/numberformat/wordpress/tree/master/${project.version}/${project.artifactId}</gitUrl>
				</configuration>
				<executions>
					<execution>
						<id>1</id>
						<phase>site</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

<!-- Run the application using: 
mvn compile exec:java -Dexec.mainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dexec.args="com.test.config.HelloWorldJobConfig helloWorldJob"
-->
</project>

Setup the log4j configuration files. We will be using a very basic file that outputs to the console.
vi src/main/resources/log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
   
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
        </layout>
    </appender>
    <logger name="org.springframework" additivity="false">
        <level value="INFO"/>
        <appender-ref ref="CONSOLE"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root>
</log4j:configuration>

The job configuration is stored in the com.test.config package. The configuration for the infrastructure and the job is present in this package.

Config Interface

vi src/main/java/com/test/config/InfrastructureConfiguration.java

package com.test.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;

public interface InfrastructureConfiguration {

	@Bean
	public abstract DataSource dataSource();

}

The implementation

vi src/main/java/com/test/config/StandaloneInfrastructureConfiguration.java

package com.test.config;

import javax.sql.DataSource;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

@Configuration
@EnableBatchProcessing
public class StandaloneInfrastructureConfiguration implements InfrastructureConfiguration {
	
	@Bean
	public DataSource dataSource(){
		EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder();
		return embeddedDatabaseBuilder.addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
				.addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
				.setType(EmbeddedDatabaseType.HSQL)
				.build();
	}

}

Job Configuration

vi src/main/java/com/test/config/HelloWorldJobConfig.java

package com.test.config;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.test.HelloWorldTasklet;

@Configuration
@Import(StandaloneInfrastructureConfiguration.class)
public class HelloWorldJobConfig {

	@Autowired
	private JobBuilderFactory jobBuilders;
	
	@Autowired
	private StepBuilderFactory stepBuilders;
	
	@Autowired
	private InfrastructureConfiguration infrastructureConfiguration;
	
	@Autowired
	private DataSource dataSource; // just for show...
	
	@Bean
	public Job helloWorldJob(){
		return jobBuilders.get("helloWorldJob")
				.start(step())
				.build();
	}
	
	@Bean
	public Step step(){
		return stepBuilders.get("step")
				.tasklet(tasklet())
				.build();
	}
	
	@Bean
	public Tasklet tasklet() {
		return new HelloWorldTasklet();
	}
}

The Tasklet

This class does the actual printing of the message to the console.

vi src/main/java/com/test/HelloWorldTasklet.java

package com.test;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class HelloWorldTasklet implements Tasklet {
	 
    public RepeatStatus execute(StepContribution arg0, ChunkContext arg1)
            throws Exception {
        System.out.println("");
        System.out.println(" XXX XXX           XX      XX             ");
        System.out.println("  X   X             X       X             ");
        System.out.println("  X   X             X       X             ");
        System.out.println("  X   X   XXXXX     X       X     XXXXX   ");
        System.out.println("  XXXXX  X     X    X       X    X     X  ");
        System.out.println("  X   X  XXXXXXX    X       X    X     X  ");
        System.out.println("  X   X  X          X       X    X     X  ");
        System.out.println("  X   X  X     X    X       X    X     X  ");
        System.out.println(" XXX XXX  XXXXX   XXXXX   XXXXX   XXXXX   ");
        System.out.println("                                          ");
        System.out.println("                                          ");
        System.out.println("                                          ");
        System.out.println("                                          ");
        System.out.println(" XXX XXX                   XX        XX   ");
        System.out.println("  X   X                     X         X   ");
        System.out.println("  X   X                     X         X   ");
        System.out.println("  X   X   XXXXX  XXX XX     X     XXXXX   ");
        System.out.println("  X X X  X     X   XX  X    X    X    X   ");
        System.out.println("  X X X  X     X   X        X    X    X   ");
        System.out.println("  X X X  X     X   X        X    X    X   ");
        System.out.println("   X X   X     X   X        X    X    X   ");
        System.out.println("   X X    XXXXX  XXXXX    XXXXX   XXXXXX  ");
        System.out.println("");
        return RepeatStatus.FINISHED;
    }
}

Execute the Job

To run the job from the command line type the following.

mvn compile exec:java -Dexec.mainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dexec.args="com.test.config.HelloWorldJobConfig helloWorldJob"

Deploying the application

The following tutorial describes how to Package and deploy the application as a self contained jar. (just be mindful to change the commandline args to the one you see here.)

What’s Next?

In the next few articles I plan on describing how to:

  1. Read and write flat files.
  2. Write header and footer records in the output file.
  3. Replace the in-memory database with a HyperSQL Java database so we can have job information persist between job invocations.
  4. Throw an exception in the middle of a large batch job and restart the job execution from the point where it left off.
  5. Use validation framework like “commons-validator” to perform input file validation and create reject records for manual correction and later processing.
Advertisements

3 Responses to “Hello World With Spring Batch 3.0.x with Pure Annotations”


  1. 1 Jim C
    January 13, 2016 at 4:23 pm

    If I try your example from Spring Tool Suite (i.e. Eclipse) it works perfectly. I mean, I download your project, import in STS, right click Run, Configuration, I added the two arguments (com.test.config.HelloWorldJobConfig and helloWorldJob) it runs. On other hands, if I try via command exactly as you wrote, I get this error:
    C:\temp\TaskletJavaConfig\spring-batch-helloworld>mvn compile exec:java -Dexec.m
    ainClass=org.springframework.batch.core.launch.support.CommandLineJobRunner -Dex
    ec.args=”com.test.config.HelloWorldJobConfig helloWorldJob”
    Picked up JAVA_TOOL_OPTIONS: -agentlib:jvmhook
    Picked up _JAVA_OPTIONS: -Xrunjvmhook -Xbootclasspath/a:C:\PROGRA~2\HP\QUICKT~1\
    bin\JAVA_S~1\classes;C:\PROGRA~2\HP\QUICKT~1\bin\JAVA_S~1\classes\jasmine.jar
    [INFO] Scanning for projects…
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven
    -jar-plugin/2.4/maven-jar-plugin-2.4.pom
    [WARNING] Failed to retrieve plugin descriptor for org.apache.maven.plugins:mave
    n-jar-plugin:2.4: Plugin org.apache.maven.plugins:maven-jar-plugin:2.4 or one of
    its dependencies could not be resolved: Failed to read artifact descriptor for
    org.apache.maven.plugins:maven-jar-plugin:jar:2.4
    Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven
    -deploy-plugin/2.7/maven-deploy-plugin-2.7.pom

    [ERROR] No plugin found for prefix ‘exec’ in the current project and in the plug
    in groups [org.codehaus.mojo, org.apache.maven.plugins] available from the repos
    itories [local (C:\Users\myUser\.m2\repository), central (https://repo.maven.ap
    ache.org/maven2)]

  2. November 19, 2016 at 6:35 pm

    The example doesn’t work using command line !


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 77 other followers

December 2013
S M T W T F S
« Oct   Feb »
1234567
891011121314
15161718192021
22232425262728
293031  

Blog Stats

  • 830,676 hits

%d bloggers like this: