When spying on a CompletableFuture with Mockito, spyObj.get occasionally fails - java

I'm running into a problem where occasionally my example code below will fail when running the test suite, but the tests individually always seem to pass. If I use just .get() for the spy CompletableFuture without specifying a timeout, it hangs indefinitely.
This problem occurs both on Windows, OS X, and I've tried a few different versions of the the Java 8 JDK.
I have this problem with Mockito 2.18.3 and Mockito 1.10.19.
I can run the example test suite code below sometimes 7-10 times successfully, but almost always when trying more than 10 times I'll see random test failures.
Any help would be greatly appreciated. I have also posted on the Mockito mailing list, but things look fairly quite there.
package example;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
import static org.mockito.Mockito.spy;
public class MockitoCompletableFuture1Test {
#Test
public void test1() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "ABC");
CompletableFuture<String> futureSpy = spy(future);
try {
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
assertEquals("ABC", future.get(1, TimeUnit.SECONDS)); // PASSES
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS)); // OCCASIONALLY FAILS
fail("futureSpy.get(...) timed out");
}
}
#Test
public void test2() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "ABC");
CompletableFuture<String> futureSpy = spy(future);
try {
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
assertEquals("ABC", future.get(1, TimeUnit.SECONDS)); // PASSES
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS)); // OCCASIONALLY FAILS
fail("futureSpy.get(...) timed out");
}
}
#Test
public void test3() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "ABC");
CompletableFuture<String> futureSpy = spy(future);
try {
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
assertEquals("ABC", future.get(1, TimeUnit.SECONDS)); // PASSES
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS)); // OCCASIONALLY FAILS
fail("futureSpy.get(...) timed out");
}
}
#Test
public void test4() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "ABC");
CompletableFuture<String> futureSpy = spy(future);
try {
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
assertEquals("ABC", future.get(1, TimeUnit.SECONDS)); // PASSES
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS)); // OCCASIONALLY FAILS
fail("futureSpy.get(...) timed out");
}
}
#Test
public void test5() throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "ABC");
CompletableFuture<String> futureSpy = spy(future);
try {
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
assertEquals("ABC", future.get(1, TimeUnit.SECONDS)); // PASSES
assertEquals("ABC", futureSpy.get(1, TimeUnit.SECONDS)); // OCCASIONALLY FAILS
fail("futureSpy.get(...) timed out");
}
}
}

When future is created (calling CompletableFuture.supplyAsync), it will also create a Thread (ForkJoinPool.commonPool-worker-N) to execute the lambda expression. That thread has a reference to the newly created object (future in our case). When the async job is finished, the thread (ForkJoinPool.commonPool-worker-N) will notify (wake up) the other thread (main) waiting for it that it has finished.
How does it know which thread is waiting for it? When you call the get() method the current thread will be saved as a field in the class and the thread will park (sleep) and will wait to be unparked by some other thread.
The problem is that futureSpy will save in its own field the current thread (main), but the async thread will try to read the information from the future object (null).
The problem doesn't always appear in your test case because if the async function is already finished, get won't put the main thread to sleep.
Reduced example
For testing purposes I've reduced you test cases to something shorter that reliably reproduces the error (except the first run):
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.mockito.Mockito.spy;
public class App {
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
for (int i = 0; i < 100; i++) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "ABC";
});
CompletableFuture<String> futureSpy = spy(future);
try {
futureSpy.get(2, TimeUnit.SECONDS);
System.out.println("i = " + i);
} catch (TimeoutException ex) {
System.out.println("i = " + i + " FAIL");
}
}
}
}
In my tests the output is:
i = 0
i = 1 FAIL
i = 2 FAIL
i = 3 FAIL

