/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.linkedin.pinot.transport.common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.linkedin.pinot.common.response.ServerInstance;
import com.linkedin.pinot.transport.common.CompositeFuture.GatherModeOnError;
public class CompositeFutureTest {
protected static Logger LOGGER = LoggerFactory.getLogger(CompositeFutureTest.class);
@Test
/**
* Happy path. we got response from all the futures
* @throws Exception
*/
public void testMultiFutureComposite1() throws Exception {
List<ServerInstance> keys = new ArrayList<>();
int numFutures = 100;
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
Map<ServerInstance, String> expectedMessages = new HashMap<>();
for (int i = 0; i < numFutures; i++) {
ServerInstance key = new ServerInstance("localhost:" + i);
keys.add(key);
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key, "");
futureMap.put(key, future);
}
CompositeFuture<String> compositeFuture =
new CompositeFuture<String>("test", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
// Send response for underlying futures
for (int i = 0; i < numFutures; i++) {
String message = "dummy Message_" + i;
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
future.onSuccess(message);
expectedMessages.put(k, message);
}
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getError().isEmpty(), "Composite No Error :");
Map<ServerInstance, String> runnerResponse = runner.getMessage();
Map<ServerInstance, String> listenerResponse = listener.getMessage();
for (int i = 0; i < numFutures; i++) {
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertEquals(future.getOne(), expectedMessages.get(k), "Reponse :");
Assert.assertNull(future.getError(), "No Error :");
Assert.assertEquals(runnerResponse.get(k), expectedMessages.get(k), "Message_" + i);
Assert.assertEquals(listenerResponse.get(k), expectedMessages.get(k), "Message_" + i);
}
Assert.assertFalse(listener.isCancelled(), "listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "listener Is Done ? ");
Assert.assertTrue(listener.getError().isEmpty(), "listener No Error :");
executor.shutdown();
}
@Test
/**
* We got error from all the futures and stoponFirstError is false
* @throws Exception
*/
public void testMultiFutureComposite2() throws Exception {
List<ServerInstance> keys = new ArrayList<>();
int numFutures = 100;
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
Map<ServerInstance, Exception> expectedErrors = new HashMap<>();
for (int i = 0; i < numFutures; i++) {
ServerInstance key = new ServerInstance("localhost:" + i);
keys.add(key);
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key, "");
futureMap.put(key, future);
}
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
// Send response for underlying futures
for (int i = 0; i < numFutures; i++) {
Exception expectedError = new Exception("error processing_" + i);
ServerInstance k = new ServerInstance("localhost:" + i);
ServerResponseFuture<String> future = futureMap.get(k);
((AsyncResponseFuture<String>) future).onError(expectedError);
expectedErrors.put(k, expectedError);
}
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getMessage().isEmpty(), "Composite No Response :");
Map<ServerInstance, Throwable> runnerException = runner.getError();
Map<ServerInstance, Throwable> listenerException = listener.getError();
for (int i = 0; i < numFutures; i++) {
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertEquals(future.getError().values().iterator().next(), expectedErrors.get(k), "Error :");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertEquals(runnerException.get(k), expectedErrors.get(k), "Message_" + i);
Assert.assertEquals(listenerException.get(k), expectedErrors.get(k), "Message_" + i);
}
Assert.assertFalse(listener.isCancelled(), "listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "listener Is Done ? ");
Assert.assertTrue(listener.getMessage().isEmpty(), "listener No Response :");
executor.shutdown();
}
@Test
/**
* Cancelled Future. Future Client calls get() and another listens before cancel().
* A response and exception arrives after cancel but they should be discarded.
*/
public void testMultiFutureComposite3() throws Exception {
List<ServerInstance> keys = new ArrayList<>();
int numFutures = 100;
int numSuccessFutures = 50;
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
for (int i = 0; i < numFutures; i++) {
ServerInstance key = new ServerInstance("localhost:" + i);
keys.add(key);
AsyncResponseFuture<String> future = new AsyncResponseFuture<String>(key, "");
futureMap.put(key, future);
}
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
compositeFuture.cancel(false);
// Send response for some of the underlying futures
for (int i = 0; i < numSuccessFutures; i++) {
String message = "dummy Message_" + i;
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
future.onSuccess(message);
}
// Send exception for some of the underlying futures
for (int i = numSuccessFutures; i < numFutures; i++) {
Exception expectedError = new Exception("error processing_" + i);
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
future.onError(expectedError);
}
runner.waitForDone();
Assert.assertTrue(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getMessage().isEmpty(), "Composite No Reponse :");
Assert.assertTrue(runner.getError().isEmpty(), "Composite No Error :");
for (int i = 0; i < numFutures; i++) {
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
Assert.assertTrue(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertNull(future.getError(), "No Error :");
}
Assert.assertTrue(listener.isCancelled(), "listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "listener Is Done ? ");
Assert.assertTrue(listener.getMessage().isEmpty(), "listener No Reponse :");
Assert.assertTrue(listener.getError().isEmpty(), "listener No Error :");
executor.shutdown();
}
@Test
/**
* 100 futures, we get responses from 5 and then get an error. stopOnFirstError = true
* We expect all the futures to be marked done. the 5 futures will have responses. The next one will have error.
* Rest will be cancelled.
* @throws Exception
*/
public void testMultiFutureComposite4() throws Exception {
List<ServerInstance> keys = new ArrayList<>();
int numFutures = 100;
int numSuccessFutures = 5;
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
Map<ServerInstance, String> expectedMessages = new HashMap<>();
for (int i = 0; i < numFutures; i++) {
ServerInstance key = new ServerInstance("localhost:" + i);
keys.add(key);
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key, "");
futureMap.put(key, future);
}
CompositeFuture<String> compositeFuture =
new CompositeFuture<>("a", GatherModeOnError.SHORTCIRCUIT_AND); //stopOnError = true
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
// Send response for underlying futures
for (int i = 0; i < numSuccessFutures; i++) {
String message = "dummy Message_" + i;
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
future.onSuccess(message);
expectedMessages.put(k, message);
}
// Send error. This should complete the future/.
ServerInstance errorKey = new ServerInstance("localhost:" + numSuccessFutures);
Exception expectedException = new Exception("Exception");
AsyncResponseFuture<String> f = (AsyncResponseFuture<String>) futureMap.get(errorKey);
f.onError(expectedException);
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertFalse(listener.isCancelled(), "listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "listener Is Done ? ");
Map<ServerInstance, Throwable> runnerException = runner.getError();
Map<ServerInstance, String> runnerMessages = runner.getMessage();
Map<ServerInstance, String> listenerMessages = listener.getMessage();
Map<ServerInstance, Throwable> listenerException = listener.getError();
for (int i = 0; i < numSuccessFutures; i++) {
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertEquals(future.getOne(), expectedMessages.get(k), "Reponse :");
Assert.assertNull(future.getError(), "No Error :");
Assert.assertEquals(runnerMessages.get(k), expectedMessages.get(k), "Message_" + i);
Assert.assertEquals(listenerMessages.get(k), expectedMessages.get(k), "Message_" + i);
}
ServerInstance key1 = new ServerInstance("localhost:" + numSuccessFutures);
f = (AsyncResponseFuture<String>) futureMap.get(key1);
Assert.assertFalse(f.isCancelled(), "Cancelled ?");
Assert.assertTrue(f.isDone(), "Is Done ? ");
Assert.assertEquals(f.getError().values().iterator().next(), expectedException, "Exception :");
Assert.assertNull(f.get(), "No Response :");
Assert.assertEquals(runnerException.get(key1), expectedException, "Exception_" + numSuccessFutures);
Assert.assertEquals(listenerException.get(key1), expectedException, "Exception_" + numSuccessFutures);
for (int i = numSuccessFutures + 1; i < numFutures; i++) {
ServerInstance k = new ServerInstance("localhost:" + i);
AsyncResponseFuture<String> future = (AsyncResponseFuture<String>) futureMap.get(k);
Assert.assertTrue(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertNull(future.getError(), "No Error :");
Assert.assertNull(runnerMessages.get(k));
Assert.assertNull(listenerMessages.get(k));
Assert.assertNull(runnerException.get(k));
Assert.assertNull(listenerException.get(k));
}
executor.shutdown();
}
@Test
/**
* Tests Composite future with one underlying future.
* @throws Exception
*/
public void testSingleFutureComposite() throws Exception {
ServerInstance key1 = new ServerInstance("localhost:8080");
//Cancelled Future. Future Client calls get() and another listens before cancel().
// A response and exception arrives after cancel but they should be discarded.
{
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key1, "");
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
futureMap.put(key1, future);
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
compositeFuture.cancel(false);
String message = "dummy Message";
future.onSuccess(message);
Exception expectedError = new Exception("error processing");
future.onError(expectedError);
runner.waitForDone();
Assert.assertTrue(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getMessage().isEmpty(), "Composite No Reponse :");
Assert.assertTrue(runner.getError().isEmpty(), "Composite No Error :");
Assert.assertTrue(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertNull(future.getError(), "No Error :");
Assert.assertTrue(listener.isCancelled(), "listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "listener Is Done ? ");
Assert.assertTrue(listener.getMessage().isEmpty(), "listener No Reponse :");
Assert.assertTrue(listener.getError().isEmpty(), "listener No Error :");
executor.shutdown();
}
//Cancelled Future. Future Client calls get() and another listens after cancel()
// A response and exception arrives after cancel but they should be discarded.
{
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key1, "");
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
futureMap.put(key1, future);
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.cancel(false);
String message = "dummy Message";
future.onSuccess(message);
Exception expectedError = new Exception("error processing");
future.onError(expectedError);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
runner.waitForDone();
Assert.assertTrue(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getMessage().isEmpty(), "Composite No Reponse :");
Assert.assertTrue(runner.getError().isEmpty(), "Composite No Error :");
Assert.assertTrue(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertNull(future.getError(), "No Error :");
Assert.assertTrue(listener.isCancelled(), "listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "listener Is Done ? ");
Assert.assertTrue(listener.getMessage().isEmpty(), "listener No Reponse :");
Assert.assertTrue(listener.getError().isEmpty(), "listener No Error :");
executor.shutdown();
}
// Throw Exception. Future Client calls get() and another listens before exception
// A response and cancellation arrives after exception but they should be discarded.
{
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key1, "");
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
futureMap.put(key1, future);
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
Exception expectedError = new Exception("error processing");
future.onError(expectedError);
compositeFuture.cancel(false);
String message = "dummy Message";
future.onSuccess(message);
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getMessage().isEmpty(), "Composite No Reponse :");
Assert.assertEquals(runner.getError().get(key1), expectedError, "Composite Error");
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertEquals(future.getError().values().iterator().next(), expectedError, "Error");
Assert.assertFalse(listener.isCancelled(), "Listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "Listener Is Done ? ");
Assert.assertTrue(listener.getMessage().isEmpty(), "Listener No Reponse :");
Assert.assertEquals(listener.getError().get(key1), expectedError, "Listener Error");
executor.shutdown();
}
// Throw Exception. Future Client calls get() and another listens after exception
// A response and cancellation arrives after exception but they should be discarded.
{
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key1, "");
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
futureMap.put(key1, future);
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
Exception expectedError = new Exception("error processing");
future.onError(expectedError);
compositeFuture.cancel(false);
String message = "dummy Message";
future.onSuccess(message);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getMessage().isEmpty(), "Composite No Reponse :");
Assert.assertEquals(runner.getError().get(key1), expectedError, "Composite Error");
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertNull(future.get(), "No Reponse :");
Assert.assertEquals(future.getError().values().iterator().next(), expectedError, "Error");
Assert.assertFalse(listener.isCancelled(), "Listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "Listener Is Done ? ");
Assert.assertTrue(listener.getMessage().isEmpty(), "Listener No Reponse :");
Assert.assertEquals(listener.getError().get(key1), expectedError, "Listener Error");
executor.shutdown();
}
// Get Response. Future Client calls get() and another listens before response
// An exception and cancellation arrives after exception but they should be discarded.
{
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key1, "");
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
futureMap.put(key1, future);
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
String message = "dummy Message";
future.onSuccess(message);
Exception expectedError = new Exception("error processing");
future.onError(expectedError);
compositeFuture.cancel(false);
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getError().isEmpty(), "Composite No Error :");
Assert.assertEquals(runner.getMessage().get(key1), message, "Composite Message");
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertEquals(future.getOne(), message, "Reponse :");
Assert.assertNull(future.getError(), "No Error");
Assert.assertFalse(listener.isCancelled(), "Listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "Listener Is Done ? ");
Assert.assertTrue(listener.getError().isEmpty(), "listener No Error :");
Assert.assertEquals(listener.getMessage().get(key1), message, "listener Message");
executor.shutdown();
}
// Get Response. Future Client calls get() and another listens after response
// An exception and cancellation arrives after exception but they should be discarded.
{
AsyncResponseFuture<String> future = new AsyncResponseFuture<>(key1, "");
Map<ServerInstance, ServerResponseFuture<String>> futureMap = new HashMap<>();
futureMap.put(key1, future);
CompositeFuture<String> compositeFuture = new CompositeFuture<>("a", GatherModeOnError.AND);
compositeFuture.start(futureMap.values());
ResponseCompositeFutureClientRunnerListener runner =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
ResponseCompositeFutureClientRunnerListener listener =
new ResponseCompositeFutureClientRunnerListener(compositeFuture);
String message = "dummy Message";
future.onSuccess(message);
Exception expectedError = new Exception("error processing");
future.onError(expectedError);
compositeFuture.cancel(false);
compositeFuture.addListener(listener, null);
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(runner);
runner.waitForAboutToGet(); // No guarantees as this only ensures the thread is started but not blocking in get().
Thread.sleep(100);
runner.waitForDone();
Assert.assertFalse(runner.isCancelled(), "Composite Cancelled ?");
Assert.assertTrue(runner.isDone(), "Composite Is Done ? ");
Assert.assertTrue(runner.getError().isEmpty(), "Composite No Error :");
Assert.assertEquals(runner.getMessage().get(key1), message, "Composite Message");
Assert.assertFalse(future.isCancelled(), "Cancelled ?");
Assert.assertTrue(future.isDone(), "Is Done ? ");
Assert.assertEquals(future.getOne(), message, "Reponse :");
Assert.assertNull(future.getError(), "No Error");
Assert.assertFalse(listener.isCancelled(), "Listener Cancelled ?");
Assert.assertTrue(listener.isDone(), "Listener Is Done ? ");
Assert.assertTrue(listener.getError().isEmpty(), "listener No Error :");
Assert.assertEquals(listener.getMessage().get(key1), message, "listener Message");
executor.shutdown();
}
}
/**
* Same class used both as a listener and the one that blocks on get().
*/
private static class ResponseCompositeFutureClientRunnerListener implements Runnable {
private boolean _isDone;
private boolean _isCancelled;
private Map<ServerInstance, String> _message;
private Map<ServerInstance, Throwable> _errorMap;
private final CompositeFuture<String> _future;
private final CountDownLatch _latch = new CountDownLatch(1);
private final CountDownLatch _endLatch = new CountDownLatch(1);
public ResponseCompositeFutureClientRunnerListener(CompositeFuture<String> f) {
_future = f;
}
public void waitForAboutToGet() throws InterruptedException {
_latch.await();
}
public void waitForDone() throws InterruptedException {
_endLatch.await();
}
@Override
public synchronized void run() {
LOGGER.info("Running Future runner !!");
Map<ServerInstance, String> message = null;
try {
_latch.countDown();
message = _future.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
_isDone = _future.isDone();
_isCancelled = _future.isCancelled();
if (null != message) {
_message = message;
}
_errorMap = _future.getError();
_endLatch.countDown();
LOGGER.info("End Running Listener !!");
}
public boolean isDone() {
return _isDone;
}
public boolean isCancelled() {
return _isCancelled;
}
public Map<ServerInstance, String> getMessage() {
return _message;
}
public Map<ServerInstance, Throwable> getError() {
return _errorMap;
}
public CompositeFuture<String> getFuture() {
return _future;
}
}
}