diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java index 34d6d19f58..4ff5969f90 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java @@ -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. @@ -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 { @@ -96,6 +97,8 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In private BatchJobObservationConvention observationConvention = new DefaultBatchJobObservationConvention(); + private String description; + /** * Default constructor. */ @@ -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}. diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/builder/JobBuilderHelper.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/builder/JobBuilderHelper.java index dee585863e..0865d23f41 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/builder/JobBuilderHelper.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/builder/JobBuilderHelper.java @@ -46,6 +46,7 @@ * @author Dave Syer * @author Mahmoud Ben Hassine * @author Taeik Lim + * @author Minkuk Jo * @since 2.2 */ public abstract class JobBuilderHelper> { @@ -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; } @@ -221,6 +235,11 @@ protected void enhance(AbstractJob job) { job.setRestartable(restartable); } + String description = properties.getDescription(); + if (description != null) { + job.setDescription(description); + } + List listeners = properties.getJobExecutionListeners(); if (!listeners.isEmpty()) { job.setJobExecutionListeners(listeners.toArray(new JobExecutionListener[0])); @@ -245,6 +264,8 @@ public static class CommonJobProperties { private JobParametersValidator jobParametersValidator; + private String description; + public CommonJobProperties() { } @@ -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() { @@ -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; } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobBuilderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobBuilderTests.java index 9678a8e4cc..84535c3936 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobBuilderTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobBuilderTests.java @@ -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. @@ -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; @@ -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 { @@ -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 { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobXmlDescriptionTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobXmlDescriptionTests.java new file mode 100644 index 0000000000..1a82d98a7f --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/JobXmlDescriptionTests.java @@ -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"); + } + +} \ No newline at end of file diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/TestTasklet.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/TestTasklet.java new file mode 100644 index 0000000000..d5808eebd1 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/builder/TestTasklet.java @@ -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; + } + +} \ No newline at end of file diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/job/builder/JobXmlDescriptionTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/job/builder/JobXmlDescriptionTests-context.xml new file mode 100644 index 0000000000..15b0828f6e --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/job/builder/JobXmlDescriptionTests-context.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-docs/modules/ROOT/pages/job/configuring.adoc b/spring-batch-docs/modules/ROOT/pages/job/configuring.adoc index c7aaa78828..7c672126a0 100644 --- a/spring-batch-docs/modules/ROOT/pages/job/configuring.adoc +++ b/spring-batch-docs/modules/ROOT/pages/job/configuring.adoc @@ -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()) @@ -38,6 +39,7 @@ The following example creates a `footballJob`: [source, xml] ---- + A job that processes football data