--- a/src/java.net.http/share/classes/java/net/http/HttpResponse.java Wed Jun 06 12:10:30 2018 +0100
+++ b/src/java.net.http/share/classes/java/net/http/HttpResponse.java Wed Jun 06 13:57:19 2018 +0100
@@ -830,7 +830,7 @@
* BodySubscriber} provides implementations of many common body subscribers.
*
* <p> The object acts as a {@link Flow.Subscriber}<{@link List}<{@link
- * ByteBuffer}>> to the HTTP client implementation, which publishes
+ * ByteBuffer}>> to the HTTP Client implementation, which publishes
* lists of ByteBuffers containing the response body. The Flow of data, as
* well as the order of ByteBuffers in the Flow lists, is a strictly ordered
* representation of the response body. Both the Lists and the ByteBuffers,
--- a/src/java.net.http/share/classes/java/net/http/package-info.java Wed Jun 06 12:10:30 2018 +0100
+++ b/src/java.net.http/share/classes/java/net/http/package-info.java Wed Jun 06 13:57:19 2018 +0100
@@ -42,10 +42,14 @@
* Hypertext Transfer Protocol (HTTP/1.1)</a>, and
* <a href="https://tools.ietf.org/html/rfc6455">The WebSocket Protocol</a>.
*
- * <p> Asynchronous tasks and dependent actions of returned {@link
- * java.util.concurrent.CompletableFuture} instances are executed on the threads
- * supplied by the client's {@link java.util.concurrent.Executor}, where
- * practical.
+ * <p> In general, asynchronous tasks are executed either by the thread
+ * performing the send operation, or by the threads supplied by the client's
+ * {@link java.net.http.HttpClient#executor() executor}. Dependent tasks, those
+ * that are triggered by returned CompletionStages or CompletableFutures, that
+ * do not explicitly specify an executor, are executed in the same
+ * {@link java.util.concurrent.CompletableFuture#defaultExecutor() default
+ * executor} as that of CompletableFuture, or the invoking thread if the send
+ * operation completes before the dependent task is registered.
*
* <p> {@code CompletableFuture}s returned by this API will throw {@link
* java.lang.UnsupportedOperationException} for their {@link
--- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java Wed Jun 06 12:10:30 2018 +0100
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java Wed Jun 06 13:57:19 2018 +0100
@@ -28,6 +28,7 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.Authenticator;
@@ -56,6 +57,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -531,6 +533,8 @@
}
}
+ private static final Executor ASYNC_POOL = new CompletableFuture<Void>().defaultExecutor();
+
@Override
public <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest userRequest, BodyHandler<T> responseHandler)
@@ -538,7 +542,6 @@
return sendAsync(userRequest, responseHandler, null);
}
-
@Override
public <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest userRequest,
@@ -596,13 +599,11 @@
(b,t) -> debugCompleted("ClientImpl (async)", start, userRequest));
}
- // makes sure that any dependent actions happen in the executor.
- // we only need to do that for sendAsync(...), when exchangeExecutor
- // is non null.
+ // makes sure that any dependent actions happen in the CF default
+ // executor. This is only needed for sendAsync(...), when
+ // exchangeExecutor is non-null.
if (exchangeExecutor != null) {
- executor = acc == null ? executor
- : new PrivilegedExecutor(executor, acc);
- res = res.whenCompleteAsync((r, t) -> { /* do nothing */}, executor);
+ res = res.whenCompleteAsync((r, t) -> { /* do nothing */}, ASYNC_POOL);
}
return res;
} catch(Throwable t) {
--- a/test/jdk/java/net/httpclient/DependentActionsTest.java Wed Jun 06 12:10:30 2018 +0100
+++ b/test/jdk/java/net/httpclient/DependentActionsTest.java Wed Jun 06 13:57:19 2018 +0100
@@ -82,12 +82,14 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.System.out;
import static java.lang.String.format;
+import static java.util.stream.Collectors.toList;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@@ -374,13 +376,13 @@
}
final List<String> extractStream(HttpResponse<Stream<String>> resp) {
- return resp.body().collect(Collectors.toList());
+ return resp.body().collect(toList());
}
final List<String> extractInputStream(HttpResponse<InputStream> resp) {
try (InputStream is = resp.body()) {
return new BufferedReader(new InputStreamReader(is))
- .lines().collect(Collectors.toList());
+ .lines().collect(toList());
} catch (IOException x) {
throw new CompletionException(x);
}
@@ -399,43 +401,27 @@
.findFirst();
}
+ static final Predicate<StackFrame> DAT = sfe ->
+ sfe.getClassName().startsWith("DependentActionsTest");
+ static final Predicate<StackFrame> JUC = sfe ->
+ sfe.getClassName().startsWith("java.util.concurrent");
+ static final Predicate<StackFrame> JLT = sfe ->
+ sfe.getClassName().startsWith("java.lang.Thread");
+ static final Predicate<StackFrame> NotDATorJUCorJLT = Predicate.not(DAT.or(JUC).or(JLT));
+
+
<T> void checkThreadAndStack(Thread thread,
AtomicReference<RuntimeException> failed,
T result,
Throwable error) {
- if (Thread.currentThread() == thread) {
- //failed.set(new RuntimeException("Dependant action was executed in " + thread));
- List<StackFrame> httpStack = WALKER.walk(s -> s.filter(f -> f.getDeclaringClass()
- .getModule().equals(HttpClient.class.getModule()))
- .collect(Collectors.toList()));
- if (!httpStack.isEmpty()) {
- System.out.println("Found unexpected trace: ");
- httpStack.forEach(f -> System.out.printf("\t%s%n", f));
- failed.set(new RuntimeException("Dependant action has unexpected frame in " +
- Thread.currentThread() + ": " + httpStack.get(0)));
+ //failed.set(new RuntimeException("Dependant action was executed in " + thread));
+ List<StackFrame> otherFrames = WALKER.walk(s -> s.filter(NotDATorJUCorJLT).collect(toList()));
+ if (!otherFrames.isEmpty()) {
+ System.out.println("Found unexpected trace: ");
+ otherFrames.forEach(f -> System.out.printf("\t%s%n", f));
+ failed.set(new RuntimeException("Dependant action has unexpected frame in " +
+ Thread.currentThread() + ": " + otherFrames.get(0)));
- }
- return;
- } else if (System.getSecurityManager() != null) {
- Optional<StackFrame> sf = WALKER.walk(s -> findFrame(s, "PrivilegedRunnable"));
- if (!sf.isPresent()) {
- failed.set(new RuntimeException("Dependant action does not have expected frame in "
- + Thread.currentThread()));
- return;
- } else {
- System.out.println("Found expected frame: " + sf.get());
- }
- } else {
- List<StackFrame> httpStack = WALKER.walk(s -> s.filter(f -> f.getDeclaringClass()
- .getModule().equals(HttpClient.class.getModule()))
- .collect(Collectors.toList()));
- if (!httpStack.isEmpty()) {
- System.out.println("Found unexpected trace: ");
- httpStack.forEach(f -> System.out.printf("\t%s%n", f));
- failed.set(new RuntimeException("Dependant action has unexpected frame in " +
- Thread.currentThread() + ": " + httpStack.get(0)));
-
- }
}
}