--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java Fri Jan 19 16:48:12 2018 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java Mon Jan 22 17:29:07 2018 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -114,7 +114,7 @@
} else {
DEBUG_LOGGER.log(Level.DEBUG, "HTTP/2 connection creation failed "
+ "with unexpected exception: %s", (Object)t);
- return CompletableFuture.failedFuture(t);
+ return MinimalFuture.failedFuture(t);
}
}
if (secure && c== null) {
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java Fri Jan 19 16:48:12 2018 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java Mon Jan 22 17:29:07 2018 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -98,7 +98,7 @@
synchronized (this) {
Http2Connection connection = connections.get(key);
if (connection != null) { // fast path if connection already exists
- return CompletableFuture.completedFuture(connection);
+ return MinimalFuture.completedFuture(connection);
}
if (!req.secure() || failures.contains(key)) {
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java Fri Jan 19 16:48:12 2018 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java Mon Jan 22 17:29:07 2018 +0000
@@ -282,7 +282,7 @@
public CompletionStage<InputStream> getBody() {
// Returns the stream immediately, before the
// response body is received.
- // This makes it possible for senAsync().get().body()
+ // This makes it possible for sendAsync().get().body()
// to complete before the response body is received.
return CompletableFuture.completedStage(this);
}
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java Fri Jan 19 16:48:12 2018 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java Mon Jan 22 17:29:07 2018 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,14 +26,10 @@
package jdk.incubator.http.internal.common;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
-import java.util.concurrent.Executor;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
+import java.util.concurrent.atomic.AtomicLong;
import static java.util.Objects.requireNonNull;
-import java.util.concurrent.atomic.AtomicLong;
/*
* A CompletableFuture which does not allow any obtrusion logic.
@@ -46,8 +42,8 @@
U get() throws Throwable;
}
- final static AtomicLong TOKENS = new AtomicLong();
- final long id;
+ private final static AtomicLong TOKENS = new AtomicLong();
+ private final long id;
public static <U> MinimalFuture<U> completedFuture(U value) {
MinimalFuture<U> f = new MinimalFuture<>();
@@ -73,73 +69,13 @@
return cf;
}
- public static <U> CompletableFuture<U> supply(ExceptionalSupplier<U> supplier, Executor executor) {
- CompletableFuture<U> cf = new MinimalFuture<>();
- cf.completeAsync( () -> {
- try {
- return supplier.get();
- } catch (Throwable ex) {
- throw new CompletionException(ex);
- }
- }, executor);
- return cf;
- }
-
public MinimalFuture() {
super();
this.id = TOKENS.incrementAndGet();
}
- /**
- * Creates a defensive copy of the given {@code CompletionStage}.
- *
- * <p> Might be useful both for producers and consumers of {@code
- * CompletionStage}s.
- *
- * <p> Producers are protected from possible uncontrolled modifications
- * (cancellation, completion, obtrusion, etc.) as well as from executing
- * unknown potentially lengthy or faulty dependants in the given {@code
- * CompletionStage}'s default execution facility or synchronously.
- *
- * <p> Consumers are protected from some of the aspects of misbehaving
- * implementations (e.g. accepting results, applying functions, running
- * tasks, etc. more than once or escape of a reference to their private
- * executor, etc.) by providing a reliable proxy they use instead.
- *
- * @param src
- * the {@code CompletionStage} to make a copy from
- * @param executor
- * the executor used to propagate the completion
- * @param <T>
- * the type of the {@code CompletionStage}'s result
- *
- * @return a copy of the given stage
- */
- public static <T> MinimalFuture<T> copy(CompletionStage<T> src,
- Executor executor) {
- MinimalFuture<T> copy = new MinimalFuture<>();
- BiConsumer<T, Throwable> relay =
- (result, error) -> {
- if (error != null) {
- copy.completeExceptionally(error);
- } else {
- copy.complete(result);
- }
- };
-
- if (src.getClass() == CompletableFuture.class) {
- // No subclasses! Strictly genuine CompletableFuture.
- src.whenCompleteAsync(relay, executor);
- return copy;
- } else {
- // Don't give our executor away to an unknown CS!
- src.whenComplete(relay);
- return (MinimalFuture<T>)
- copy.thenApplyAsync(Function.identity(), executor);
- }
- }
-
- public static <U> MinimalFuture<U> newMinimalFuture() {
+ @Override
+ public <U> MinimalFuture<U> newIncompleteFuture() {
return new MinimalFuture<>();
}
@@ -159,7 +95,7 @@
}
public static <U> MinimalFuture<U> of(CompletionStage<U> stage) {
- MinimalFuture<U> cf = newMinimalFuture();
+ MinimalFuture<U> cf = new MinimalFuture<>();
stage.whenComplete((r,t) -> complete(cf, r, t));
return cf;
}
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java Fri Jan 19 16:48:12 2018 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java Mon Jan 22 17:29:07 2018 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,26 +25,27 @@
package jdk.incubator.http.internal.common;
-import java.io.IOException;
-import java.lang.System.Logger.Level;
-import java.nio.ByteBuffer;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Flow;
-import java.util.concurrent.Flow.Subscriber;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicInteger;
+import jdk.incubator.http.internal.common.SubscriberWrapper.SchedulingAction;
+
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
-import jdk.incubator.http.internal.common.SubscriberWrapper.SchedulingAction;
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Implements SSL using two SubscriberWrappers.
@@ -89,7 +90,7 @@
final Writer writer;
final SSLEngine engine;
final String tubeName; // hack
- final CompletableFuture<Void> cf;
+ private final CompletableFuture<Void> cf;
final CompletableFuture<String> alpnCF; // completes on initial handshake
final static ByteBuffer SENTINEL = Utils.EMPTY_BYTEBUFFER;
volatile boolean close_notify_received;
@@ -110,8 +111,9 @@
this.engine = engine;
this.exec = exec;
this.handshakeState = new AtomicInteger(NOT_HANDSHAKING);
- this.cf = CompletableFuture.allOf(reader.completion(), writer.completion())
- .thenRun(this::normalStop);
+ CompletableFuture<Void> cs = CompletableFuture.allOf(
+ reader.completion(), writer.completion()).thenRun(this::normalStop);
+ this.cf = MinimalFuture.of(cs);
this.alpnCF = new MinimalFuture<>();
// connect the Reader to the downReader and the
--- a/test/jdk/java/net/httpclient/LineBodyHandlerTest.java Fri Jan 19 16:48:12 2018 +0000
+++ b/test/jdk/java/net/httpclient/LineBodyHandlerTest.java Mon Jan 22 17:29:07 2018 +0000
@@ -21,6 +21,23 @@
* questions.
*/
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -36,6 +53,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Flow;
import java.util.function.Function;
@@ -43,30 +61,13 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-import com.sun.net.httpserver.HttpsConfigurator;
-import com.sun.net.httpserver.HttpsServer;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
-import jdk.incubator.http.HttpResponse.BodyHandler;
-import jdk.incubator.http.HttpResponse.BodySubscriber;
-import jdk.testlibrary.SimpleSSLContext;
-import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeTest;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-import javax.net.ssl.SSLContext;
-
import static java.nio.charset.StandardCharsets.UTF_16;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.assertNotNull;
/*
* @test
@@ -194,9 +195,11 @@
.build();
StringSubscriber subscriber = new StringSubscriber();
- HttpResponse<String> response = client.sendAsync(request,
- BodyHandler.fromLineSubscriber(subscriber, Supplier::get,"\n"))
- .join();
+ CompletableFuture<HttpResponse<String>> cf
+ = client.sendAsync(request, BodyHandler.fromLineSubscriber(
+ subscriber, Supplier::get, "\n"));
+ assertNoObtrusion(cf);
+ HttpResponse<String> response = cf.join();
String text = response.body();
System.out.println(text);
assertEquals(response.statusCode(), 200);
@@ -213,8 +216,10 @@
.POST(fromString(body))
.build();
- HttpResponse<Stream<String>> response = client.sendAsync(request,
- BodyHandler.asLines()).join();
+ CompletableFuture<HttpResponse<Stream<String>>> cf
+ = client.sendAsync(request, BodyHandler.asLines());
+ assertNoObtrusion(cf);
+ HttpResponse<Stream<String>> response = cf.join();
Stream<String> stream = response.body();
List<String> list = stream.collect(Collectors.toList());
String text = list.stream().collect(Collectors.joining("|"));
@@ -234,8 +239,11 @@
.build();
StringSubscriber subscriber = new StringSubscriber();
- HttpResponse<Void> response = client.sendAsync(request,
- BodyHandler.fromLineSubscriber(subscriber)).join();
+ CompletableFuture<HttpResponse<Void>> cf
+ = client.sendAsync(request,
+ BodyHandler.fromLineSubscriber(subscriber));
+ assertNoObtrusion(cf);
+ HttpResponse<Void> response = cf.join();
String text = subscriber.get();
System.out.println(text);
assertEquals(response.statusCode(), 200);
@@ -251,8 +259,10 @@
.POST(fromString(body))
.build();
- HttpResponse<Stream<String>> response = client.sendAsync(request,
- BodyHandler.asLines()).join();
+ CompletableFuture<HttpResponse<Stream<String>>> cf
+ = client.sendAsync(request, BodyHandler.asLines());
+ assertNoObtrusion(cf);
+ HttpResponse<Stream<String>> response = cf.join();
Stream<String> stream = response.body();
List<String> list = stream.collect(Collectors.toList());
String text = list.stream().collect(Collectors.joining("|"));
@@ -309,8 +319,10 @@
.POST(fromString(body))
.build();
- HttpResponse<Stream<String>> response = client.sendAsync(request,
- BodyHandler.asLines()).join();
+ CompletableFuture<HttpResponse<Stream<String>>> cf
+ = client.sendAsync(request, BodyHandler.asLines());
+ assertNoObtrusion(cf);
+ HttpResponse<Stream<String>> response = cf.join();
Stream<String> stream = response.body();
List<String> list = stream.collect(Collectors.toList());
String text = list.stream().collect(Collectors.joining("|"));
@@ -334,8 +346,10 @@
.header("Content-type", "text/text; charset=UTF-8")
.POST(fromString(body, UTF_8)).build();
- HttpResponse<Stream<String>> response = client.sendAsync(request,
- BodyHandler.asLines()).join();
+ CompletableFuture<HttpResponse<Stream<String>>> cf
+ = client.sendAsync(request, BodyHandler.asLines());
+ assertNoObtrusion(cf);
+ HttpResponse<Stream<String>> response = cf.join();
Stream<String> stream = response.body();
List<String> list = stream.collect(Collectors.toList());
String text = list.stream().collect(Collectors.joining("|"));
@@ -358,8 +372,10 @@
.header("Content-type", "text/text; charset=UTF-16")
.POST(fromString(body, UTF_16)).build();
- HttpResponse<Stream<String>> response = client.sendAsync(request,
- BodyHandler.asLines()).join();
+ CompletableFuture<HttpResponse<Stream<String>>> cf
+ = client.sendAsync(request, BodyHandler.asLines());
+ assertNoObtrusion(cf);
+ HttpResponse<Stream<String>> response = cf.join();
Stream<String> stream = response.body();
List<String> list = stream.collect(Collectors.toList());
String text = list.stream().collect(Collectors.joining("|"));
@@ -384,9 +400,11 @@
.build();
ObjectSubscriber subscriber = new ObjectSubscriber();
- HttpResponse<String> response = client.sendAsync(request,
- BodyHandler.fromLineSubscriber(subscriber, ObjectSubscriber::get, "\r\n"))
- .join();
+ CompletableFuture<HttpResponse<String>> cf
+ = client.sendAsync(request, BodyHandler.fromLineSubscriber(
+ subscriber, ObjectSubscriber::get, "\r\n"));
+ assertNoObtrusion(cf);
+ HttpResponse<String> response = cf.join();
String text = response.body();
System.out.println(text);
assertEquals(response.statusCode(), 200);
@@ -406,10 +424,11 @@
.header("Content-type", "text/text; charset=UTF-16")
.POST(fromString(body, UTF_16)).build();
ObjectSubscriber subscriber = new ObjectSubscriber();
- HttpResponse<String> response = client.sendAsync(request,
- BodyHandler.fromLineSubscriber(subscriber,
- ObjectSubscriber::get,
- null)).join();
+ CompletableFuture<HttpResponse<String>> cf
+ = client.sendAsync(request, BodyHandler.fromLineSubscriber(
+ subscriber, ObjectSubscriber::get, null));
+ assertNoObtrusion(cf);
+ HttpResponse<String> response = cf.join();
String text = response.body();
System.out.println(text);
assertEquals(response.statusCode(), 200);
@@ -433,8 +452,11 @@
.build();
ObjectSubscriber subscriber = new ObjectSubscriber();
- HttpResponse<Void> response = client.sendAsync(request,
- BodyHandler.fromLineSubscriber(subscriber)).join();
+ CompletableFuture<HttpResponse<Void>> cf
+ = client.sendAsync(request,
+ BodyHandler.fromLineSubscriber(subscriber));
+ assertNoObtrusion(cf);
+ HttpResponse<Void> response = cf.join();
String text = subscriber.get();
System.out.println(text);
assertEquals(response.statusCode(), 200);
@@ -518,9 +540,11 @@
.build();
StringSubscriber subscriber = new StringSubscriber();
- HttpResponse<String> response = client.sendAsync(request,
- BodyHandler.fromLineSubscriber(subscriber, Supplier::get,"\r\n"))
- .join();
+ CompletableFuture<HttpResponse<String>> cf
+ = client.sendAsync(request, BodyHandler.fromLineSubscriber(
+ subscriber, Supplier::get, "\r\n"));
+ assertNoObtrusion(cf);
+ HttpResponse<String> response = cf.join();
String text = response.body();
System.out.println(text);
assertEquals(response.statusCode(), 200);
@@ -537,8 +561,10 @@
.POST(fromString(bigtext))
.build();
- HttpResponse<Stream<String>> response = client.sendAsync(request,
- BodyHandler.asLines()).join();
+ CompletableFuture<HttpResponse<Stream<String>>> cf
+ = client.sendAsync(request, BodyHandler.asLines());
+ assertNoObtrusion(cf);
+ HttpResponse<Stream<String>> response = cf.join();
Stream<String> stream = response.body();
List<String> list = stream.collect(Collectors.toList());
String text = list.stream().collect(Collectors.joining("|"));
@@ -699,4 +725,11 @@
}
}
}
+
+ private static void assertNoObtrusion(CompletableFuture<?> cf) {
+ assertThrows(UnsupportedOperationException.class,
+ () -> cf.obtrudeException(new RuntimeException()));
+ assertThrows(UnsupportedOperationException.class,
+ () -> cf.obtrudeValue(null));
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/whitebox/MinimalFutureTestDriver.java Mon Jan 22 17:29:07 2018 +0000
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @run testng jdk.incubator.httpclient/jdk.incubator.http.internal.common.MinimalFutureTest
+ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/MinimalFutureTest.java Mon Jan 22 17:29:07 2018 +0000
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.incubator.http.internal.common;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.testng.Assert.assertThrows;
+
+public class MinimalFutureTest {
+
+ @Test(dataProvider = "futures")
+ public void test(CompletableFuture<Object> mf) {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ try {
+ assertNoObtrusion(mf.thenApply(MinimalFutureTest::apply));
+ assertNoObtrusion(mf.thenApplyAsync(MinimalFutureTest::apply));
+ assertNoObtrusion(mf.thenApplyAsync(MinimalFutureTest::apply, executor));
+
+ assertNoObtrusion(mf.thenAccept(MinimalFutureTest::accept));
+ assertNoObtrusion(mf.thenAcceptAsync(MinimalFutureTest::accept));
+ assertNoObtrusion(mf.thenAcceptAsync(MinimalFutureTest::accept, executor));
+
+ assertNoObtrusion(mf.thenRun(MinimalFutureTest::run));
+ assertNoObtrusion(mf.thenRunAsync(MinimalFutureTest::run));
+ assertNoObtrusion(mf.thenRunAsync(MinimalFutureTest::run, executor));
+
+ assertNoObtrusion(mf.thenCombine(otherFuture(), MinimalFutureTest::apply));
+ assertNoObtrusion(mf.thenCombineAsync(otherFuture(), MinimalFutureTest::apply));
+ assertNoObtrusion(mf.thenCombineAsync(otherFuture(), MinimalFutureTest::apply, executor));
+
+ assertNoObtrusion(mf.thenAcceptBoth(otherFuture(), MinimalFutureTest::accept));
+ assertNoObtrusion(mf.thenAcceptBothAsync(otherFuture(), MinimalFutureTest::accept));
+ assertNoObtrusion(mf.thenAcceptBothAsync(otherFuture(), MinimalFutureTest::accept, executor));
+
+ assertNoObtrusion(mf.runAfterBoth(otherFuture(), MinimalFutureTest::run));
+ assertNoObtrusion(mf.runAfterBothAsync(otherFuture(), MinimalFutureTest::run));
+ assertNoObtrusion(mf.runAfterBothAsync(otherFuture(), MinimalFutureTest::run, executor));
+
+ // "either" methods may return something else if otherFuture() is
+ // not MinimalFuture
+
+ assertNoObtrusion(mf.applyToEither(otherFuture(), MinimalFutureTest::apply));
+ assertNoObtrusion(mf.applyToEitherAsync(otherFuture(), MinimalFutureTest::apply));
+ assertNoObtrusion(mf.applyToEitherAsync(otherFuture(), MinimalFutureTest::apply, executor));
+
+ assertNoObtrusion(mf.acceptEither(otherFuture(), MinimalFutureTest::accept));
+ assertNoObtrusion(mf.acceptEitherAsync(otherFuture(), MinimalFutureTest::accept));
+ assertNoObtrusion(mf.acceptEitherAsync(otherFuture(), MinimalFutureTest::accept, executor));
+
+ assertNoObtrusion(mf.runAfterEither(otherFuture(), MinimalFutureTest::run));
+ assertNoObtrusion(mf.runAfterEitherAsync(otherFuture(), MinimalFutureTest::run));
+ assertNoObtrusion(mf.runAfterEitherAsync(otherFuture(), MinimalFutureTest::run, executor));
+
+ assertNoObtrusion(mf.thenCompose(MinimalFutureTest::completionStageOf));
+ assertNoObtrusion(mf.thenComposeAsync(MinimalFutureTest::completionStageOf));
+ assertNoObtrusion(mf.thenComposeAsync(MinimalFutureTest::completionStageOf, executor));
+
+ assertNoObtrusion(mf.handle(MinimalFutureTest::relay));
+ assertNoObtrusion(mf.handleAsync(MinimalFutureTest::relay));
+ assertNoObtrusion(mf.handleAsync(MinimalFutureTest::relay, executor));
+
+ assertNoObtrusion(mf.whenComplete(MinimalFutureTest::accept));
+ assertNoObtrusion(mf.whenCompleteAsync(MinimalFutureTest::accept));
+ assertNoObtrusion(mf.whenCompleteAsync(MinimalFutureTest::accept, executor));
+
+ assertNoObtrusion(mf.toCompletableFuture());
+ assertNoObtrusion(mf.exceptionally(t -> null));
+
+ assertNoObtrusion(mf);
+ assertNoObtrusion(mf.copy());
+ assertNoObtrusion(mf.newIncompleteFuture());
+ } finally {
+ executor.shutdownNow();
+ }
+ }
+
+ private static CompletableFuture<Object> otherFuture() {
+ return MinimalFuture.completedFuture(new Object());
+ }
+
+ private static Object relay(Object r, Throwable e) {
+ if (e != null)
+ throw new CompletionException(e);
+ else
+ return r;
+ }
+
+ private static CompletableFuture<?> completionStageOf(Object r) {
+ return new CompletableFuture<>();
+ }
+
+ private static void accept(Object arg) {
+ }
+
+ private static void accept(Object arg1, Object arg2) {
+ }
+
+ private static void run() {
+ }
+
+ private static Object apply(Object arg) {
+ return new Object();
+ }
+
+ private static Object apply(Object arg1, Object arg2) {
+ return new Object();
+ }
+
+
+ @DataProvider(name = "futures")
+ public Object[][] futures() {
+
+ MinimalFuture<Object> mf = new MinimalFuture<>();
+ mf.completeExceptionally(new Throwable());
+
+ MinimalFuture<Object> mf1 = new MinimalFuture<>();
+ mf1.complete(new Object());
+
+ return new Object[][]{
+ new Object[]{new MinimalFuture<>()},
+ new Object[]{MinimalFuture.failedFuture(new Throwable())},
+ new Object[]{MinimalFuture.completedFuture(new Object())},
+ new Object[]{mf},
+ new Object[]{mf1},
+ };
+ }
+
+ private void assertNoObtrusion(CompletableFuture<?> cf) {
+ assertThrows(UnsupportedOperationException.class,
+ () -> cf.obtrudeValue(null));
+ assertThrows(UnsupportedOperationException.class,
+ () -> cf.obtrudeException(new RuntimeException()));
+ }
+}