According to Important gotcha on spying real objects!:
Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it. So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction and their effect on real instance state. […]
So basically, it will take the state of your future at the time you call spy() on it. If it is already completed, then the resulting spy will be too. Otherwise, your spy will remain uncompleted, except if you complete it yourself.
As the asynchronous completion will be performed on the original future and not on your spy, it will thus not be reflected in your spy.
The only case where this would work properly, is when you have full control over it. This means you would have created your CompletableFuture with new, wrap it in a spy, and then only use that spy.
In general however, I would advise to avoid mocking futures, as you often don't have control over how they are handled. And as stated in Mockito's Remember section:
Do not mock types you don’t own
CompletableFuture is not a type you own.
Anyway, it shouldn't be necessary to mock CompletableFuture methods, as you can control what they do based on complete() or completeExecptionally(). On the other side, it shouldn't be necessary to check whether its methods are called since:
the methods with side-effects (like complete()) can easily be asserted afterwards;
the other methods are returning values that should make your test fail if they are not used.
Basically, CompletableFuture behaves similarly to a value object, and the documentation states:
Don’t mock value objects
If you feel your test cannot be written without using a spy, try to reduce it to an MCVE and post a separate question on how to do it.

Related

java CompletableFuture completion sequence

From CompletableFuture javadocs:
Actions supplied for dependent completions of non-async methods may be
performed by the thread that completes the current CompletableFuture,
or by any other caller of a completion method.
and looking into CompletableFuture.complete() it calls postComplete() which seems to pop off the dependent chains and tries to complete them.
To test my understanding, I wrote a simple program
import java.util.concurrent.*;
public class TestCompletableFuture {
public static void main(String[] args) throws Exception {
CompletableFuture<Void> future = new CompletableFuture<Void>()
.whenComplete((res, exc) -> {
System.out.println("inside handle.");
if (exc != null) {
System.out.println("exception.");
}
System.out.println("completed.");
}
);
future.completeExceptionally(new Exception("exception"));
System.out.println("done.");
}
}
the output of the code:
done.
From my understanding, when the main thread calls future.completeExceptionally() it should invoke the function passed into CompletableFuture.whenComplete().
Why is this not the case?
This is because you are completing the wrong future. You need to get a reference to the first stage and complete that to see the whenComplete in action:
public class TestCompletableFuture {
public static void main(String[] args) throws Exception {
// get reference
CompletableFuture<Void> future = new CompletableFuture<>();
// configure the action that to be run when the future is complete
CompletableFuture<Void> future2 = future
.whenComplete((res, exc) -> {
System.out.println("inside handle.");
if (exc != null) {
System.out.println("exception.");
}
System.out.println("completed.");
}
);
future.completeExceptionally(new Exception("exception"));
System.out.println("done.");
}
}
So, now the code speaks for itself... When the future is complete run that action (res, exc) -> {...}. And then just trigger that completion on the future by calling completeExceptionally(...) on it.
Another thing to note is that all of the above stages (futures) are completed exceptionally now:
System.out.println(future.isDone()); // true
System.out.println(future2.isDone()); // true
System.out.println(future.isCompletedExceptionally()); // true
System.out.println(future2.isCompletedExceptionally()); // true

How to run CompletableFutures of type boolean in parallel and exit on the first FALSE result or return when all tasks return TRUE?

