Skip to content

Commit ece2afc

Browse files
authored
Bael 9487 bael 9552 (#18995)
* Update MyServiceImpl.java * Update AppConfig.java * Update MyService.java * Update LogAppender.java * Update SpringRetryIntegrationTest.java * Update SpringAsyncConfig.java * Update AsyncComponent.java * Update AsyncService.java * Update CustomAsyncExceptionHandler.java * Update FirstAsyncService.java * Update SecondAsyncService.java * Update AsyncAnnotationExampleIntegrationTest.java * Update AsyncWithXMLIntegrationTest.java * Update SpringRetryIntegrationTest.java * Update AsyncServiceUnitTest.java
1 parent eb86424 commit ece2afc

File tree

14 files changed

+309
-97
lines changed

14 files changed

+309
-97
lines changed
Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
package com.baeldung.async;
22

33
import org.springframework.scheduling.annotation.Async;
4-
import org.springframework.scheduling.annotation.AsyncResult;
54
import org.springframework.stereotype.Component;
6-
7-
import java.util.concurrent.Future;
5+
import java.util.concurrent.CompletableFuture;
86

97
@Component
108
public class AsyncComponent {
119

1210
@Async
1311
public void asyncMethodWithVoidReturnType() {
12+
System.out.println("Execute method asynchronously. "
13+
+ Thread.currentThread().getName());
1414
}
1515

1616
@Async
17-
public Future<String> asyncMethodWithReturnType() {
18-
try {
19-
Thread.sleep(5000);
20-
return new AsyncResult<>("hello world !!!!");
21-
} catch (final InterruptedException e) {
22-
23-
}
17+
public CompletableFuture<String> asyncMethodWithReturnType() {
18+
System.out.println("Execute method asynchronously - "
19+
+ Thread.currentThread().getName());
2420

25-
return null;
21+
try {
22+
Thread.sleep(5000);
23+
return CompletableFuture.completedFuture("hello world !!!!");
24+
} catch (InterruptedException e) {
25+
return CompletableFuture.failedFuture(e);
26+
}
2627
}
2728

2829
@Async("threadPoolTaskExecutor")
2930
public void asyncMethodWithConfiguredExecutor() {
31+
System.out.println("Execute method with configured executor - "
32+
+ Thread.currentThread().getName());
3033
}
3134

3235
@Async
3336
public void asyncMethodWithExceptions() throws Exception {
3437
throw new Exception("Throw message from asynchronous method. ");
3538
}
36-
3739
}

spring-scheduling/src/main/java/com/baeldung/async/AsyncService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
public class AsyncService {
1010

1111
@Autowired
12-
private FirstAsyncService fisrtService;
12+
private FirstAsyncService firstService;
1313
@Autowired
1414
private SecondAsyncService secondService;
1515

1616
public CompletableFuture<String> asyncMergeServicesResponse() throws InterruptedException {
17-
CompletableFuture<String> fisrtServiceResponse = fisrtService.asyncGetData();
17+
CompletableFuture<String> firstServiceResponse = firstService.asyncGetData();
1818
CompletableFuture<String> secondServiceResponse = secondService.asyncGetData();
1919

2020
// Merge responses from FirstAsyncService and SecondAsyncService
21-
return fisrtServiceResponse.thenCompose(fisrtServiceValue -> secondServiceResponse.thenApply(secondServiceValue -> fisrtServiceValue + secondServiceValue));
21+
return firstServiceResponse.thenCompose(firstServiceValue -> secondServiceResponse.thenApply(secondServiceValue -> firstServiceValue + secondServiceValue));
2222
}
2323
}

spring-scheduling/src/main/java/com/baeldung/async/CustomAsyncExceptionHandler.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandle
88

99
@Override
1010
public void handleUncaughtException(final Throwable throwable, final Method method, final Object... obj) {
11-
System.out.println("Exception message - " + throwable.getMessage());
12-
for (final Object param : obj) {
13-
System.out.println("Param - " + param);
11+
System.err.println("Async Exception Detected!");
12+
System.err.println("Exception message - " + throwable.getMessage());
13+
System.err.println("Method name - " + method.getName());
14+
for (Object param : obj) {
15+
System.err.println("Parameter value - " + param);
1416
}
1517
}
16-
1718
}
19+

spring-scheduling/src/main/java/com/baeldung/async/FirstAsyncService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,25 @@
33
import java.util.concurrent.CompletableFuture;
44

55
import org.springframework.scheduling.annotation.Async;
6-
import org.springframework.scheduling.annotation.AsyncResult;
76
import org.springframework.stereotype.Service;
87

98
@Service
109
public class FirstAsyncService {
1110

11+
/**
12+
* Executes asynchronously and returns a CompletableFuture holding the result.
13+
* The method directly uses CompletableFuture, avoiding the deprecated AsyncResult.
14+
* * @return A CompletableFuture containing the result string.
15+
* @throws InterruptedException if the sleep is interrupted.
16+
*/
1217
@Async
1318
public CompletableFuture<String> asyncGetData() throws InterruptedException {
19+
// Simulate a long-running task
1420
Thread.sleep(4000);
15-
return new AsyncResult<>(super.getClass().getSimpleName() + " response !!! ").completable();
21+
22+
// Return the result wrapped in a completed CompletableFuture
23+
return CompletableFuture.completedFuture(
24+
super.getClass().getSimpleName() + " response !!! "
25+
);
1626
}
17-
1827
}

spring-scheduling/src/main/java/com/baeldung/async/SecondAsyncService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,25 @@
33
import java.util.concurrent.CompletableFuture;
44

55
import org.springframework.scheduling.annotation.Async;
6-
import org.springframework.scheduling.annotation.AsyncResult;
76
import org.springframework.stereotype.Service;
87

98
@Service
109
public class SecondAsyncService {
1110

11+
/**
12+
* Executes asynchronously and returns a CompletableFuture holding the result.
13+
* The method directly uses CompletableFuture, avoiding the deprecated AsyncResult.
14+
* * @return A CompletableFuture containing the result string.
15+
* @throws InterruptedException if the sleep is interrupted.
16+
*/
1217
@Async
1318
public CompletableFuture<String> asyncGetData() throws InterruptedException {
19+
// Simulate a long-running task
1420
Thread.sleep(4000);
15-
return new AsyncResult<>(super.getClass().getSimpleName() + " response !!! ").completable();
21+
22+
// Return the result wrapped in a completed CompletableFuture
23+
return CompletableFuture.completedFuture(
24+
super.getClass().getSimpleName() + " response !!! "
25+
);
1626
}
17-
1827
}

spring-scheduling/src/main/java/com/baeldung/async/config/SpringAsyncConfig.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,44 @@
1212
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
1313

1414
@Configuration
15-
@EnableAsync()
15+
@EnableAsync
1616
@ComponentScan("com.baeldung.async")
1717
public class SpringAsyncConfig implements AsyncConfigurer {
1818

19+
/**
20+
* Defines a custom Executor used by @Async("threadPoolTaskExecutor") calls.
21+
*/
1922
@Bean(name = "threadPoolTaskExecutor")
2023
public Executor threadPoolTaskExecutor() {
21-
return new ThreadPoolTaskExecutor();
24+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
25+
executor.setCorePoolSize(5);
26+
executor.setMaxPoolSize(10);
27+
executor.setQueueCapacity(25);
28+
executor.setThreadNamePrefix("CustomPool-");
29+
executor.initialize();
30+
return executor;
2231
}
2332

33+
/**
34+
* Defines the default Executor used by un-named @Async calls.
35+
*/
2436
@Override
2537
public Executor getAsyncExecutor() {
26-
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
27-
threadPoolTaskExecutor.initialize();
28-
return threadPoolTaskExecutor;
38+
// You could return the named bean here, or create a new one.
39+
// Creating a new one for demonstration purposes.
40+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
41+
executor.setCorePoolSize(2);
42+
executor.setMaxPoolSize(4);
43+
executor.setThreadNamePrefix("DefaultAsync-");
44+
executor.initialize();
45+
return executor;
2946
}
3047

48+
/**
49+
* Defines the exception handler for asynchronous method calls.
50+
*/
3151
@Override
3252
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
3353
return new CustomAsyncExceptionHandler();
3454
}
35-
}
55+
}

