Skip to content

Add a description for a step both with the XML and Java configuration styles #4830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -71,6 +71,7 @@
* @author Lucas Ward
* @author Dave Syer
* @author Mahmoud Ben Hassine
* @author Minkuk Jo
*/
public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, InitializingBean {

Expand All @@ -96,6 +97,8 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In

private BatchJobObservationConvention observationConvention = new DefaultBatchJobObservationConvention();

private String description;

/**
* Default constructor.
*/
Expand All @@ -112,6 +115,24 @@ public AbstractJob(String name) {
this.name = name;
}

/**
* Set the description of the job.
* @param description the job description
* @since 6.0
*/
public void setDescription(String description) {
this.description = description;
}

/**
* Get the description of the job.
* @return the job description
* @since 6.0
*/
public String getDescription() {
return this.description;
}

/**
* A validator for job parameters. Defaults to a vanilla
* {@link DefaultJobParametersValidator}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
* @author Dave Syer
* @author Mahmoud Ben Hassine
* @author Taeik Lim
* @author Minkuk Jo
* @since 2.2
*/
public abstract class JobBuilderHelper<B extends JobBuilderHelper<B>> {
Expand Down Expand Up @@ -180,6 +181,19 @@ public B preventRestart() {
return result;
}

/**
* Add a description to the job.
* @param description the job description
* @return this to enable fluent chaining
* @since 6.0
*/
public B description(String description) {
properties.description = description;
@SuppressWarnings("unchecked")
B result = (B) this;
return result;
}

protected String getName() {
return properties.name;
}
Expand Down Expand Up @@ -221,6 +235,11 @@ protected void enhance(AbstractJob job) {
job.setRestartable(restartable);
}

String description = properties.getDescription();
if (description != null) {
job.setDescription(description);
}

List<JobExecutionListener> listeners = properties.getJobExecutionListeners();
if (!listeners.isEmpty()) {
job.setJobExecutionListeners(listeners.toArray(new JobExecutionListener[0]));
Expand All @@ -245,6 +264,8 @@ public static class CommonJobProperties {

private JobParametersValidator jobParametersValidator;

private String description;

public CommonJobProperties() {
}

Expand All @@ -258,6 +279,7 @@ public CommonJobProperties(CommonJobProperties properties) {
this.jobExecutionListeners = new LinkedHashSet<>(properties.jobExecutionListeners);
this.jobParametersIncrementer = properties.jobParametersIncrementer;
this.jobParametersValidator = properties.jobParametersValidator;
this.description = properties.description;
}

public JobParametersIncrementer getJobParametersIncrementer() {
Expand Down Expand Up @@ -336,6 +358,14 @@ public void setRestartable(boolean restartable) {
this.restartable = restartable;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

private String name;

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2022 the original author or authors.
* Copyright 2020-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,11 +24,14 @@
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.annotation.AfterJob;
import org.springframework.batch.core.annotation.BeforeJob;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.JobRepositorySupport;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.ApplicationContext;
Expand All @@ -40,9 +43,11 @@
import org.springframework.transaction.PlatformTransactionManager;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Mahmoud Ben Hassine
* @author Minkuk Jo
*/
class JobBuilderTests {

Expand All @@ -65,6 +70,24 @@ void testListeners() throws Exception {

}

@Test
void testJobDescription() {
// given
ApplicationContext context = new AnnotationConfigApplicationContext(MyJobConfiguration.class);
JobRepository jobRepository = context.getBean(JobRepository.class);
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
Step step = new StepBuilder("step", jobRepository)
.tasklet((contribution, chunkContext) -> RepeatStatus.FINISHED, transactionManager)
.build();

// when
Job job = new JobBuilder("job", jobRepository).description("This is a test job").start(step).build();

// then
assertThat(job).isInstanceOf(SimpleJob.class);
assertThat(((SimpleJob) job).getDescription()).isEqualTo("This is a test job");
}

@Configuration
@EnableBatchProcessing
static class MyJobConfiguration {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2020-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.job.builder;

import org.junit.jupiter.api.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Minkuk Jo
*/
class JobXmlDescriptionTests {

@Test
void testJobDescriptionFromXml() {
// given
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"/org/springframework/batch/core/job/builder/JobXmlDescriptionTests-context.xml");

// when
Job job = context.getBean("job", Job.class);

// then
assertThat(job).isInstanceOf(SimpleJob.class);
assertThat(((SimpleJob) job).getDescription()).isEqualTo("A job that processes football data");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2020-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.job.builder;

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;

/**
* @author Minkuk Jo
*/
public class TestTasklet implements Tasklet {

@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
return RepeatStatus.FINISHED;
}

}
Comment on lines +23 to +33
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created this class expecting it to be used globally in my tests, but would it have been better to create it as an inner class in JobXmlDescriptionTests?

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

<bean id="job" class="org.springframework.batch.core.job.SimpleJob">
<property name="name" value="job"/>
<property name="description" value="A job that processes football data"/>
<property name="jobRepository" ref="jobRepository"/>
<property name="steps">
<list>
<ref bean="step1"/>
</list>
</property>
</bean>

<bean id="step1" class="org.springframework.batch.core.step.tasklet.TaskletStep">
<property name="tasklet" ref="tasklet"/>
<property name="jobRepository" ref="jobRepository"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>

<bean id="tasklet" class="org.springframework.batch.core.job.builder.TestTasklet"/>

<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:org/springframework/batch/core/schema-hsqldb.sql"/>
</jdbc:embedded-database>

<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="databaseType" value="hsql"/>
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

</beans>
2 changes: 2 additions & 0 deletions spring-batch-docs/modules/ROOT/pages/job/configuring.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Java::
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.description("A job that processes football data")
.start(playerLoad())
.next(gameLoad())
.next(playerSummarization())
Expand All @@ -38,6 +39,7 @@ The following example creates a `footballJob`:
[source, xml]
----
<job id="footballJob">
<description>A job that processes football data</description>
<step id="playerload" parent="s1" next="gameLoad"/>
<step id="gameLoad" parent="s2" next="playerSummarization"/>
<step id="playerSummarization" parent="s3"/>
Expand Down