Skip to content

Commit 3bfcc82

Browse files
authored
Merge pull request #1686 from steve-community/refactor_executors
refactor task/job executors
2 parents 0b0745b + 535d87c commit 3bfcc82

9 files changed

+164
-80
lines changed

src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.fasterxml.jackson.databind.DeserializationFeature;
2222
import com.fasterxml.jackson.databind.ObjectMapper;
2323
import com.fasterxml.jackson.databind.SerializationFeature;
24-
import com.google.common.util.concurrent.ThreadFactoryBuilder;
2524
import com.mysql.cj.conf.PropertyKey;
2625
import com.zaxxer.hikari.HikariConfig;
2726
import com.zaxxer.hikari.HikariDataSource;
@@ -45,6 +44,8 @@
4544
import org.springframework.http.converter.HttpMessageConverter;
4645
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
4746
import org.springframework.scheduling.annotation.EnableScheduling;
47+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
48+
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
4849
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
4950
import org.springframework.web.accept.ContentNegotiationManager;
5051
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -55,16 +56,10 @@
5556
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
5657
import org.springframework.web.servlet.view.InternalResourceViewResolver;
5758

58-
import jakarta.annotation.PreDestroy;
5959
import jakarta.validation.Validator;
6060

6161
import javax.sql.DataSource;
6262
import java.util.List;
63-
import java.util.concurrent.ExecutorService;
64-
import java.util.concurrent.ScheduledExecutorService;
65-
import java.util.concurrent.ScheduledThreadPoolExecutor;
66-
import java.util.concurrent.ThreadFactory;
67-
import java.util.concurrent.TimeUnit;
6863

6964
import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
7065

