Skip to content

Commit fc4a665

Browse files
committed
Make JobOperator extend JobLauncher
Related to #4832
1 parent adffc58 commit fc4a665

File tree

19 files changed

+105
-179
lines changed

19 files changed

+105
-179
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java

-23
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ class BatchRegistrar implements ImportBeanDefinitionRegistrar {
5252

5353
private static final String JOB_REPOSITORY = "jobRepository";
5454

55-
private static final String JOB_LAUNCHER = "jobLauncher";
56-
5755
private static final String JOB_REGISTRY = "jobRegistry";
5856

5957
private static final String JOB_LOADER = "jobLoader";
@@ -67,7 +65,6 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B
6765
.get(EnableBatchProcessing.class)
6866
.synthesize();
6967
registerJobRepository(registry, batchAnnotation);
70-
registerJobLauncher(registry, batchAnnotation);
7168
registerJobRegistry(registry);
7269
registerJobRegistrySmartInitializingSingleton(registry);
7370
registerJobOperator(registry, batchAnnotation);
@@ -147,25 +144,6 @@ private void registerJobRepository(BeanDefinitionRegistry registry, EnableBatchP
147144
registry.registerBeanDefinition(JOB_REPOSITORY, beanDefinitionBuilder.getBeanDefinition());
148145
}
149146

150-
private void registerJobLauncher(BeanDefinitionRegistry registry, EnableBatchProcessing batchAnnotation) {
151-
if (registry.containsBeanDefinition(JOB_LAUNCHER)) {
152-
LOGGER.info("Bean jobLauncher already defined in the application context, skipping"
153-
+ " the registration of a jobLauncher");
154-
return;
155-
}
156-
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
157-
.genericBeanDefinition(TaskExecutorJobLauncher.class);
158-
// set mandatory properties
159-
beanDefinitionBuilder.addPropertyReference(JOB_REPOSITORY, JOB_REPOSITORY);
160-
161-
// set optional properties
162-
String taskExecutorRef = batchAnnotation.taskExecutorRef();
163-
if (registry.containsBeanDefinition(taskExecutorRef)) {
164-
beanDefinitionBuilder.addPropertyReference("taskExecutor", taskExecutorRef);
165-
}
166-
registry.registerBeanDefinition(JOB_LAUNCHER, beanDefinitionBuilder.getBeanDefinition());
167-
}
168-
169147
private void registerJobRegistry(BeanDefinitionRegistry registry) {
170148
if (registry.containsBeanDefinition(JOB_REGISTRY)) {
171149
LOGGER.info("Bean jobRegistry already defined in the application context, skipping"
@@ -205,7 +183,6 @@ private void registerJobOperator(BeanDefinitionRegistry registry, EnableBatchPro
205183
beanDefinitionBuilder.addPropertyReference("transactionManager", transactionManagerRef);
206184

207185
beanDefinitionBuilder.addPropertyReference(JOB_REPOSITORY, JOB_REPOSITORY);
208-
beanDefinitionBuilder.addPropertyReference(JOB_LAUNCHER, JOB_LAUNCHER);
209186
beanDefinitionBuilder.addPropertyReference(JOB_REGISTRY, JOB_REGISTRY);
210187

211188
// set optional properties

spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java

+3-26
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.springframework.batch.core.launch.JobLauncher;
4040
import org.springframework.batch.core.launch.JobOperator;
4141
import org.springframework.batch.core.launch.support.JobOperatorFactoryBean;
42-
import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
4342
import org.springframework.batch.core.repository.ExecutionContextSerializer;
4443
import org.springframework.batch.core.repository.JobRepository;
4544
import org.springframework.batch.core.repository.dao.AbstractJdbcBatchMetadataDao;
@@ -145,27 +144,6 @@ public JobRepository jobRepository() throws BatchConfigurationException {
145144
}
146145
}
147146

148-
/**
149-
* Define a job launcher bean.
150-
* @param jobRepository the job repository
151-
* @return a job launcher
152-
* @throws BatchConfigurationException if unable to configure the default job launcher
153-
* @since 5.2
154-
*/
155-
@Bean
156-
public JobLauncher jobLauncher(JobRepository jobRepository) throws BatchConfigurationException {
157-
TaskExecutorJobLauncher taskExecutorJobLauncher = new TaskExecutorJobLauncher();
158-
taskExecutorJobLauncher.setJobRepository(jobRepository);
159-
taskExecutorJobLauncher.setTaskExecutor(getTaskExecutor());
160-
try {
161-
taskExecutorJobLauncher.afterPropertiesSet();
162-
return taskExecutorJobLauncher;
163-
}
164-
catch (Exception e) {
165-
throw new BatchConfigurationException("Unable to configure the default job launcher", e);
166-
}
167-
}
168-
169147
@Bean
170148
public JobRegistry jobRegistry() throws BatchConfigurationException {
171149
return new MapJobRegistry();
@@ -175,20 +153,19 @@ public JobRegistry jobRegistry() throws BatchConfigurationException {
175153
* Define a job operator bean.
176154
* @param jobRepository a job repository
177155
* @param jobRegistry a job registry
178-
* @param jobLauncher a job launcher
179156
* @return a job operator
180157
* @throws BatchConfigurationException if unable to configure the default job operator
181158
* @since 5.2
182159
*/
183160
@Bean
184-
public JobOperator jobOperator(JobRepository jobRepository, JobRegistry jobRegistry, JobLauncher jobLauncher)
161+
public JobOperator jobOperator(JobRepository jobRepository, JobRegistry jobRegistry)
185162
throws BatchConfigurationException {
186163
JobOperatorFactoryBean jobOperatorFactoryBean = new JobOperatorFactoryBean();
187-
jobOperatorFactoryBean.setTransactionManager(getTransactionManager());
188164
jobOperatorFactoryBean.setJobRepository(jobRepository);
189165
jobOperatorFactoryBean.setJobRegistry(jobRegistry);
190-
jobOperatorFactoryBean.setJobLauncher(jobLauncher);
166+
jobOperatorFactoryBean.setTransactionManager(getTransactionManager());
191167
jobOperatorFactoryBean.setJobParametersConverter(getJobParametersConverter());
168+
jobOperatorFactoryBean.setTaskExecutor(getTaskExecutor());
192169
try {
193170
jobOperatorFactoryBean.afterPropertiesSet();
194171
return jobOperatorFactoryBean.getObject();

spring-batch-core/src/main/java/org/springframework/batch/core/launch/JobOperator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2024 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@
4242
* @author Mahmoud Ben Hassine
4343
* @since 2.0
4444
*/
45-
public interface JobOperator {
45+
public interface JobOperator extends JobLauncher {
4646

4747
/**
4848
* List the {@link JobExecution JobExecutions} associated with a particular

spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/JobOperatorFactoryBean.java

+40-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2024 the original author or authors.
2+
* Copyright 2022-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,15 +17,20 @@
1717

1818
import java.util.Properties;
1919

20+
import io.micrometer.core.instrument.MeterRegistry;
21+
import io.micrometer.core.instrument.Metrics;
22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
2024
import org.springframework.aop.framework.ProxyFactory;
2125
import org.springframework.batch.core.configuration.JobRegistry;
2226
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
2327
import org.springframework.batch.core.converter.JobParametersConverter;
24-
import org.springframework.batch.core.launch.JobLauncher;
2528
import org.springframework.batch.core.launch.JobOperator;
2629
import org.springframework.batch.core.repository.JobRepository;
2730
import org.springframework.beans.factory.FactoryBean;
2831
import org.springframework.beans.factory.InitializingBean;
32+
import org.springframework.core.task.SyncTaskExecutor;
33+
import org.springframework.core.task.TaskExecutor;
2934
import org.springframework.transaction.PlatformTransactionManager;
3035
import org.springframework.transaction.TransactionManager;
3136
import org.springframework.transaction.annotation.Isolation;
@@ -46,6 +51,8 @@
4651
*/
4752
public class JobOperatorFactoryBean implements FactoryBean<JobOperator>, InitializingBean {
4853

54+
protected static final Log logger = LogFactory.getLog(JobOperatorFactoryBean.class);
55+
4956
private static final String TRANSACTION_ISOLATION_LEVEL_PREFIX = "ISOLATION_";
5057

5158
private static final String TRANSACTION_PROPAGATION_PREFIX = "PROPAGATION_";
@@ -56,20 +63,25 @@ public class JobOperatorFactoryBean implements FactoryBean<JobOperator>, Initial
5663

5764
private JobRegistry jobRegistry;
5865

59-
private JobLauncher jobLauncher;
60-
6166
private JobRepository jobRepository;
6267

6368
private JobParametersConverter jobParametersConverter = new DefaultJobParametersConverter();
6469

70+
private TaskExecutor taskExecutor;
71+
72+
private MeterRegistry meterRegistry = Metrics.globalRegistry;
73+
6574
private final ProxyFactory proxyFactory = new ProxyFactory();
6675

6776
@Override
6877
public void afterPropertiesSet() throws Exception {
69-
Assert.notNull(this.transactionManager, "TransactionManager must not be null");
70-
Assert.notNull(this.jobLauncher, "JobLauncher must not be null");
71-
Assert.notNull(this.jobRegistry, "JobRegistry must not be null");
7278
Assert.notNull(this.jobRepository, "JobRepository must not be null");
79+
Assert.notNull(this.jobRegistry, "JobRegistry must not be null");
80+
Assert.notNull(this.transactionManager, "TransactionManager must not be null");
81+
if (this.taskExecutor == null) {
82+
logger.info("No TaskExecutor has been set, defaulting to synchronous executor.");
83+
this.taskExecutor = new SyncTaskExecutor();
84+
}
7385
if (this.transactionAttributeSource == null) {
7486
Properties transactionAttributes = new Properties();
7587
String transactionProperties = String.join(",", TRANSACTION_PROPAGATION_PREFIX + Propagation.REQUIRED,
@@ -88,14 +100,6 @@ public void setJobRegistry(JobRegistry jobRegistry) {
88100
this.jobRegistry = jobRegistry;
89101
}
90102

91-
/**
92-
* Setter for the job launcher.
93-
* @param jobLauncher the job launcher to set
94-
*/
95-
public void setJobLauncher(JobLauncher jobLauncher) {
96-
this.jobLauncher = jobLauncher;
97-
}
98-
99103
/**
100104
* Setter for the job repository.
101105
* @param jobRepository the job repository to set
@@ -112,6 +116,25 @@ public void setJobParametersConverter(JobParametersConverter jobParametersConver
112116
this.jobParametersConverter = jobParametersConverter;
113117
}
114118

119+
/**
120+
* Set the TaskExecutor. (Optional)
121+
* @param taskExecutor instance of {@link TaskExecutor}.
122+
* @since 6.0
123+
*/
124+
public void setTaskExecutor(TaskExecutor taskExecutor) {
125+
this.taskExecutor = taskExecutor;
126+
}
127+
128+
/**
129+
* Set the meter registry to use for metrics. Defaults to
130+
* {@link Metrics#globalRegistry}.
131+
* @param meterRegistry the meter registry
132+
* @since 6.0
133+
*/
134+
public void setMeterRegistry(MeterRegistry meterRegistry) {
135+
this.meterRegistry = meterRegistry;
136+
}
137+
115138
/**
116139
* Setter for the transaction manager.
117140
* @param transactionManager the transaction manager to set
@@ -155,7 +178,8 @@ private SimpleJobOperator getTarget() throws Exception {
155178
SimpleJobOperator simpleJobOperator = new SimpleJobOperator();
156179
simpleJobOperator.setJobRegistry(this.jobRegistry);
157180
simpleJobOperator.setJobRepository(this.jobRepository);
158-
simpleJobOperator.setJobLauncher(this.jobLauncher);
181+
simpleJobOperator.setTaskExecutor(this.taskExecutor);
182+
simpleJobOperator.setMeterRegistry(this.meterRegistry);
159183
simpleJobOperator.setJobParametersConverter(this.jobParametersConverter);
160184
simpleJobOperator.afterPropertiesSet();
161185
return simpleJobOperator;

spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java

+6-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2024 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -86,17 +86,13 @@
8686
* @author Mahmoud Ben Hassine
8787
* @since 2.0
8888
*/
89-
public class SimpleJobOperator implements JobOperator, InitializingBean {
89+
public class SimpleJobOperator extends TaskExecutorJobLauncher implements JobOperator, InitializingBean {
9090

9191
private static final String ILLEGAL_STATE_MSG = "Illegal state (only happens on a race condition): "
9292
+ "%s with name=%s and parameters=%s";
9393

9494
private ListableJobLocator jobRegistry;
9595

96-
private JobLauncher jobLauncher;
97-
98-
private JobRepository jobRepository;
99-
10096
private JobParametersConverter jobParametersConverter = new DefaultJobParametersConverter();
10197

10298
private final Log logger = LogFactory.getLog(getClass());
@@ -108,9 +104,8 @@ public class SimpleJobOperator implements JobOperator, InitializingBean {
108104
*/
109105
@Override
110106
public void afterPropertiesSet() throws Exception {
111-
Assert.state(jobLauncher != null, "JobLauncher must be provided");
107+
super.afterPropertiesSet();
112108
Assert.state(jobRegistry != null, "JobLocator must be provided");
113-
Assert.state(jobRepository != null, "JobRepository must be provided");
114109
}
115110

116111
/**
@@ -129,18 +124,6 @@ public void setJobRegistry(ListableJobLocator jobRegistry) {
129124
this.jobRegistry = jobRegistry;
130125
}
131126

132-
public void setJobRepository(JobRepository jobRepository) {
133-
this.jobRepository = jobRepository;
134-
}
135-
136-
/**
137-
* Public setter for the {@link JobLauncher}.
138-
* @param jobLauncher the {@link JobLauncher} to set
139-
*/
140-
public void setJobLauncher(JobLauncher jobLauncher) {
141-
this.jobLauncher = jobLauncher;
142-
}
143-
144127
@Override
145128
public List<Long> getExecutions(long instanceId) throws NoSuchJobInstanceException {
146129
JobInstance jobInstance = jobRepository.getJobInstance(instanceId);
@@ -233,7 +216,7 @@ public Long restart(long executionId) throws JobInstanceAlreadyCompleteException
233216
logger.info(String.format("Attempting to resume job with name=%s and parameters=%s", jobName, parameters));
234217
}
235218
try {
236-
return jobLauncher.run(job, parameters).getId();
219+
return run(job, parameters).getId();
237220
}
238221
catch (JobExecutionAlreadyRunningException e) {
239222
throw new UnexpectedJobExecutionException(
@@ -263,7 +246,7 @@ public Long start(String jobName, Properties parameters)
263246
.info(String.format("Attempting to launch job with name=%s and parameters={%s}", jobName, parameters));
264247
}
265248
try {
266-
return jobLauncher.run(job, jobParameters).getId();
249+
return run(job, jobParameters).getId();
267250
}
268251
catch (JobExecutionAlreadyRunningException e) {
269252
throw new UnexpectedJobExecutionException(
@@ -293,7 +276,7 @@ public Long startNextInstance(String jobName)
293276
logger.info(String.format("Attempting to launch job with name=%s and parameters=%s", jobName, parameters));
294277
}
295278
try {
296-
return jobLauncher.run(job, parameters).getId();
279+
return run(job, parameters).getId();
297280
}
298281
catch (JobExecutionAlreadyRunningException e) {
299282
throw new UnexpectedJobExecutionException(

spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/TaskExecutorJobLauncher.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2024 the original author or authors.
2+
* Copyright 2022-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -70,13 +70,13 @@ public class TaskExecutorJobLauncher implements JobLauncher, InitializingBean {
7070

7171
protected static final Log logger = LogFactory.getLog(TaskExecutorJobLauncher.class);
7272

73-
private JobRepository jobRepository;
73+
protected JobRepository jobRepository;
7474

75-
private TaskExecutor taskExecutor;
75+
protected TaskExecutor taskExecutor;
7676

77-
private MeterRegistry meterRegistry = Metrics.globalRegistry;
77+
protected MeterRegistry meterRegistry = Metrics.globalRegistry;
7878

79-
private Counter jobLaunchCount; // NoopCounter is still incubating
79+
protected Counter jobLaunchCount; // NoopCounter is still incubating
8080

8181
/**
8282
* Run the provided job with the given {@link JobParameters}. The

spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/BatchRegistrarTests.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2024 the original author or authors.
2+
* Copyright 2022-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -77,7 +77,6 @@ void testConfigurationWithUserDefinedBeans() {
7777
var context = new AnnotationConfigApplicationContext(JobConfigurationWithUserDefinedInfrastructureBeans.class);
7878

7979
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRepository.class)).isMock());
80-
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobLauncher.class)).isMock());
8180
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRegistry.class)).isMock());
8281
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobOperator.class)).isMock());
8382
Assertions

0 commit comments

Comments
 (0)