Hi can someone help as to why the main in below never completes. When I pass 1 to the test method it get stuck completely. However passing 2 makes the run ok. Want to understand the actual issue and also what would be the correct way to code this.
public class Test {
private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1, r -> {
Thread t = defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
});
public static void main(String[] args) {
Test t = new Test();
t.test(1).toCompletableFuture().join();
System.out.println("DONE");
}
public CompletionStage<Void> run(int i) {
if (i == 1) throw new RuntimeException();
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
}
public CompletionStage<Void> test(int i) {
CompletableFuture<Void> future = new CompletableFuture<>();
EXECUTOR.schedule(() -> run(i).handle((output, error) -> {
if (error instanceof CompletionException) {
error = error.getCause();
}
if (error != null) {
CompletableFuture<Void> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(error);
return failedFuture;
}
return completedFuture(output);
}).thenCompose(u -> u).thenApply(future::complete).exceptionally(future::completeExceptionally), 0, TimeUnit.SECONDS);
return future;
}
}
EDIT: used solution suggested by holger and working fine. In this solution the handle catches the runtime exception. Why not previously?
public class Test {
private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1, r -> {
Thread t = defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
});
public static void main(String[] args) {
Test t = new Test();
System.out.println("STARTED");
t.test(1).toCompletableFuture().join();
System.out.println("DONE");
}
public CompletionStage<Void> run(int i) {
if (i == 1)
throw new RuntimeException();
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
}
public CompletionStage<Void> test(int i) {
return completedFuture(i).thenComposeAsync(this::run, r -> EXECUTOR.schedule(r, 0, TimeUnit.SECONDS))
.handle((res, ex) -> {
if (ex == null)
return completedFuture(res);
if (ex instanceof CompletionException) {
ex = ex.getCause();
}
CompletableFuture<Void> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(ex);
return failedFuture;
}).thenCompose(u -> u);
}
}
Related
I am trying to make 3 rest calls using completablefutures and return for the first one that matches a specific response. Below is sample test code I wrote (Minus the rest calls) for it but that does not seem to work. I always see "future1" getting returned even with the wait time, which means, test2 and test3 are blocking. How do I achieve the ask?
I thought of using CompletableFuture.anyOf but that just returns the result for the first future that gets executed. Not the first one that matches a specified response. Please advise
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Testing {
public static void main(String args[]) throws InterruptedException, ExecutionException {
CompletableFuture<String> combinedFuture = test("future1", 10000)
.thenCompose(response1 -> test2()
.thenCompose(response2 -> test3()
.thenApply(response3 -> {
return combine(response1, response2, response3);
})));
System.out.println(combinedFuture.get());
}
private static CompletableFuture<String> test(String str, int i) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
return str;
});
}
private static CompletableFuture<String> test2() {
return test("future2", 0);
}
private static CompletableFuture<String> test3() {
return test("future3", 0);
}
private static String combine(String response1, String response2, String response3) {
String responseString = null;
if (response1 != null) {
return response1;
} else if (response2 != null) {
return response2;
} else if (response3 != null) {
return response3;
}
return responseString;
}
}
You need to have a specific task that will collect the result of the others and complete only when desired.
For instance:
public class Testing {
public static void main(String args[]) throws InterruptedException, ExecutionException {
String result = aggregator(
Arrays.asList(
test("future1", 10000),
test("future2", 0),
test("future3", 0)),
(value) -> { return value != null; },
"default value"
).get();
System.out.println(result);
}
private static CompletableFuture<String> aggregator(Collection<CompletableFuture<String>> tasks, Predicate<String> validator, String defaultValue)
{
CompletableFuture<String> a = new CompletableFuture<String>();
AtomicInteger count = new AtomicInteger(0);
tasks.forEach((t) -> {
t.whenComplete((value, error) -> {
int c = count.incrementAndGet();
if( error == null && validator.test(value) ) a.complete(value);
else if( c == tasks.size() ) a.complete(defaultValue);
});
});
return a;
}
private static CompletableFuture<String> test(String str, int i) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
return str;
});
}
}
The aggregator method will accept any number of other tasks and will compare the result of each using the validator provided. The first one that matches is returned immediately without waiting for the others.
And at the end, if none matched, it completes with the default value.
You can race the futures against each other and delegate completions to another one:
static <T> CompletableFuture<T> first(Stream<CompletableFuture<T>> futures) {
var delegate = new CompletableFuture<T>();
runAsync(() ->
futures.forEach(future ->
future.handle(
(value, error) -> {
if (value == null) {
return delegate.completeExceptionally(error);
} else {
return delegate.complete(value);
}
})));
return delegate;
}
The future returned by first completes (either successfully or with an error), whenever the first one passed in the futures argument completes.
Now
CompletableFuture<String> combinedFuture =
first(Stream.of(test("future1", 10000), test2(), test3()));
System.out.println(combinedFuture.get());
prints either "future2" or "future3" depending on which on happens to complete first.
In the below code the subscriber stops recieving data whenever there is a timeout exception. How can I make sure that the subscriber does not stop when there is exception.
public class ReactiveDataService
{
private static String[] quotes = {"ITEM1", "ITEM2", "ITEM3"};
public Observable<Notification<String>> getStreamData()
{
return Observable.create(subscriber -> {
if(!subscriber.isUnsubscribed())
{
Stream<String> streams = Arrays.stream(quotes);
streams.map(quote -> quote.toString()).filter(quote -> quote!=null)
.forEach(q -> {
subscriber.onNext(Notification.createOnNext(q));
try
{
Random rand = new Random();
Integer i = (rand.nextInt(5)+1)*1000;
Thread.sleep(i);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
});
}
subscriber.onCompleted();
});
}
}
public class ReactiveResource
{
public static void main(String args[])
{
Observable<Notification<String>> watcher = new ReactiveResource().getData()
.timeout(4, TimeUnit.SECONDS)
.doOnError(failure -> System.out.println("Error:" + failure.getCause()))
.onErrorResumeNext(th -> {
return Observable.just(Notification.createOnError(new TimeoutException("Timed Out!")));
});
watcher.subscribe(
ReactiveResource::callBack,
ReactiveResource::errorCallBack,
ReactiveResource::completeCallBack
);
}
public static Action1 callBack(Notification<String> data)
{
System.out.println(data.getValue());
return null;
}
public static void errorCallBack(Throwable throwable)
{
System.out.println(throwable instanceof TimeoutException);
System.out.println(throwable);
}
public static void completeCallBack()
{
System.out.println("On completed successfully");
}
private Observable<Notification<String>> getData()
{
return new ReactiveDataService().getStreamData();
}
You can combine publish, mergeWith and timer to achieve this effect:
static <T> ObservableTransformer<T, T> onTimeoutKeepAlive(
long timeout, TimeUnit unit, Scheduler scheduler, T keepAliveItem) {
return upstream ->
upstream.publish(o ->
o.mergeWith(
Observable.timer(timeout, unit, scheduler)
.map(t -> keepAliveItem)
.takeUntil(o)
.repeat()
.takeUntil(o.ignoreElements().toObservable())
)
);
}
usage:
source
.compose(onTimeoutKeepAlive(
10, TimeUnit.SECONDS, Schedulers.computation(),
Notification.createOnError(new TimeoutException())
))
.subscribe(/* ... */);
I'm trying to write a unit test for the following function.
responseHandler and ErrorHandler are both methods that are passes using the Command Pattern
and are used to continue the program's flow.
futureCommand is a lettuce object (Redis implementation in java).
I'm having difficulties with how to test this method since is both using a future and does not return anything.
public void getOfferData(EventRequestContext<? extends BaseEvent> ctx, int offerId, ResponseHandler<T1Offer> responseHandler,
ErrorHandler<Throwable> errorHandler) throws Exception {
String redisKey = keyPrefix + offerId;
RedisFuture<List<String>> futureCommand = connectionWrapper.getHashValues(redisKey, getRequiredParams());
futureCommand.thenAccept(valuesList -> {
TrackerScheduler.processT1GenreicPool.execute(ctx, () -> {
Map<String, String> resultMap = reconstructMapValues(valuesList, getRequiredParams(), redisKey, ctx);
T1Offer offerData;
if(!resultMap.isEmpty()) {
offerData = new T1Offer(resultMap);
} else {
offerData = new T1Offer();
}
if(!offerData.isValid()) {
errorHandler.onError(new Exception("Invalid fields in offerData"));
} else {
responseHandler.onResponse(offerData);
}
});
});
}
My best attempt was to send the assertion using the responseHandler method like this:
#Test
public void getOfferData_offerFullData_parseSuccess() throws Exception {
T1ClickOfferDao.instance.getOfferData(null, Integer.parseInt(OFF_ID), resultOffer -> {
Assert.assertEquals("", resultOffer.getActivationDate());
}, error -> {
});
}
but the Test context is finished before the future is evaluated. And even if I Threas.sleep for a second - the assertion does not affect the test result.
How about
#Test
public void getOfferData_offerFullData_parseSuccess() throws Exception {
final String lock = new String("lock");
T1ClickOfferDao.instance.getOfferData(null, Integer.parseInt(OFF_ID), resultOffer -> {
Assert.assertEquals("", resultOffer.getActivationDate());
synchronized(lock){
lock.notifyAll();
}
}, error -> {
});
synchronized(lock){
try{
lock.wait(1000*2);
} catch (InterruptedException ex) {
fail("Timeout");
}
}
}
I want to use ManagedBlocker with CompletableFuture to prevent ForkJoinPool.commonPool from exhaustion, i.e.:
#Test
public void testCompletableFutureWithManagedBlocker() throws ExecutionException, InterruptedException {
final long startingTime = System.currentTimeMillis();
final int numberOfFuture = 32;
final CountDownLatch countDownLatch = new CountDownLatch(numberOfFuture);
final List<CompletableFuture<Void>> futures = Stream
.generate(() -> CompletableFuture.runAsync(() -> {
countDownLatch.countDown();
BlockingTasks.callInManagedBlock((() -> {sleep(); return null; }));
}))
.limit(numberOfFuture)
.collect(Collectors.toList());
futures.forEach((future) -> {
try { countDownLatch.await(); } catch (InterruptedException ignored) {}
future.join();
});
System.out.println("Time taken roughly: [" + (System.currentTimeMillis() - startingTime) + "]ms");
}
public class BlockingTasks {
public static<T> T callInManagedBlock(final Supplier<T> supplier) {
final SupplierManagedBlock<T> managedBlock = new SupplierManagedBlock<>(supplier);
try {
ForkJoinPool.managedBlock(managedBlock);
} catch (InterruptedException e) {
throw new Error(e);
}
return managedBlock.getResult();
}
private static class SupplierManagedBlock<T> implements ForkJoinPool.ManagedBlocker {
private final Supplier<T> supplier;
private T result;
private boolean done = false;
private SupplierManagedBlock(final Supplier<T> supplier) {
this.supplier = supplier;
}
#Override
public boolean block() {
result = supplier.get();
done = true;
return true;
}
#Override
public boolean isReleasable() {
return done;
}
public T getResult() {
return result;
}
}
}
Are there any caveats of using ManagedBlocker with CompletableFuture that I should be aware of?
However testCase2 does not handles the exception and throws an error. Am I missing something? Sorry if I did, quite new to this.
#Test
public void testCase1() throws Exception {
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException();
return "Promise";
}).exceptionally((ex) -> {
return "Fake Promise";
}).get();
}
#Test
public void testCase2() throws Exception {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException();
return "Promise";
});
cf.exceptionally((ex) -> {
return "Fake Promise";
});
cf.get();
}
However testCase2 does not handles the exception
Your testCase2 did handled the exception, you can add extra print statement to check it.
The reason testCase2 throws an Exception is that code:
cf.exceptionally((ex) -> {
System.out.println("Fake Promise: " + System.nanoTime());
return "Fake Promise";
})
will return a new CompletableFuture but you just discard it, the variable cf in cf.get is still not registered with any exception handler. The code should be:
#Test
public void testCase2() throws Exception {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException();
return "Promise";
});
CompletableFuture<String> handledCf = cf.exceptionally((ex) -> {
return "Fake Promise";
});
return handledCf.get();
}