So I have a use case where i have four tasks A,B,C,D which i want to run in parallel. the response type of these tasks is boolean.
After running them in parallel, i want to stop the execution on either of the below condition
return on receiving first false from any one of the task.
All of the 4 tasks are completed and none of them returned false.
my project is using Java, springboot
so i'm trying to use completable futures to achieve this. I could succeed in the first step with something like the below code. but not the second step. any help?
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class CompFutu {
public static <T> CompletableFuture<T> firstMatchingCondition(
List<? extends CompletionStage<? extends T>> work,
Predicate<? super T> successFullResultCondition) {
CompletableFuture<T> result = new CompletableFuture<>();
Consumer<T> tester = workUnit -> {
if (successFullResultCondition.test(workUnit)) {
result.complete(workUnit);
}
};
work.forEach(s -> s.thenAccept(tester));
return result;
}
public static void main(String[] args) throws Exception {
List<CompletableFuture<Boolean>> list= Arrays.asList(
CompletableFuture.supplyAsync(()-> {
try {
System.err.println(Thread.currentThread().getName());
Thread.sleep(1000);
System.err.println("DONE(true 1) "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}),
CompletableFuture.supplyAsync(()-> {
try {
System.err.println(Thread.currentThread().getName());
Thread.sleep(20_000);
System.err.println("DONE(false 20) "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}),
CompletableFuture.supplyAsync(()-> {
try {
System.err.println(Thread.currentThread().getName());
Thread.sleep(5000);
System.err.println("DONE(false 5) "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
})
);
firstMatchingCondition(list, i->!i).join();
System.err.println("Done");
}
}
Create another future that will be completed when all futures have been completed, in addition to your future that will be completed as soon as one fulfills the condition. Then, combine them to a future that will be completed as soon as either of them is completed:
CompletableFuture<?> firstFalse = firstMatchingCondition(list, i -> !i);
CompletableFuture<?> all
= CompletableFuture.allOf(list.toArray(new CompletableFuture<?>[0]));
CompletableFuture.anyOf(firstFalse, all).join();
You can inline the creations into a single expression.
CompletableFuture.anyOf(
firstMatchingCondition(list, i -> !i),
CompletableFuture.allOf(list.toArray(new CompletableFuture<?>[0]))
).join();
But it’s important to keep in mind that this only affects how long the initiating thread will wait before proceeding. This does not cancel the other futures and since CompletableFuture does not support interruption in general, it’s not possible to stop ongoing evaluations anyway. Only their results will be ignored.

Use CompletableFuture to execute single or multiple Callables and avoid blocking

I execute a few callables through ThreadPoolExecutor. If thread list contains only 1 callable then I directly call call method of my CallableService. If list contains more than 1 callables then I execute all those threads in parallel via thread pool executor.
How can I achieve this with Java 8 CompletableFuture? And if future.get() is enhanced to avoid blocking, that will be a plus.
private static ThreadPoolExecutor myThreadPoolExecutor = new ThreadPoolExecutor(0, 100, 5L, TimeUnit.SECONDS, new SynchronousQueue<>());
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
List<Future<Boolean>> futureList = null;
CallableService singleService = (CallableService) threadList.get(0);
if (1 == threadList.size()) {
singleService.call();
}
else {
try {
futureList = myThreadPoolExecutor.invokeAll(threadList);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
if (null != futureList) {
for (Future<Boolean> future : futureList) {
try {
future.get();
}
catch (Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
}
There is no need for CompletableFuture, as the way you use the ExecutorService is sufficient, though, there are some aspects of the code flow which could be improved. You fetch the first element, even when not needed, and you cast it to CallableService for no reason, as you can call the method via the Callable interface already. In the other branch you are catching InterruptedException and proceeding, so the caller would never know that not all jobs have been executed. And in a straight-forward code flow, you don't need to check the list for null:
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
if(1 == threadList.size()) {
Callable<Boolean> singleService = threadList.get(0);
singleService.call();
}
else {
List<Future<Boolean>> futureList = myThreadPoolExecutor.invokeAll(threadList);
for(Future<Boolean> future : futureList) {
try {
future.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
}
You could shorten it further to
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
if(1 == threadList.size()) {
threadList.get(0).call();
}
else {
for(Future<Boolean> future : myThreadPoolExecutor.invokeAll(threadList)) {
try {
future.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
}
But that's a matter of preferred coding style. But note that it caught my eye that in the single element case, you're not performing the same exception handling.
To use CompletableFuture, we need an adapter method, as the convenience method supplyAsync requires a Supplier instead of a Callable. Using a modified variant of this answer, we get
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
if(1 == threadList.size()) {
threadList.get(0).call();
}
else {
CompletableFuture<?> all = CompletableFuture.allOf(
threadList.stream()
.map(c -> callAsync(c, myThreadPoolExecutor))
.toArray(CompletableFuture<?>[]::new));
try {
all.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
}
public static <R> CompletableFuture<R> callAsync(Callable<R> callable, Executor e) {
CompletableFuture<R> cf = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try { cf.complete(callable.call()); }
catch(Throwable ex) { cf.completeExceptionally(ex); }
}, e);
return cf;
}
So we have no invokeAll which takes care of submitting all jobs. We have to do this manually, either with a loop or a stream operation. On the other hand, we get a single future via allOf representing the completion status, exceptionally if at least one job failed.
Unlike invokeAll, which waits for the completion, allOf only returns the future so it is the all.get() call which waits for the completion. We could do other things before it or even use this property to always perform the first job in the caller thread:
public static void execute(List<Callable<Boolean>> threadList) throws Exception {
CompletableFuture<?> tail = CompletableFuture.allOf(
threadList.stream().skip(1)
.map(c -> callAsync(c, myThreadPoolExecutor))
.toArray(CompletableFuture<?>[]::new)),
head = callAsync(threadList.get(0), Runnable::run);
try {
head.get();
tail.get();
}
catch(Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
This will always call the first callable in the current thread, as Runnable::run used as Executor will perform the action immediately in the calling thread. But it's treated uniformly in all other aspects, especially the exception handling. When there is only one job, allOf invoke with an empty array will do nothing and return an already completed future, which will have the desired effect.
Future.isDone() tells us if the executor has finished processing the task. If the task is completed, it will return true otherwise, it returns false.
for (Future<Boolean> future : futureList) {
while(!future.isDone())
{
doSOmethingElse();
Thread.sleep(300);//Optional
}
try {
future.get();
}
catch (Exception e)
{
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
But we don't have to worry about that since we get to the point where get() is called after making sure that the task is finished.
I execute a few callables through ThreadPoolExecutor. If thread list contains only 1 callable then I directly call call method of my CallableService. If list contains more than 1 callables then I execute all those threads in parallel via thread pool executor.
I guess you have already implemented this part. (You might run into memory usage issues if your jobs are heavy and you have 100 threads running as configured. But that is a different problem.)
And if future.get() is enhanced to avoid blocking, that will be a plus.
For this, you may take this approach:
Create another ExecutorService whose job will be just to run the Future.get() calls.
Submit your Future.get() to that service as shown below.
Shut it down and await termination.
if (null != futureList) {
ExecutorService waitSvc = Executors.newCachedThreadPool();
for (Future<Boolean> future : futureList) {
try {
waitSvc.submit( () -> future.get() );
}
catch (Exception e) {
//do some calculations here and then throw exception
throw new Exception(e.getMessage(), e);
}
}
waitSvc.shutdown(); //This may take some time. You may want to call awaitTermination() after this.
}
However, I feel that you should redesign the overall approach of using so many threads, unless this is only a for-learning application.

Why doesn't my thread wait for CompletableFutures to complete with `allOf()`?

I'm studying CompletableFuture in Java 1.8 and having trouble trying to understand allOf. It seems the main thread doesn't wait for any CompletableFuture to complete.
See https://github.com/nurkiewicz/reactive/blob/master/src/test/java/be/more/reactive/S03_AllOf.java for the example I'm testing.
The test job finishes before any result is printed.
There are two (ugly?) ways to circumvent this: 1) set a timeout on the main thread and wait for both to finish. 2) set a .get() at the end and it will become a blocking task.
Why is this?
Code fragment:
package be.more.reactive;
import be.more.reactive.util.BaseTest;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
public class S03_AllOf extends BaseTest {
private static final Logger log = LoggerFactory.getLogger(S03_AllOf.class);
private final CompletableFuture<String> futureResult1 = getFutureQueryResult("1"); //.exceptionally() ??
private final CompletableFuture<String> futureResult2 = getFutureQueryResult("2");
private final CompletableFuture<String> futureResult3 = getFutureQueryResult("3");
private final CompletableFuture<String> futureResult4 = getFutureQueryResult("4");
#Test
public void allOf() throws Exception {
final CompletableFuture<Void> futureResult = CompletableFuture.allOf( //Void ?? I want List<String>
futureResult1, futureResult2, futureResult3, futureResult4
);
// futureResult.thenAccept((Void vd) -> vd.??) //no, it won't work
futureResult.thenRun(() -> {
try {
log.debug("Query result 1: '{}'", futureResult1.get());
log.debug("Query result 2: '{}'", futureResult2.get());
log.debug("Query result 3: '{}'", futureResult3.get());
log.debug("Query result 4: '{}'", futureResult4.get()); //a lot of manual work
log.debug("Now do on complete"); //handling onComplete
} catch (Exception e) {
log.error("", e);
}
});
}
}
And in BaseTest:
protected CompletableFuture<String> getFutureQueryResult(final String queryId) {
return CompletableFuture.supplyAsync(
() -> db.apply(new Query(queryId))
);
}
And in DB.java
package be.more.reactive.db;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.apache.commons.lang3.RandomUtils.nextInt;
import static org.apache.commons.lang3.RandomUtils.nextLong;
public class DB {
public String apply(Query query) {
try {
TimeUnit.SECONDS.sleep(nextLong(2, 4));
} catch (InterruptedException e) {
e.printStackTrace();
}
return String.format("%s_%s", randomAlphabetic(nextInt(4, 12)), query.getId());
}
}
From the Javadoc
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete.
A Future is an async task which doesn't block until you invoke get (only blocks if the task is still running).
In this case, the CompleteableFuture is a compound Future of all the CompletableFutures. This future is still going to be a blocking async call and you must invoke get or join to wait for all futures to complete. Again, from the javadoc
CompletableFutures before continuing a program, as in: CompletableFuture.allOf(c1, c2, c3).join();.
Your (2) solution, in my opinion, is neither ugly nor unexpected functionality.
The behavior you are seeing is not unexpected. When you create a CompletableFuture, you are basically scheduling a piece of work that would run asynchronously.
Things to understand about CompletableFuture before using allOf
Let's say we create a CompletableFuture like so
var myFuture = CompletableFuture.supplyAsync(() -> myLongRunningOperation());
A CompletableFuture would call myLongRunningOperation on a separate thread.
CompletableFuture runs the tasks using an ExecutorService, that can be provided during the creation of the CompletableFuture.
If no ExecutorService is provided, it uses the one provided by ForkJoinPool#commonPool, which provides a thread pool of Daemon Threads.
The task () -> myLongRunningOperation() would be submitted to the ExecutorService irrespective of whether someone is waiting for the result of myFuture, i.e. irrespective of whether myFuture.join() or myFuture.get() are called.
In your test method, this is what is happening behind the scenes
#Test
public void allOf() throws Exception {
// Schedules a computation (futureResult) on a different thread whose only
// work is to wait for the futures futureResult1, futureResult2, futureResult3
// and futureResult4 to complete
final CompletableFuture<Void> futureResult = CompletableFuture.allOf(
futureResult1, futureResult2, futureResult3, futureResult4
);
// Schedules a computation that prints the results AFTER the futureResult is complete.
futureResult.thenRun(() -> {
try {
log.debug("Query result 1: '{}'", futureResult1.get());
log.debug("Query result 2: '{}'", futureResult2.get());
log.debug("Query result 3: '{}'", futureResult3.get());
log.debug("Query result 4: '{}'", futureResult4.get());
log.debug("Now do on complete");
} catch (Exception e) {
log.error("", e);
}
});
// Nothing more to do, so exit
}
but, when you call .join() or .get() the thread executing the Test (main thread) will wait for it the scheduled computation to complete before the it exits.
So, if you want your test to wait for the scheduled computation to complete before it exists,
// Schedules a computation that prints the results ONCE the futureResult is complete.
final CompletableFuture<Void> myFuture = futureResult.thenRun(() -> {
try {
log.debug("Query result 1: '{}'", futureResult1.get());
log.debug("Query result 2: '{}'", futureResult2.get());
log.debug("Query result 3: '{}'", futureResult3.get());
log.debug("Query result 4: '{}'", futureResult4.get()); //a lot of manual work
log.debug("Now do on complete"); //handling onComplete
} catch (Exception e) {
log.error("", e);
}
});
// Wait for the myFuture to complete (sucessfully or throw an exception) before continuing.
myFuture.get();
Setting a timeout on the main thread to wait for the Future to complete is an anti-pattern.
If you care about the result and need to wait for the future to
complete, call join() or get() based on how you want to do
exception handling.
If you do not care about the result, but would like the application
to wait for the future to complete, then create a custom executor
service that creates non-daemon threads.

How to use completableFuture in testNG class

I want to run CompletableFuture in a TestNG class with #Test annotation.
Below is the code snippet, recursionFuture method is called recursively to do a certain task by the main() method.
Now if I use main() as public static void main(String[] args)) then everything works as desired. But when I used main() with #Test annotation then TestNG stops the execution in between and the whole task is not performed.
What should I do to make #Test to wait until all tasks are completed by recursionFuture?
I've to use CompletableFuture for async tasks and need to use #Test also.
Any help would be appreciated. Thanks.
//public static void main(String[] args) throws FileNotFoundException, InterruptedException, ExecutionException
#Test // --> this logic of recursion with threads is problematic with testNG
public static void main() throws FileNotFoundException, InterruptedException, ExecutionException
{
System.setOut(new PrintStream(new File("/Users/Pankaj/Desktop/Thread")));
Method[] method = ConcurrencyPoC_CompletableFuture.class.getMethods();
for(int i=0; i<method.length; i++)
{
if(method[i].getName().startsWith("task"))
{
TreeMap<Object, Object> m = new TreeMap<>();
m.put(method[i].getName(), method[i]);
m.put("status", new AtomicBoolean(true));
taskmap.put(method[i].getName(), m);
}
}
//submitting tasks
ExecutorService ex = Executors.newCachedThreadPool();
CompletableFuture<?> [] arrayFutures = new CompletableFuture[3];
recursionFuture(ex, arrayFutures);
}
public static String recursionFuture(ExecutorService ex, CompletableFuture<?> [] arrayFutures)
{
try
{
//check if any of submitted future is completed - to be used in case of array
for(CompletableFuture<?> future : arrayFutures)
{
future = CompletableFuture.supplyAsync(() -> new ConcurrencyPoC_CompletableFuture().executeTask(), ex);
//storing future in a final variable for further calculation
final CompletableFuture<?> task = future;
CompletableFuture.anyOf(task).thenRunAsync(() ->
{
try {
//apply recursion only when future's output is not null, otherwise there will be hell lot of futures get created which
//don't do anything just eating up memory and executing the else block of executeTask() method.
// Latest change, as soon as any task is free, create a new array of 1 size to do the next task
if(task.get() != null)
recursionFuture(ex, new CompletableFuture[1]);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
, ex);
}
}
catch(Exception e)
{
e.printStackTrace();
}
return "ALl TASK COMPLETED";
}
There is no correspondence between TestNG #Test annotation and your completable future.
The problem is in your tast.get() method. It should be blocking running threads until all completable futures are finished.
I use Completable Future in my tests and never ever I ever got into a problem with TestNG.
The problem is one of your future's finishes, and you return to the test method instead of waiting for all the futures. You should combine all your futures with thenApplyAsync() or compose() because your Future is final you are waiting only for one future. Plus you should not use CompletbleFuture.Any() because it returns to execution when the first future is complete.

Categories

Resources