Skip to content

Commit 214b14b

Browse files
committed
#367: replaced homebrew async result handling with CompletableFuture
1 parent 30f3923 commit 214b14b

File tree

17 files changed

+10367
-239
lines changed

17 files changed

+10367
-239
lines changed

modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import org.jetbrains.annotations.NotNull;
88
import org.jetbrains.annotations.Nullable;
99
import org.simplejavamail.api.internal.batchsupport.LifecycleDelegatingTransport;
10-
import org.simplejavamail.api.mailer.AsyncResponse;
1110
import org.simplejavamail.api.mailer.config.OperationalConfig;
1211
import org.simplejavamail.internal.batchsupport.concurrent.NonJvmBlockingThreadPoolExecutor;
1312
import org.simplejavamail.internal.modules.BatchModule;
@@ -17,6 +16,7 @@
1716
import org.slf4j.LoggerFactory;
1817

1918
import java.util.UUID;
19+
import java.util.concurrent.CompletableFuture;
2020
import java.util.concurrent.ExecutorService;
2121
import java.util.concurrent.Future;
2222
import java.util.concurrent.FutureTask;
@@ -41,9 +41,8 @@ public class BatchSupport implements BatchModule {
4141
/**
4242
* @see BatchModule#executeAsync(String, Runnable)
4343
*/
44-
@NotNull
4544
@Override
46-
public AsyncResponse executeAsync(@NotNull final String processName, @NotNull final Runnable operation) {
45+
public CompletableFuture<Void> executeAsync(@NotNull final String processName, @NotNull final Runnable operation) {
4746
return AsyncOperationHelper.executeAsync(processName, operation);
4847
}
4948

@@ -52,7 +51,7 @@ public AsyncResponse executeAsync(@NotNull final String processName, @NotNull fi
5251
*/
5352
@NotNull
5453
@Override
55-
public AsyncResponse executeAsync(@NotNull final ExecutorService executorService, @NotNull final String processName, @NotNull final Runnable operation) {
54+
public CompletableFuture<Void> executeAsync(@NotNull final ExecutorService executorService, @NotNull final String processName, @NotNull final Runnable operation) {
5655
return AsyncOperationHelper.executeAsync(executorService, processName, operation);
5756
}
5857

modules/core-module/src/main/java/org/simplejavamail/api/mailer/AsyncResponse.java

-38
This file was deleted.

modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java

+7-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.simplejavamail.api.mailer.config.ServerConfig;
1414
import org.simplejavamail.api.mailer.config.TransportStrategy;
1515

16+
import java.util.concurrent.CompletableFuture;
1617
import java.util.concurrent.Future;
1718

1819
/**
@@ -48,16 +49,16 @@ public interface Mailer {
4849
* <p>
4950
* Note: synchronizes on the thread for sending mails so that we don't get into race condition conflicts with emails actually being sent.
5051
*
51-
* @return An AsyncResponse in case of async == true, otherwise <code>null</code>.
52+
* @return A {@link CompletableFuture} that is completed immediately if not <em>async</em>.
5253
*/
53-
AsyncResponse testConnection(boolean async);
54+
@NotNull CompletableFuture<Void> testConnection(boolean async);
5455

5556
/**
5657
* Delegates to {@link #sendMail(Email, boolean)}, with <code>async = false</code>. This method returns only when the email has been processed by
5758
* the target SMTP server.
58-
* @return AsyncResponse if the email was configured to be sent asynchronously.
59+
* @return A {@link CompletableFuture} that is completed immediately if not <em>async</em>.
5960
*/
60-
@Nullable AsyncResponse sendMail(Email email);
61+
@NotNull CompletableFuture<Void> sendMail(Email email);
6162

6263
/**
6364
* Processes an {@link Email} instance into a completely configured {@link Message}.
@@ -78,14 +79,12 @@ public interface Mailer {
7879
* @param email The information for the email to be sent.
7980
* @param async If false, this method blocks until the mail has been processed completely by the SMTP server. If true, a new thread is started to
8081
* send the email and this method returns immediately.
81-
* @return A {@link AsyncResponse} or null if not <em>async</em>.
82+
* @return A {@link CompletableFuture} that is completed immediately if not <em>async</em>.
8283
* @throws MailException Can be thrown if an email isn't validating correctly, or some other problem occurs during connection, sending etc.
8384
* @see java.util.concurrent.Executors#newFixedThreadPool(int)
8485
* @see #validate(Email)
8586
*/
86-
@Nullable
87-
// FIXME replace with Optional when Java 8?
88-
AsyncResponse sendMail(Email email, @SuppressWarnings("SameParameterValue") boolean async);
87+
@NotNull CompletableFuture<Void> sendMail(Email email, @SuppressWarnings("SameParameterValue") boolean async);
8988

9089
/**
9190
* Validates an {@link Email} instance. Validation fails if the subject is missing, content is missing, or no recipients are defined or that

modules/core-module/src/main/java/org/simplejavamail/internal/modules/BatchModule.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
import jakarta.mail.Transport;
55
import org.jetbrains.annotations.NotNull;
66
import org.simplejavamail.api.internal.batchsupport.LifecycleDelegatingTransport;
7-
import org.simplejavamail.api.mailer.AsyncResponse;
87
import org.simplejavamail.api.mailer.config.OperationalConfig;
98

109
import java.util.UUID;
10+
import java.util.concurrent.CompletableFuture;
1111
import java.util.concurrent.ExecutorService;
1212
import java.util.concurrent.Future;
1313

@@ -23,14 +23,13 @@ public interface BatchModule {
2323
*
2424
* @see java.util.concurrent.Executors#newSingleThreadExecutor()
2525
*/
26-
@NotNull
27-
AsyncResponse executeAsync(@NotNull String processName, @NotNull Runnable operation);
26+
CompletableFuture<Void> executeAsync(@NotNull String processName, @NotNull Runnable operation);
2827

2928
/**
3029
* Executes using the given ExecutorService, which is left running after the thread finishes running.
3130
*/
3231
@NotNull
33-
AsyncResponse executeAsync(@NotNull ExecutorService executorService, @NotNull String processName, @NotNull Runnable operation);
32+
CompletableFuture<Void> executeAsync(@NotNull ExecutorService executorService, @NotNull String processName, @NotNull Runnable operation);
3433

3534
/**
3635
* @return A NonJvmBlockingThreadPoolExecutor instance that by default doesn't block the JVM from exiting
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,41 @@
11
package org.simplejavamail.internal.util.concurrent;
22

33
import org.jetbrains.annotations.NotNull;
4-
import org.simplejavamail.api.mailer.AsyncResponse;
5-
import org.slf4j.Logger;
64

5+
import java.util.concurrent.CompletableFuture;
76
import java.util.concurrent.ExecutorService;
87
import java.util.concurrent.Executors;
9-
import java.util.concurrent.atomic.AtomicReference;
108

9+
import static java.util.concurrent.CompletableFuture.runAsync;
1110
import static java.util.concurrent.Executors.newSingleThreadExecutor;
1211
import static org.simplejavamail.internal.util.Preconditions.assumeTrue;
13-
import static org.slf4j.LoggerFactory.getLogger;
1412

1513
/**
16-
* Util that facilitates running a concurrent operation while supporting {@link AsyncResponse}.
14+
* Util that facilitates running a concurrent operation with CompletableFuture support.
1715
*/
1816
@SuppressWarnings("SameParameterValue")
1917
public class AsyncOperationHelper {
2018

21-
private static final Logger LOGGER = getLogger(AsyncOperationHelper.class);
22-
19+
// TODO Lombok
2320
private AsyncOperationHelper() {
2421
}
2522

2623
/**
27-
* Executes using a single-execution ExecutorService, which shutdown immediately after the thread finishes.
24+
* Executes using a single-execution ExecutorService, which is shutdown immediately after the operation finishes.
2825
*
2926
* @see Executors#newSingleThreadExecutor()
3027
*/
31-
public static AsyncResponse executeAsync(final @NotNull String processName,
32-
final @NotNull Runnable operation) {
33-
return executeAsync(newSingleThreadExecutor(), processName, operation, true);
28+
public static CompletableFuture<Void> executeAsync(final @NotNull String processName, final @NotNull Runnable operation) {
29+
final ExecutorService executorService = newSingleThreadExecutor();
30+
return runAsync(new NamedRunnable(processName, operation), executorService)
31+
.thenRun(executorService::shutdown);
3432
}
3533

3634
/**
3735
* Executes using the given ExecutorService, which is left running after the thread finishes running.
38-
*
39-
* @see Executors#newSingleThreadExecutor()
4036
*/
41-
public static AsyncResponse executeAsync(final @NotNull ExecutorService executorService,
42-
final @NotNull String processName,
43-
final @NotNull Runnable operation) {
44-
return executeAsync(executorService, processName, operation, false);
45-
}
46-
47-
private static AsyncResponse executeAsync(final @NotNull ExecutorService executorService,
48-
final @NotNull String processName,
49-
final @NotNull Runnable operation,
50-
final boolean shutDownExecutorService) {
51-
// atomic reference is needed to be able to smuggle the asyncResponse
52-
// into the Runnable which is passed itself to the asyncResponse.
53-
final AtomicReference<AsyncResponseImpl> asyncResponseRef = new AtomicReference<>();
37+
public static CompletableFuture<Void> executeAsync(final @NotNull ExecutorService executorService, final @NotNull String processName, final @NotNull Runnable operation) {
5438
assumeTrue(!executorService.isShutdown(), "cannot send async email, executor service is already shut down!");
55-
asyncResponseRef.set(new AsyncResponseImpl(executorService.submit(new NamedRunnable(processName) {
56-
@Override
57-
public void run() {
58-
// by the time the code reaches here, the user would have configured the appropriate handlers
59-
try {
60-
operation.run();
61-
asyncResponseRef.get().delegateSuccessHandling();
62-
} catch (Exception e) {
63-
LOGGER.error("Failed to run " + processName, e);
64-
asyncResponseRef.get().delegateExceptionHandling(e);
65-
throw e; // trigger the returned Future's exception handle
66-
} finally {
67-
if (shutDownExecutorService) {
68-
executorService.shutdown();
69-
}
70-
}
71-
}
72-
73-
})));
74-
return asyncResponseRef.get();
39+
return runAsync(new NamedRunnable(processName, operation), executorService);
7540
}
76-
}
41+
}

modules/core-module/src/main/java/org/simplejavamail/internal/util/concurrent/AsyncResponseImpl.java

-68
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,40 @@
11
package org.simplejavamail.internal.util.concurrent;
22

3-
public abstract class NamedRunnable implements Runnable {
3+
import org.jetbrains.annotations.NotNull;
4+
import org.slf4j.Logger;
45

5-
private final String name;
6+
import static org.slf4j.LoggerFactory.getLogger;
67

7-
protected NamedRunnable(final String name) {
8-
this.name = name;
8+
9+
/**
10+
* This Runnable is smart in the sense that it can shutdown the
11+
*/
12+
// TODO LOMBOK
13+
public class NamedRunnable implements Runnable {
14+
15+
private static final Logger LOGGER = getLogger(NamedRunnable.class);
16+
17+
@NotNull private final String processName;
18+
@NotNull private final Runnable operation;
19+
20+
protected NamedRunnable(@NotNull String processName, @NotNull Runnable operation) {
21+
this.processName = processName;
22+
this.operation = operation;
23+
}
24+
25+
@Override
26+
public void run() {
27+
// by the time the code reaches here, the user would have configured the appropriate handlers
28+
try {
29+
operation.run();
30+
} catch (Exception e) {
31+
LOGGER.error("Failed to run " + processName, e);
32+
throw e;
33+
}
934
}
1035

1136
@Override
1237
public String toString() {
13-
return name;
38+
return processName;
1439
}
1540
}

modules/core-test-module/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<dependency>
4343
<groupId>org.assertj</groupId>
4444
<artifactId>assertj-core</artifactId>
45-
<version>2.9.1</version>
45+
<version>3.21.0</version>
4646
</dependency>
4747
</dependencies>
4848
</project>

0 commit comments

Comments
 (0)