@@ -81,8 +76,6 @@
8176
@ComponentScan("de.rwth.idsg.steve")
8277
public class BeanConfiguration implements WebMvcConfigurer {
8378

84-
private ScheduledThreadPoolExecutor executor;
85-
8679
/**
8780
* https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
8881
*/
@@ -144,13 +137,28 @@ public DSLContext dslContext(DataSource dataSource) {
144137
return DSL.using(conf);
145138
}
146139

147-
@Bean
148-
public ScheduledExecutorService scheduledExecutorService() {
149-
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("SteVe-Executor-%d")
150-
.build();
140+
@Bean(destroyMethod = "close")
141+
public DelegatingTaskScheduler asyncTaskScheduler() {
142+
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
143+
scheduler.setPoolSize(5);
144+
scheduler.setThreadNamePrefix("SteVe-TaskScheduler-");
145+
scheduler.setWaitForTasksToCompleteOnShutdown(true);
146+
scheduler.setAwaitTerminationSeconds(30);
147+
scheduler.initialize();
151148

152-
executor = new ScheduledThreadPoolExecutor(5, threadFactory);
153-
return executor;
149+
return new DelegatingTaskScheduler(scheduler);
150+
}
151+
152+
@Bean(destroyMethod = "close")
153+
public DelegatingTaskExecutor asyncTaskExecutor() {
154+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
155+
executor.setCorePoolSize(5);
156+
executor.setThreadNamePrefix("SteVe-TaskExecutor-");
157+
executor.setWaitForTasksToCompleteOnShutdown(true);
158+
executor.setAwaitTerminationSeconds(30);
159+
executor.initialize();
160+
161+
return new DelegatingTaskExecutor(executor);
154162
}
155163

156164
@Bean
@@ -173,29 +181,6 @@ public ReleaseCheckService releaseCheckService() {
173181
}
174182
}
175183

176-
@PreDestroy
177-
public void shutDown() {
178-
if (executor != null) {
179-
gracefulShutDown(executor);
180-
}
181-
}
182-
183-
private void gracefulShutDown(ExecutorService executor) {
184-
try {
185-
executor.shutdown();
186-
executor.awaitTermination(30, TimeUnit.SECONDS);
187-
188-
} catch (InterruptedException e) {
189-
log.error("Termination interrupted", e);
190-
191-
} finally {
192-
if (!executor.isTerminated()) {
193-
log.warn("Killing non-finished tasks");
194-
}
195-
executor.shutdownNow();
196-
}
197-
}
198-
199184
// -------------------------------------------------------------------------
200185
// Web config
201186
// -------------------------------------------------------------------------
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
3+
* Copyright (C) 2013-2025 SteVe Community Team
4+
* All Rights Reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package de.rwth.idsg.steve.config;
20+
21+
import lombok.RequiredArgsConstructor;
22+
import lombok.extern.slf4j.Slf4j;
23+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
24+
25+
import java.io.Closeable;
26+
import java.io.IOException;
27+
28+
/**
29+
* @author Sevket Goekay <[email protected]>
30+
* @since 02.02.2025
31+
*/
32+
@Slf4j
33+
@RequiredArgsConstructor
34+
public class DelegatingTaskExecutor implements Closeable {
35+
36+
private final ThreadPoolTaskExecutor delegate;
37+
38+
@Override
39+
public void close() throws IOException {
40+
log.info("Shutting down");
41+
delegate.shutdown();
42+
}
43+
44+
public void execute(Runnable task) {
45+
delegate.execute(task);
46+
}
47+
48+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
3+
* Copyright (C) 2013-2025 SteVe Community Team
4+
* All Rights Reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package de.rwth.idsg.steve.config;
20+
21+
import lombok.RequiredArgsConstructor;
22+
import lombok.extern.slf4j.Slf4j;
23+
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
24+
25+
import java.io.Closeable;
26+
import java.io.IOException;
27+
import java.time.Duration;
28+
import java.time.Instant;
29+
import java.util.concurrent.ScheduledFuture;
30+
31+
/**
32+
* @author Sevket Goekay <[email protected]>
33+
* @since 02.02.2025
34+
*/
35+
@Slf4j
36+
@RequiredArgsConstructor
37+
public class DelegatingTaskScheduler implements Closeable {
38+
39+
private final ThreadPoolTaskScheduler delegate;
40+
41+
@Override
42+
public void close() throws IOException {
43+
log.info("Shutting down");
44+
delegate.shutdown();
45+
}
46+
47+
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
48+
return delegate.scheduleAtFixedRate(task, startTime, period);
49+
}
50+
51+
}

src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
5151
@Autowired private Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint;
5252

5353
public static final String PATH_INFIX = "/websocket/CentralSystemService/";
54-
public static final long PING_INTERVAL = TimeUnit.MINUTES.toMinutes(15);
54+
public static final Duration PING_INTERVAL = Duration.ofMinutes(15);
5555
public static final Duration IDLE_TIMEOUT = Duration.ofHours(2);
5656
public static final int MAX_MSG_SIZE = 8_388_608; // 8 MB for max message size
5757

src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package de.rwth.idsg.steve.ocpp.soap;
2020

21+
import de.rwth.idsg.steve.config.DelegatingTaskExecutor;
2122
import de.rwth.idsg.steve.ocpp.OcppProtocol;
2223
import de.rwth.idsg.steve.repository.OcppServerRepository;
2324
import de.rwth.idsg.steve.repository.impl.ChargePointRepositoryImpl;
@@ -41,7 +42,6 @@
4142

4243
import javax.xml.namespace.QName;
4344
import java.util.Optional;
44-
import java.util.concurrent.ScheduledExecutorService;
4545

4646
import static org.apache.cxf.ws.addressing.JAXWSAConstants.ADDRESSING_PROPERTIES_INBOUND;
4747

@@ -62,7 +62,7 @@ public class MessageHeaderInterceptor extends AbstractPhaseInterceptor<Message>
6262

6363
@Autowired private OcppServerRepository ocppServerRepository;
6464
@Autowired private ChargePointHelperService chargePointHelperService;
65-
@Autowired private ScheduledExecutorService executorService;
65+
@Autowired private DelegatingTaskExecutor asyncTaskExecutor;
6666

6767
private static final String BOOT_OPERATION_NAME = "BootNotification";
6868
private static final String CHARGEBOX_ID_HEADER = "ChargeBoxIdentity";
@@ -93,7 +93,7 @@ public void handleMessage(Message message) throws Fault {
9393
// 2. update endpoint
9494
// -------------------------------------------------------------------------
9595

96-
executorService.execute(() -> {
96+
asyncTaskExecutor.execute(() -> {
9797
try {
9898
String endpointAddress = getEndpointAddress(message);
9999
if (endpointAddress != null) {

src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.google.common.base.Strings;
2222
import de.rwth.idsg.steve.config.WebSocketConfiguration;
23+
import de.rwth.idsg.steve.config.DelegatingTaskScheduler;
2324
import de.rwth.idsg.steve.ocpp.OcppTransport;
2425
import de.rwth.idsg.steve.ocpp.OcppVersion;
2526
import de.rwth.idsg.steve.ocpp.ws.data.CommunicationContext;
@@ -39,14 +40,13 @@
3940
import org.springframework.web.socket.WebSocketMessage;
4041
import org.springframework.web.socket.WebSocketSession;
4142

43+
import java.time.Instant;
4244
import java.util.ArrayList;
4345
import java.util.Collections;
4446
import java.util.Deque;
4547
import java.util.List;
4648
import java.util.Map;
47-
import java.util.concurrent.ScheduledExecutorService;
4849
import java.util.concurrent.ScheduledFuture;
49-
import java.util.concurrent.TimeUnit;
5050
import java.util.function.Consumer;
5151

5252
/**
@@ -55,7 +55,7 @@
5555
*/
5656
public abstract class AbstractWebSocketEndpoint extends ConcurrentWebSocketHandler implements SubProtocolCapable {
5757

58-
@Autowired private ScheduledExecutorService service;
58+
@Autowired private DelegatingTaskScheduler asyncTaskScheduler;
5959
@Autowired private OcppServerRepository ocppServerRepository;
6060
@Autowired private FutureResponseContextStore futureResponseContextStore;
6161
@Autowired private ApplicationEventPublisher applicationEventPublisher;
@@ -131,11 +131,11 @@ public void onOpen(WebSocketSession session) throws Exception {
131131

132132
// Just to keep the connection alive, such that the servers do not close
133133
// the connection because of a idle timeout, we ping-pong at fixed intervals.
134-
ScheduledFuture pingSchedule = service.scheduleAtFixedRate(
134+
ScheduledFuture pingSchedule = asyncTaskScheduler.scheduleAtFixedRate(
135135
new PingTask(chargeBoxId, session),
136-
WebSocketConfiguration.PING_INTERVAL,
137-
WebSocketConfiguration.PING_INTERVAL,
138-
TimeUnit.MINUTES);
136+
Instant.now().plus(WebSocketConfiguration.PING_INTERVAL),
137+
WebSocketConfiguration.PING_INTERVAL
138+
);
139139

140140
futureResponseContextStore.addSession(session);
141141

src/main/java/de/rwth/idsg/steve/service/BackgroundService.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
*/
1919
package de.rwth.idsg.steve.service;
2020

21+
import de.rwth.idsg.steve.config.DelegatingTaskExecutor;
2122
import de.rwth.idsg.steve.repository.dto.ChargePointSelect;
2223
import lombok.AccessLevel;
2324
import lombok.RequiredArgsConstructor;
2425

2526
import java.util.List;
26-
import java.util.concurrent.ExecutorService;
2727
import java.util.function.Consumer;
2828

2929
/**
@@ -32,10 +32,11 @@
3232
*/
3333
@RequiredArgsConstructor
3434
public class BackgroundService {
35-
private final ExecutorService executorService;
3635

37-
public static BackgroundService with(ExecutorService executorService) {
38-
return new BackgroundService(executorService);
36+
private final DelegatingTaskExecutor asyncTaskExecutor;
37+
38+
public static BackgroundService with(DelegatingTaskExecutor asyncTaskExecutor) {
39+
return new BackgroundService(asyncTaskExecutor);
3940
}
4041

4142
public Runner forFirst(List<ChargePointSelect> list) {
@@ -56,7 +57,7 @@ private class BackgroundSingleRunner implements Runner {
5657

5758
@Override
5859
public void execute(Consumer<ChargePointSelect> consumer) {
59-
executorService.execute(() -> consumer.accept(cps));
60+
asyncTaskExecutor.execute(() -> consumer.accept(cps));
6061
}
6162
}
6263

@@ -66,7 +67,7 @@ private class BackgroundListRunner implements Runner {
6667

6768
@Override
6869
public void execute(Consumer<ChargePointSelect> consumer) {
69-
executorService.execute(() -> list.forEach(consumer));
70+
asyncTaskExecutor.execute(() -> list.forEach(consumer));
7071
}
7172
}
7273
}

0 commit comments

Comments
 (0)