use value from step in job xml file configuration in spring batch - spring-batch

I have a spring batch job where I need to get a value from database in a step and use that value in a reader in an other step . but I m getting an error saying :
'Invalid property 'id' of bean class [org.springframework.batch.core.scope.context.StepContext]: Bean property 'id' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?'
here I will put only the relevant part of my files to be clear , if need extra infos I can add it , thanks
xml config file:
<bean id="setter" class="fat.IdSetter" scope="step">
<property name="id" value="#{stepExecutionContext([id])}"></property>
</bean>
<bean id="reader"
class="org.springframework.batch.item.database.JdbcCursorItemReader" >
<property name="dataSource" ref="dataSource" />
<property name="sql" value="SELECT * FROM TABLE WHERE ID = ?" />
<property name="rowMapper">
<bean class="fat.RowMapper"></bean>
</property>
<property name="preparedStatementSetter" ref="setter"></property>
</bean>
<batch:job id="job">
<batch:step id="getId" next="loopStep">
<batch:tasklet ref="getId"></batch:tasklet>
</batch:step>
<batch:step id="loopStep">
<batch:tasklet>
<batch:chunk reader="reader" processor="processor"
writer="xmlWriter" commit-interval="1000">
</batch:chunk>
<batch:listeners>
<batch:listener ref="footerCallBack"></batch:listener>
<batch:listener ref="headerCallBack"></batch:listener>
</batch:listeners>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="getId" class="fat.GetId" >
<property name="restCall" ref="restCall"></property>
</bean>
the tasklet where I get the value of id :
public class GetId implements Tasklet {
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
// here I call my web service to get data and put it in a Long variable named id
chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("id",
id);
return RepeatStatus.FINISHED;
}
}
the prepared statement setter for id class:
public class IdSetter implements PreparedStatementSetter {
private Long id;
#Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setLong(1 , id);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
I think the error because it didn t find the id parameter in the stepExecutionContext , any help is appreciated.

Your tasklet GetId puts the id in the job execution context. So your reader can get it from there as follows:
<bean id="reader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step" >
<property name="dataSource" ref="dataSource" />
<property name="sql" value="SELECT * FROM TABLE WHERE ID = #{jobExecutionContext[id]}" />
<property name="rowMapper">
<bean class="fat.RowMapper"></bean>
</property>
<!-- <property name="preparedStatementSetter" ref="setter"></property> --> // This is not needed
</bean>
Note that the reader should step scoped. With that, you don't need the bean named setter.
Another option is to pass the id as a job parameter and use it in the reader (See example here).

Related

How to specify fragmentRootElementNames in StaxEventItemReader for nested elements?

Working with Spring-batch 4.1.2 to consume a complex XML and write to database.
Using StaxEventItemReader for reading XML data.
I am facing an issue for nested elements, while specifying fragmentRootElementNames.
<bean id="productFileItemReader" class="org.springframework.batch.item.xml.StaxEventItemReader" scope="step">
<property name="resource" ref="inputResource" />
<property name="fragmentRootElementNames" value="ProductFeatures,ProductFeaturesDetail,SomeOtherNon-NestedFragmentNames..." />
<property name="unmarshaller" ref="productMarshaller" />
</bean>
<bean id="productMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.xyz.jaxb.ProductFeaturesType</value>
<value>com.xyz.jaxb.ProductFeaturesDetailType</value>
...Some more types
</list>
</property>
</bean>
<bean id="productFileItemProcessor" class="org.springframework.batch.item.support.ClassifierCompositeItemProcessor"
scope="step">
<property name="classifier" ref="prodItemProcessorclassifier" />
</bean>
<bean id="prodItemProcessorclassifier" class="org.springframework.classify.BackToBackPatternClassifier">
<property name="routerDelegate">
<bean class="com.xyz.batch.CustomProdTypeClassifier" />
</property>
<property name="matcherMap">
<map>
<entry key="ProductFeatures" value-ref="prodFeaturesProcessor" />
<entry key="ProductFeaturesDetail" value-ref="prodFeaturesDetailProcessor" />
</map>
</property>
</bean>
<bean id="prodItemWriter" class="org.springframework.batch.item.support.ClassifierCompositeItemWriter"
scope="step">
<property name="classifier" ref="prodItemWriterclassifier" />
</bean>
<bean id="prodItemWriterclassifier" class="org.springframework.classify.BackToBackPatternClassifier">
<property name="routerDelegate">
<bean class="com.xyz.batch.CustomProdTypeClassifier" />
</property>
<property name="matcherMap">
<map>
<entry key="ProductFeaturesDetail" value-ref="prodHibernateItemWriter" />
<entry key="ProductParameters" value-ref="myListItemWriter" />
</map>
</property>
</bean>
<bean id="myListItemWriter" class="com.xyz.writer.ListDelegateItemWriter">
<property name="delegate" ref="prodHibernateItemWriter" />
</bean>
<bean id="prodHibernateItemWriter" class="org.springframework.batch.item.database.HibernateItemWriter">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
Here is the signature of the ItemProcessors
public class ProductFeaturesProcessor implements ItemProcessor<ProductFeaturesType, List<ProductParamEntity>>{}
public class ProductFeaturesDetailProcessor implements ItemProcessor<ProductFeaturesDetailType, ProductFeaturesDetailEntity> {}
XML structure is as follows -
<ProductFeatures>
<Version>12</Version>
<MessageEN>Welcome</MessageEN>
<MessageFR>Bienvenue</MessageFR>
<ProductFeaturesDetail>
<!-- Some elements here -->
</ProductFeaturesDetail>
<ProductFeaturesDetail>
<!-- Some elements here -->
</ProductFeaturesDetail>
<ProductFeaturesDetail>
<!-- Some elements here -->
</ProductFeaturesDetail>
<ProductFeaturesDetail>
<!-- Some elements here -->
</ProductFeaturesDetail>
</ProductFeatures>
Here, ProductFeatures has minOccurs=1, maxOccurs=1.
ProductFeaturesDetail is unbounded.
Each of Version, MessageEN, MessageFR need to be persisted as a record in Table-A which has 2 columns ParamName|ParamValue Each ProductFeaturesDetail is a record to be persisted in Table-B.
i.e. The 3 elements are each mapped to ProductParamEntity (i.e. with
ParamName=Version, ParamValue=12 ; ParamName=MessageEN,
ParamValue=Welcome etc.)
Logically, ProductFeatures is NOT mapped to a table directly but data
is stored in 2 tables as List and
List into tables PRODUCT_PARAM and
PRODUCT_FEATURES_DETAIL respectively.
Here are the Objects - JAXB and Corresponding Entities
#XmlRootElement(name = "ProductFeatures")
public class ProductFeaturesType {
#XmlElement(name = "Version")
protected String version;
#XmlElement(name = "MessageEN")
protected String messageEN;
#XmlElement(name = "MessageFR")
protected String messageFR;
#XmlElement(name = "ProductFeaturesDetail")
protected List<ProductFeaturesDetailType> prodFeaturesDetail;
}
#Entity
#Table(name="PRODUCT_PARAM")
public class ProductParamsEntity extends BaseEntity implements Serializable {
#Id
#Column(name="PARAM_NAME")
private String paramName;
#Column(name="PARAM_VALUE")
private String paramValue;
}
#Entity
#Table(name="PRODUCT_FEATURES_DETAIL")
public class ProductFeaturesDetailEntity extends BaseEntity implements Serializable {
#Id
#Column(name="PROD_CATEGORY")
private String prodCategory;
//---More attributes ---
}
Here is my Job Config :
<batch:job id="consumeProductFileJob" job-repository="jobRepository" restartable="true">
<batch:step id="validateFile" parent="validateXMLSchema">
<batch:next on="COMPLETED" to="persistData" />
<batch:next on="FAILED" to="notifyException" />
</batch:step>
<batch:step id="persistData">
<batch:tasklet transaction-manager="prodHibernateTransactionManager">
<batch:chunk reader="productFileItemReader" processor="productFileItemProcessor" writer="prodItemWriter" commit-interval="500" >
</batch:chunk>
<batch:transaction-attributes isolation="DEFAULT" propagation="REQUIRED" />
</batch:tasklet>
</batch:step>
<batch:step id="notifyException">
-- Do something---
</batch:step>
</batch:job>
Issue : If I specify both as fragmentRootElementNames, only ProductFeatures elements Version, MessageEN, MessageFR are persisted
in Table-A. ProductFeaturesDetail is being ignored.
If I specify just ProductFeaturesDetail, it works fine. But I dont get
the individual elements within ProductFeatures.
I want the data from both elements. What is the way to achieve this?
P.S. I am using HibernateItemWriter for persistence.

Spring Batch With Schedular

enter image description hereI am new to Spring Batch with Scheduler. Here my task is to read the data from one table and write it into another table.
I am randomly going through the blogs and different tutorials.
I don't know whether there is any direct approach read from database and write into database. I took this approach like
Job 1 : Reads the data from the db using JdbcCursorItemReader, writing the data into a txt file using FlatFileItemWriter.
Job 2: Read the data from the txt file using FlatFileItemReader, multiResourceItemReader and writing the data into another table using HibernateItemWriter.
I am using a scheduler and it is going to run the batch for every 20 sec.
In this approach for the first run it is working fine. For the second run(after 20 sec), I am updating the data in the database(base table) but it is not writing updated data into the file and database.
Here is my configuration & code`package com.cg.schedulers;
public class UserScheduler {
#Autowired
private JobLauncher launcher;
#Autowired
private Job userJob;
#Autowired
private Job userJob2;
private JobExecution execution1,execution2;
public void run() {
try {
execution1 = launcher.run(userJob, new JobParameters());
execution2 = launcher.run(userJob2, new JobParameters());
System.out.println("Execution status: " + execution1.getStatus());
System.out.println("Execution status: " + execution2.getStatus());
} catch (JobExecutionAlreadyRunningException e) {
e.printStackTrace();
} catch (JobRestartException e) {
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
e.printStackTrace();
} catch (JobParametersInvalidException e) {
e.printStackTrace();
}
}
}
Xml Configuration
<import resource="spring-batch1.xml" />
<import resource="springbatch-database.xml" />
<context:annotation-config/>
<context:component-scan base-package="com.cg"/>
<!-- Reading data from -->
<bean id="itemReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql" value="select UserId, UserName, Password from USER" />
<property name="rowMapper">
<bean class="com.cg.mapper.UserRowMapper" />
</property>
</bean>
<!-- ItemWriter writes a line into output flat file -->
<bean id="flatFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step">
<property name="resource" value="file:csv/User.txt" />
<property name="lineAggregator">
<!-- An Aggregator which converts an object into delimited list of strings -->
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="," />
<property name="fieldExtractor">
<!-- Extractor which returns the value of beans property through reflection -->
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="userId, username, password" />
</bean>
</property>
</bean>
</property>
</bean>
<!-- ItemReader reads a complete line one by one from input file -->
<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader"
scope="step">
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="fieldSetMapper">
<!-- Mapper which maps each individual items in a record to properties
in POJO -->
<bean class="com.cg.mapper.UserFieldSetMapper" />
</property>
<property name="lineTokenizer">
<!-- A tokenizer class to be used when items in input record are separated
by specific characters -->
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="," />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="multiResourceItemReader"
class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="classpath:csv/User.txt" />
<property name="delegate" ref="flatFileItemReader" />
</bean>
<!-- Optional JobExecutionListener to perform business logic before and
after the job -->
<bean id="jobListener" class="com.cg.support.UserItemListener" />
<!-- Optional ItemProcessor to perform business logic/filtering on the input
records -->
<bean id="itemProcessor1" class="com.cg.support.UserItemProcessor" />
<bean id="itemProcessor2" class="com.cg.support.UserItemProcessor2" />
<!-- ItemWriter which writes data to database -->
<bean id="databaseItemWriter"
class="org.springframework.batch.item.database.HibernateItemWriter">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Actual Job -->
<batch:job id="userJob">
<batch:step id="step1">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="itemReader" writer="flatFileItemWriter"
processor="itemProcessor1" commit-interval="10" />
</batch:tasklet>
</batch:step>
<batch:listeners>
<batch:listener ref="jobListener" />
</batch:listeners>
</batch:job>
<batch:job id="userJob2">
<batch:step id="step2">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="multiResourceItemReader" writer="databaseItemWriter"
processor="itemProcessor2" commit-interval="10" />
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="myScheduler" class="com.cg.schedulers.UserScheduler"/>
<task:scheduled-tasks>
<task:scheduled ref="myScheduler" method="run" cron="*/20 * * * * *" />
</task:scheduled-tasks>
Please provide the direct approach if possible using hibernate.
[enter image description here][2]
Execution status: COMPLETED
Thanks,
Vamshi.

Spring Batch Database Dependency pass parameters to SQL statement

I want separate the sql from the batch.xml file, so I defined the sql statement into a properties file. Inside the batch.xml I bind the property-placeholder bean then point to the properties file.
For simple select statement should not be a problem. But if I want to pass the parameter as where clause condition is it possible to do that?
<context:property-placeholder
location="classpath:batch-sql.properties/>
<bean id="secondReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql" value="${sql1}" />
<property name="rowMapper">
<bean class="com.test.batchjob.process.TestPersonMapper" />
</property>
</bean>
This is my sql statment in properties file:
SELECT * FROM Person WHERE id = ?
Can the id pass from jobparameter?
To set the parameters of the query in a JdbcPagingItemReader, you have to use the property parametersValue. This property takes a Map<String,Object> where the key is either the named parameter or the index of the parameter (if you use ?).
<bean id="secondReader"
class="org.springframework.batch.item.database.JdbcPagingItemReader"
scope="step">
<property name="queryProvider">
<bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="selectClause" value="select *" />
<property name="fromClause" value="from persons" />
<property name="whereClause" value="where id = ?" />
</bean>
</property>
<property name="parametersValue">
<map>
<entry key="1" value="#{jobParameters['id']}" />
</map>
</property>
<property name="rowMapper">
<bean class="com.test.batchjob.process.TestPersonMapper" />
</property>
</bean>
See documentation : JdbcPagingItemReader
UPDATE
You have to use a QueryProvider instead of sql and datasource properties.
You can replace the text of the query by values of the properties file.
To set the parameters of the query in a JdbcCursorItemReader, you have to use the property preparedStatementSetter. This property takes a PreparedStatementSetter which you have to implement yourself to set either named or index-based parameters.
<bean id="secondReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="sql" value="${sql1}" />
<property name="itemPreparedStatementSetter">
<bean class="xx.xx.xx.YourPreparedStatementSetter">
<property name="id" value="#{jobParameters['id']}" />
</bean>
</property>
<property name="rowMapper">
<bean class="com.test.batchjob.process.TestPersonMapper" />
</property>
An example implementation of a PreparedStatementSetter :
public class YourPreparedStatementSetter implements PreparedStatementSetter {
private String id;
#Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, this.id);
}
public setId(String id) {
this.id = id;
}
}
For JdbcCursorItemReader, you can take a look at answer here : Using Spring Batch JdbcCursorItemReader with NamedParameters

How to invoke sftp:outbound-gateway from spring batch?

I would like to invoke sftp:outbound-gateway from batch tasklet in order to download a file from sftp server.
I've seen other posts related to this subject but I'm not sure what am I doing wrong. Could anybody give me a hint based on my configuration? My batch works so the problem is just to ivoke the sftp component in batch step. I've marked the Spring Integration section with comment so it is easier to read just a relevant configuration.
I can see in my logs: DEBUG [o.s.i.e.SourcePollingChannelAdapter] Received no Message during the poll, returning 'false'. So I am not receiving a file but why?
Thanks in advance for your time spend on analysis!
<bean id="ftsSftpClientFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${my.import.sftp.localhost}"/>
<property name="user" value="${my.import.sftp.username}"/>
<property name="password" value="${my.import.sftp.passwort}"/>
</bean>
<!-- Start: Spring Integration -->
<int:channel id="replyChannel" >
<int:queue/>
</int:channel>
<int:channel id="requestChannel" />
<int-sftp:outbound-gateway id="sftpGateway"
session-factory="ftsSftpClientFactory"
request-channel="requestChannel"
reply-channel="replyChannel"
auto-startup="true"
command="get"
command-options="-P"
expression="payload"
remote-directory="."
local-directory="${my.import.sftp.copy.file.destinationpath}">
</int-sftp:outbound-gateway>
<bean name="copyFileTasklet" class="com.mydomain.CopyFileTasklet">
<property name="channel" ref="replyChannel" />
<property name="pollableChannel" ref="requestChannel" />
</bean>
<!-- Start: Spring Batch -->
<bean name="myImportTask" class="com.mydomain.MyImportTask">
<property name="job" ref="unternehmungImportJob"/>
<property name="jobLauncher" ref="jobLauncher"/>
</bean>
<bean id="jobDetail"
class="com.mydomain.MyImportJob">
<property name="myImportTask" ref="myImportTask" />
</bean>
<!--suppress SpringBatchModel -->
<batch:job id="myImportJob">
<batch:step id="copy-file-step" next="my-import-step">
<batch:tasklet ref="copyFileTasklet"/>
</batch:step>
<batch:step id="my-import-step">
<batch:tasklet>
<batch:chunk reader="myItemReader"
writer="myItemWriter"
commit-interval="10000">
<!--
skip-limit="10000"
<batch:skippable-exception-classes>
<batch:include class="java.lang.Exception"/>
<batch:exclude class="java.io.FileNotFoundException"/>
</batch:skippable-exception-classes> -->
</batch:chunk>
<batch:transaction-attributes isolation="DEFAULT" propagation="REQUIRED"/>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="myItemReader" scope="step" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="linesToSkip" value="1"/>
<property name="encoding" value="${my.import.batch.encoding}" />
<property name="resource" value="${my.import.batch.input.resource}"/>
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer" ref="lineTokenizer"/>
<property name="fieldSetMapper">
<bean class="com.mydomain.MyImportMapper"/>
</property>
</bean>
</property>
</bean>
<bean id="myItemWriter" class="com.mydomain.MyItemWriter">
<property name="myApplicationService" ref="defaultmyApplicationService" />
</bean>
<bean id="lineTokenizer" class="com.mydomain.DelimitedLineTokenizerWithEOF">
<property name="delimiter" value="${my.import.batch.delimiter}" />
<property name="eofMarker" value="${my.import.batch.eof.marker}" />
</bean>
public class CopyFileTasklet implements Tasklet {
private MessageChannel requestChannel;
private PollableChannel replyChannel;
public void setRequestChannel(MessageChannel requestChannel) {
this.requestChannel = requestChannel;
}
public void setReplyChannel(PollableChannel replyChannel) {
this.replyChannel = replyChannel;
}
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
Message<?> result = replyChannel.receive(10000);
Object file = result.getPayload();
return RepeatStatus.FINISHED;
}
}
Your issue that you don't inititate Integration Flow from your custom Tasklet. Of course you can't receive anything from the replyChannel, if you haven't sent request before.
If you just need to process Integration Flow and get result from it, it would be better to use POJI <gateway> from that Tasklet:
public interface SftpGateway {
File download(String fileName);
}
<gateway id="sftpGateway" service-interface="com.my.proj.SftpGateway"
default-request-channel="requestChannel"/>
<bean name="copyFileTasklet" class="com.mydomain.CopyFileTasklet">
<property name="sftpGateway" ref="sftpGateway" />
</bean>
Something like that.

Spring Batch error (A Job Instance Already Exists) and RunIdIncrementer generates only once

i'm using Spring Batch & Quartz to read from database table and write in another table. the database is Oracle and it is c3p0
the problem is each job must have a unique parameters, I tried RunIdIncrementer and I tried this code:
public class JobRerunner implements JobParametersIncrementer {
#Override
public JobParameters getNext(JobParameters parameters) {
System.out.println("got job parameters: " + parameters);
if (parameters==null || parameters.isEmpty()) {
return new JobParametersBuilder().addLong("run.id", System.currentTimeMillis()).toJobParameters();
}
long currentTime = parameters.getLong("run.id",System.currentTimeMillis()) + 1;
return new JobParametersBuilder().addLong("run.id",currentTime).toJobParameters();
}
}
but I get the same problem, the run.id is generated only once, and when the job is ran for the second time it has no parameters at all and the third time also (the second and third run JobParameter = null so (Job Instance Already Exists)
job context
<batch:job id="readyReqPoolJob" restartable="true">
<batch:step id="readyReqPoolStep">
<batch:tasklet>
<batch:chunk reader="readyReqPoolReader" writer="readyReqPoolWrtiter"
commit-interval="100" />
</batch:tasklet>
</batch:step>
</batch:job>
<!-- ======================================================= -->
<!-- 6) READER -->
<!-- ======================================================= -->
<bean id="readyReqPoolReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="dataSource" />
<property name="sql" value="select * from SF_ILA_Ready_Request_Pool" />
<property name="rowMapper" ref="ReadyReqPoolRowMapper" />
</bean>
<bean id="readyReqPoolWrtiter"
class="com.housekeepingservice.readyrequestpoolarchive.ReadyReqPoolArchiveWriter" />
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"
value="org.springframework.batch.sample.quartz.JobLauncherDetails" />
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="readyReqPoolJob" />
<entry key="jobLocator" value-ref="jobRegistry" />
<entry key="jobLauncher" value-ref="jobLauncher" />
</map>
</property>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0/5 * * * ?" />
</bean>
</property>
</bean>
main context:
<import resource="classpath:spring/batch/config/readyReqPoolContext.xml"
<import resource="classpath:spring/batch/config/jdbc.commons.xml" />
<!-- 1) USE ANNOTATIONS TO CONFIGURE SPRING BEANS -->
<context:component-scan base-package="com.housekeepingservice" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<!-- 3) JOB REPOSITORY -->
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
<!-- 4) LAUNCH JOBS FROM A REPOSITORY -->
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor" ref="taskExecutor" />
</bean>
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean name="jobParamatersIncrementer" class="org.springframework.batch.core.launch.support.RunIdIncrementer">
</bean>
Test.java
public class Test {
public static void main(String[] args) {
String[] springConfig = { "spring/batch/config/mainContext.xml" };
ApplicationContext context = new ClassPathXmlApplicationContext(
springConfig);
JobRerunner rerun = new JobRerunner();
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job readyRequestPoolJob = (Job) context.getBean("readyReqPoolJob");
try {
JobParameters jobParameters = new JobParameters();
JobExecution execution2 = jobLauncher.run(readyRequestPoolJob, rerun.getNext(jobParameters));
System.out.println("Exit Status : " + execution2.getStatus());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
}
}
log (checkout the job incetance parameters in the first run and the second run):
17:00:27,053 INFO SimpleJobLauncher:132 - Job: [FlowJob: [name=readyReqPoolJob]] launched with the following parameters: **[{run.id=1393855226339}]**
17:00:27.085 [Timer-0] DEBUG org.quartz.utils.UpdateChecker - Checking for available updated version of Quartz...
17:00:27,272 INFO SimpleStepHandler:135 - Executing step: [readyReqPoolStep]
17:02:08,791 INFO SimpleJobLauncher:135 - Job: [FlowJob: [name=readyReqPoolJob]] completed with the following parameters: [{run.id=1393855226339}] and the following status: [COMPLETED]
17:10:00.005 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job DEFAULT.jobDetail
17:10:00,008 INFO JobLauncherDetails:69 - Quartz trigger firing with Spring Batch jobName=readyReqPoolJob
17:10:00,036 INFO SimpleJobLauncher:132 - Job: [FlowJob: [name=readyReqPoolJob]] launched with the following parameters: **[{}]**
17:10:00,059 INFO SimpleStepHandler:135 - Executing step: [readyReqPoolStep]
To lunch a job with job Incremater you need two things
Attach the RunIdIncremater to your job.
Use a launcher that is aware of the usage Incremater.
I do not see any need for your own implementation just use the existing one.
Attach the RunIdIncremater to your job.
<batch:job id="readyReqPoolJob" incrementer="runIdIncrementer" restartable="true">
</batch:job>
<bean id="runIdIncrementer"
class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>
Use a launcher
To launch it you should use one of the following:
Option 1: CommandLineJobRunner with the –next option see the API
Option 2: User JobOperator
<bean id="jobOperator"
class="org.springframework.batch.core.launch.support.SimpleJobOperator">
<property name="jobRepository" ref="jobRepository" />
<property name="jobLauncher" ref="jobLauncher" />
<property name="jobRegistry" ref="jobRegistry" />
<property name="jobExplorer" ref="jobExplorer" />
</bean>
in the code
jobOperator.startNextInstance(jobName)
Option 3: In Junit you can use JobLauncherTestUtils.
Note that it has it’s own id Incremater and will ignore the one you use
see also the following answer SpringBatch: Test a JobExecutionListener
Set step allowStartIfComplete flag to True
Add a parameter called 'timestamp' for example or - if you want to use run.id - set Job.jobParametersIncrementer with your jobParamatersIncrementer bean definition.