Skip to content

Commit d8d7e0a

Browse files
committed
Deprecate default constructor on ConcurrentTaskExecutor/Scheduler
Includes revision of null Executor configuration. Respects TaskDecorator configuration on DefaultManagedTaskExecutor. Closes gh-27914
1 parent 73d30dd commit d8d7e0a

File tree

5 files changed

+95
-42
lines changed

5 files changed

+95
-42
lines changed

spring-context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskExecutor.java

+25-8
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@
6565
@SuppressWarnings("deprecation")
6666
public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
6767

68+
private static final Executor STUB_EXECUTOR = (task -> {
69+
throw new IllegalStateException("Executor not configured");
70+
});
71+
6872
@Nullable
6973
private static Class<?> managedExecutorServiceClass;
7074

@@ -80,15 +84,22 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
8084
}
8185
}
8286

83-
private Executor concurrentExecutor;
8487

85-
private TaskExecutorAdapter adaptedExecutor;
88+
private Executor concurrentExecutor = STUB_EXECUTOR;
89+
90+
private TaskExecutorAdapter adaptedExecutor = new TaskExecutorAdapter(STUB_EXECUTOR);
91+
92+
@Nullable
93+
private TaskDecorator taskDecorator;
8694

8795

8896
/**
8997
* Create a new ConcurrentTaskExecutor, using a single thread executor as default.
9098
* @see java.util.concurrent.Executors#newSingleThreadExecutor()
99+
* @deprecated in favor of {@link #ConcurrentTaskExecutor(Executor)} with an
100+
* externally provided Executor
91101
*/
102+
@Deprecated(since = "6.1")
92103
public ConcurrentTaskExecutor() {
93104
this.concurrentExecutor = Executors.newSingleThreadExecutor();
94105
this.adaptedExecutor = new TaskExecutorAdapter(this.concurrentExecutor);
@@ -101,8 +112,9 @@ public ConcurrentTaskExecutor() {
101112
* @param executor the {@link java.util.concurrent.Executor} to delegate to
102113
*/
103114
public ConcurrentTaskExecutor(@Nullable Executor executor) {
104-
this.concurrentExecutor = (executor != null ? executor : Executors.newSingleThreadExecutor());
105-
this.adaptedExecutor = getAdaptedExecutor(this.concurrentExecutor);
115+
if (executor != null) {
116+
setConcurrentExecutor(executor);
117+
}
106118
}
107119

108120

@@ -111,8 +123,8 @@ public ConcurrentTaskExecutor(@Nullable Executor executor) {
111123
* <p>Autodetects a JSR-236 {@link jakarta.enterprise.concurrent.ManagedExecutorService}
112124
* in order to expose {@link jakarta.enterprise.concurrent.ManagedTask} adapters for it.
113125
*/
114-
public final void setConcurrentExecutor(@Nullable Executor executor) {
115-
this.concurrentExecutor = (executor != null ? executor : Executors.newSingleThreadExecutor());
126+
public final void setConcurrentExecutor(Executor executor) {
127+
this.concurrentExecutor = executor;
116128
this.adaptedExecutor = getAdaptedExecutor(this.concurrentExecutor);
117129
}
118130

@@ -139,6 +151,7 @@ public final Executor getConcurrentExecutor() {
139151
* @since 4.3
140152
*/
141153
public final void setTaskDecorator(TaskDecorator taskDecorator) {
154+
this.taskDecorator = taskDecorator;
142155
this.adaptedExecutor.setTaskDecorator(taskDecorator);
143156
}
144157

@@ -175,11 +188,15 @@ public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
175188
}
176189

177190

178-
private static TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
191+
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
179192
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
180193
return new ManagedTaskExecutorAdapter(concurrentExecutor);
181194
}
182-
return new TaskExecutorAdapter(concurrentExecutor);
195+
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
196+
if (this.taskDecorator != null) {
197+
adapter.setTaskDecorator(this.taskDecorator);
198+
}
199+
return adapter;
183200
}
184201

185202

spring-context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskScheduler.java

+15-16
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,14 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
100100
* Create a new ConcurrentTaskScheduler,
101101
* using a single thread executor as default.
102102
* @see java.util.concurrent.Executors#newSingleThreadScheduledExecutor()
103+
* @deprecated in favor of {@link #ConcurrentTaskScheduler(ScheduledExecutorService)}
104+
* with an externally provided Executor
103105
*/
106+
@Deprecated(since = "6.1")
104107
public ConcurrentTaskScheduler() {
105108
super();
106-
this.scheduledExecutor = initScheduledExecutor(null);
109+
this.scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
110+
this.enterpriseConcurrentScheduler = false;
107111
}
108112

109113
/**
@@ -116,9 +120,11 @@ public ConcurrentTaskScheduler() {
116120
* to delegate to for {@link org.springframework.scheduling.SchedulingTaskExecutor}
117121
* as well as {@link TaskScheduler} invocations
118122
*/
119-
public ConcurrentTaskScheduler(ScheduledExecutorService scheduledExecutor) {
123+
public ConcurrentTaskScheduler(@Nullable ScheduledExecutorService scheduledExecutor) {
120124
super(scheduledExecutor);
121-
this.scheduledExecutor = initScheduledExecutor(scheduledExecutor);
125+
if (scheduledExecutor != null) {
126+
initScheduledExecutor(scheduledExecutor);
127+
}
122128
}
123129

124130
/**
@@ -134,21 +140,14 @@ public ConcurrentTaskScheduler(ScheduledExecutorService scheduledExecutor) {
134140
*/
135141
public ConcurrentTaskScheduler(Executor concurrentExecutor, ScheduledExecutorService scheduledExecutor) {
136142
super(concurrentExecutor);
137-
this.scheduledExecutor = initScheduledExecutor(scheduledExecutor);
143+
initScheduledExecutor(scheduledExecutor);
138144
}
139145

140146

141-
private ScheduledExecutorService initScheduledExecutor(@Nullable ScheduledExecutorService scheduledExecutor) {
142-
if (scheduledExecutor != null) {
143-
this.scheduledExecutor = scheduledExecutor;
144-
this.enterpriseConcurrentScheduler = (managedScheduledExecutorServiceClass != null &&
145-
managedScheduledExecutorServiceClass.isInstance(scheduledExecutor));
146-
}
147-
else {
148-
this.scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
149-
this.enterpriseConcurrentScheduler = false;
150-
}
151-
return this.scheduledExecutor;
147+
private void initScheduledExecutor(ScheduledExecutorService scheduledExecutor) {
148+
this.scheduledExecutor = scheduledExecutor;
149+
this.enterpriseConcurrentScheduler = (managedScheduledExecutorServiceClass != null &&
150+
managedScheduledExecutorServiceClass.isInstance(scheduledExecutor));
152151
}
153152

154153
/**
@@ -162,7 +161,7 @@ private ScheduledExecutorService initScheduledExecutor(@Nullable ScheduledExecut
162161
* as well, pass the same executor reference to {@link #setConcurrentExecutor}.
163162
* @see #setConcurrentExecutor
164163
*/
165-
public void setScheduledExecutor(@Nullable ScheduledExecutorService scheduledExecutor) {
164+
public void setScheduledExecutor(ScheduledExecutorService scheduledExecutor) {
166165
initScheduledExecutor(scheduledExecutor);
167166
}
168167

spring-context/src/main/java/org/springframework/scheduling/concurrent/DefaultManagedTaskExecutor.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -24,7 +24,6 @@
2424
import org.springframework.beans.factory.InitializingBean;
2525
import org.springframework.jndi.JndiLocatorDelegate;
2626
import org.springframework.jndi.JndiTemplate;
27-
import org.springframework.lang.Nullable;
2827

2928
/**
3029
* JNDI-based variant of {@link ConcurrentTaskExecutor}, performing a default lookup for
@@ -43,10 +42,15 @@ public class DefaultManagedTaskExecutor extends ConcurrentTaskExecutor implement
4342

4443
private final JndiLocatorDelegate jndiLocator = new JndiLocatorDelegate();
4544

46-
@Nullable
4745
private String jndiName = "java:comp/DefaultManagedExecutorService";
4846

4947

48+
public DefaultManagedTaskExecutor() {
49+
// Executor initialization happens in afterPropertiesSet
50+
super(null);
51+
}
52+
53+
5054
/**
5155
* Set the JNDI template to use for JNDI lookups.
5256
* @see org.springframework.jndi.JndiAccessor#setJndiTemplate
@@ -87,9 +91,7 @@ public void setJndiName(String jndiName) {
8791

8892
@Override
8993
public void afterPropertiesSet() throws NamingException {
90-
if (this.jndiName != null) {
91-
setConcurrentExecutor(this.jndiLocator.lookup(this.jndiName, Executor.class));
92-
}
94+
setConcurrentExecutor(this.jndiLocator.lookup(this.jndiName, Executor.class));
9395
}
9496

9597
}

spring-context/src/main/java/org/springframework/scheduling/concurrent/DefaultManagedTaskScheduler.java

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -24,7 +24,6 @@
2424
import org.springframework.beans.factory.InitializingBean;
2525
import org.springframework.jndi.JndiLocatorDelegate;
2626
import org.springframework.jndi.JndiTemplate;
27-
import org.springframework.lang.Nullable;
2827

2928
/**
3029
* JNDI-based variant of {@link ConcurrentTaskScheduler}, performing a default lookup for
@@ -43,10 +42,15 @@ public class DefaultManagedTaskScheduler extends ConcurrentTaskScheduler impleme
4342

4443
private final JndiLocatorDelegate jndiLocator = new JndiLocatorDelegate();
4544

46-
@Nullable
4745
private String jndiName = "java:comp/DefaultManagedScheduledExecutorService";
4846

4947

48+
public DefaultManagedTaskScheduler() {
49+
// Executor initialization happens in afterPropertiesSet
50+
super(null);
51+
}
52+
53+
5054
/**
5155
* Set the JNDI template to use for JNDI lookups.
5256
* @see org.springframework.jndi.JndiAccessor#setJndiTemplate
@@ -87,11 +91,9 @@ public void setJndiName(String jndiName) {
8791

8892
@Override
8993
public void afterPropertiesSet() throws NamingException {
90-
if (this.jndiName != null) {
91-
ScheduledExecutorService executor = this.jndiLocator.lookup(this.jndiName, ScheduledExecutorService.class);
92-
setConcurrentExecutor(executor);
93-
setScheduledExecutor(executor);
94-
}
94+
ScheduledExecutorService executor = this.jndiLocator.lookup(this.jndiName, ScheduledExecutorService.class);
95+
setConcurrentExecutor(executor);
96+
setScheduledExecutor(executor);
9597
}
9698

9799
}

spring-context/src/test/java/org/springframework/scheduling/concurrent/ConcurrentTaskExecutorTests.java

+37-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.scheduling.concurrent;
1818

19+
import java.util.concurrent.Executor;
1920
import java.util.concurrent.LinkedBlockingQueue;
2021
import java.util.concurrent.RunnableFuture;
2122
import java.util.concurrent.ThreadPoolExecutor;
@@ -25,6 +26,8 @@
2526
import org.junit.jupiter.api.Test;
2627

2728
import org.springframework.core.task.NoOpRunnable;
29+
import org.springframework.core.task.TaskDecorator;
30+
import org.springframework.util.Assert;
2831

2932
import static org.assertj.core.api.Assertions.assertThatCode;
3033

@@ -38,8 +41,8 @@ class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
3841
new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
3942

4043

41-
@Override
4244
@SuppressWarnings("deprecation")
45+
@Override
4346
protected org.springframework.core.task.AsyncListenableTaskExecutor buildExecutor() {
4447
concurrentExecutor.setThreadFactory(new CustomizableThreadFactory(this.threadNamePrefix));
4548
return new ConcurrentTaskExecutor(concurrentExecutor);
@@ -65,14 +68,44 @@ void zeroArgCtorResultsInDefaultTaskExecutorBeingUsed() {
6568
@Test
6669
void passingNullExecutorToCtorResultsInDefaultTaskExecutorBeingUsed() {
6770
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor(null);
71+
assertThatCode(() -> executor.execute(new NoOpRunnable())).hasMessage("Executor not configured");
72+
}
73+
74+
@Test
75+
void earlySetConcurrentExecutorCallRespectsConfiguredTaskDecorator() {
76+
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor();
77+
executor.setConcurrentExecutor(new DecoratedExecutor());
78+
executor.setTaskDecorator(new RunnableDecorator());
6879
assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
6980
}
7081

7182
@Test
72-
void passingNullExecutorToSetterResultsInDefaultTaskExecutorBeingUsed() {
83+
void lateSetConcurrentExecutorCallRespectsConfiguredTaskDecorator() {
7384
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor();
74-
executor.setConcurrentExecutor(null);
85+
executor.setTaskDecorator(new RunnableDecorator());
86+
executor.setConcurrentExecutor(new DecoratedExecutor());
7587
assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
7688
}
7789

90+
91+
private static class DecoratedRunnable implements Runnable {
92+
@Override
93+
public void run() {
94+
}
95+
}
96+
97+
private static class RunnableDecorator implements TaskDecorator {
98+
@Override
99+
public Runnable decorate(Runnable runnable) {
100+
return new DecoratedRunnable();
101+
}
102+
}
103+
104+
private static class DecoratedExecutor implements Executor {
105+
@Override
106+
public void execute(Runnable command) {
107+
Assert.state(command instanceof DecoratedRunnable, "TaskDecorator not applied");
108+
}
109+
}
110+
78111
}

0 commit comments

Comments
 (0)