spring-scheduling/src/main/java/com/baeldung/springretry/AppConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
import org.springframework.retry.annotation.EnableRetry;
88
import org.springframework.retry.backoff.FixedBackOffPolicy;
99
import org.springframework.retry.support.RetryTemplate;
10-
10+
import org.springframework.resilience.annotation.EnableResilientMethods;
1111

1212
@Configuration
1313
@ComponentScan(basePackages = "com.baeldung.springretry")
1414
@EnableRetry
15+
@EnableResilientMethods
1516
@PropertySource("classpath:retryConfig.properties")
1617
public class AppConfig {
1718

spring-scheduling/src/main/java/com/baeldung/springretry/MyService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import org.springframework.retry.annotation.Backoff;
66
import org.springframework.retry.annotation.Recover;
77
import org.springframework.retry.annotation.Retryable;
8-
8+
import org.springframework.resilience.annotation.ConcurrencyLimit;
99

1010
public interface MyService {
1111

@@ -26,4 +26,8 @@ public interface MyService {
2626
void recover(SQLException e, String sql);
2727

2828
void templateRetryService();
29+
30+
// **NEW Method with Concurrency Limit**
31+
@ConcurrencyLimit(5)
32+
void concurrentLimitService();
2933
}

spring-scheduling/src/main/java/com/baeldung/springretry/MyServiceImpl.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77
import org.springframework.retry.support.RetrySynchronizationManager;
88
import org.springframework.stereotype.Service;
99
import org.apache.commons.lang3.StringUtils;
10-
10+
import org.springframework.resilience.annotation.ConcurrencyLimit;
1111
@Service
1212
public class MyServiceImpl implements MyService {
1313

1414
private static final Logger logger = LoggerFactory.getLogger(MyServiceImpl.class);
1515

1616
@Override
1717
public void retryService() {
18+
// This is correct for logging the retry count (0 on first attempt, 1 on first retry, etc.)
1819
logger.info("Retry Number: " + RetrySynchronizationManager.getContext()
1920
.getRetryCount());
2021
logger.info("throw RuntimeException in method retryService()");
22+
// Always throws the exception to force the retry aspect to kick in
2123
throw new RuntimeException();
2224
}
2325

@@ -33,6 +35,7 @@ public void retryServiceWithRecovery(String sql) throws SQLException {
3335
public void retryServiceWithCustomization(String sql) throws SQLException {
3436
if (StringUtils.isEmpty(sql)) {
3537
logger.info("throw SQLException in method retryServiceWithCustomization()");
38+
// Correctly throws SQLException to trigger the 2 retry attempts
3639
throw new SQLException();
3740
}
3841
}
@@ -55,4 +58,19 @@ public void templateRetryService() {
5558
logger.info("throw RuntimeException in method templateRetryService()");
5659
throw new RuntimeException();
5760
}
61+
62+
// **NEW Implementation for Concurrency Limit**
63+
@Override
64+
@ConcurrencyLimit(5)
65+
public void concurrentLimitService() {
66+
// The ConcurrencyLimit aspect will wrap this method.
67+
logger.info("Concurrency Limit Active. Current Thread: " + Thread.currentThread().getName());
68+
// Simulate a time-consuming task to observe throttling
69+
try {
70+
Thread.sleep(1000); // Correctly blocks to hold the lock
71+
} catch (InterruptedException e) {
72+
Thread.currentThread().interrupt();
73+
}
74+
logger.info("Concurrency Limit Released. Current Thread: " + Thread.currentThread().getName());
75+
}
5876
}

spring-scheduling/src/main/java/com/baeldung/springretry/logging/LogAppender.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
8-
8+
import ch.qos.logback.classic.spi.ILoggingEvent;
99
import ch.qos.logback.core.AppenderBase;
1010

11-
public class LogAppender extends AppenderBase<ch.qos.logback.classic.spi.ILoggingEvent> {
11+
public class LogAppender extends AppenderBase<ILoggingEvent> {
1212

1313
private static final Logger logger = LoggerFactory.getLogger(LogAppender.class);
1414
private static final List<String> logMessages = new ArrayList<>();
@@ -22,4 +22,12 @@ protected void append(ch.qos.logback.classic.spi.ILoggingEvent eventObject) {
2222
public static List<String> getLogMessages() {
2323
return logMessages;
2424
}
25+
26+
/**
27+
* Clears the static list of captured log messages.
28+
* Used in test setup (@Before) to ensure a clean slate for each test case.
29+
*/
30+
public static void clearLogMessages() {
31+
logMessages.clear();
32+
}
2533
}

0 commit comments

Comments
 (0)