How to Access the Callable Task's input parameter inside thenAccept Method? - java

I have the following methods to invoke some logic in a non-blocking way.
public void runParallelFunctions(Callable<Map<String, String>> invokerTask) {
Supplier<Map<String, String>> taskSupplier = () -> {
try {
Thread.sleep(4000);
return invokerTask.call();
} catch (Exception e) {
System.out.println(e);
}
return new HashMap<>();
};
for (int i = 0; i < 5; i++) {
CompletableFuture.supplyAsync(taskSupplier::get, executorService)
.thenAccept(this::printResultsFromParallelInvocations);
}
System.out.println("Doing other work....");
}
private void printResultsFromParallelInvocations(Map<String, String> result) {
result.forEach((key, value) -> System.out.println(key + ": " + value));
}
Below is the Callable I'm passing to the runParallelFunctions method above.
public Callable<Map<String, String>> createLambdaInvokerTask(final String payload) {
return () -> invokeLambda(payload);
}
How can I access the payload parameter of createLambdaInvokerTask method in my printResultsFromParallelInvocations method? I need to concatenate the payload parameter to the result and print it. Any advice would be much appreciated.

Related

Return the future that gets executed first with a specific condition on the response

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.

Wait for a CompletableFuture to be complete, then return a different value?

I have a CompletableFuture<Void> that calls an asynchronous method whose return type I can't change, or anything about it.
I want to wait for this method to be complete (I manually complete it), and then return a String value, how would I do this?
public String getServer(Player p) {
FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
sendUTF(p, "GetServer");
try {
Thread.sleep(10000); //so the future doesnt complete itself
} catch (Exception e) {
e.printStackTrace();
}
}), p.getUniqueId().toString());
serverSet.add(f);
String server = "";
//server isn't final so I can't use it in the lambda
f.getFutureVoid().whenComplete(v -> server = f.getServer());
return server;
}
public class FutureServer {
private CompletableFuture<Void> futureVoid;
private String s;
private String uuid;
public FutureServer(CompletableFuture<Void> futureVoid, String uuid) {
this.futureVoid = futureVoid;
this.uuid = uuid;
}
public String getUuid() {
return uuid;
}
public CompletableFuture<Void> getFutureVoid() {
return futureVoid;
}
public boolean hasServer() {
return s != null;
}
public void setServer(String s) {
this.s = s;
}
public String getServer() {
return s;
}
}
I want to set string to equal FutureServer#getServer() (own method), but I need to wait until the CompletableFuture<Void> is completed. What do I do?
This is the method that gets called async and is unchangeable... the method I use that calls this other method asynchronously is sendUTF().
#Override
public void onPluginMessageReceived(String s, Player p, byte[] bytes) {
if (!s.equals("BungeeCord")) return;
ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
String subChannel = in.readUTF();
switch(subChannel) {
case "GetServer":
String server = in.readUTF();
serverSet.stream().filter(f -> f.getUuid().equals(p.getUniqueId().toString())).findFirst().ifPresent(f -> {
f.setServer(server); //getting the string I need and placing it into this object
f.getFutureVoid().complete(null); //completing the void future manually
});
break;
}
}
You could do this:
final AtomicReference<String> server = new AtomicReference<>("");
f.getFutureVoid().whenComplete(v -> server.set(f.getServer())).get(/* maybe add a timeout */);
return server.get();
The simplest solution is simply to join() on that future, either with:
public String getServer(Player p) {
FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
sendUTF(p, "GetServer");
try {
Thread.sleep(10000); //so the future doesnt complete itself
} catch (Exception e) {
e.printStackTrace();
}
}), p.getUniqueId().toString());
serverSet.add(f);
return f.getFutureVoid().thenApply(v -> f.getServer()).join();
}
which can easily be transformed to return a CompletableFuture<String> instead by removing the .join(), or also:
public String getServer(Player p) {
FutureServer f = new FutureServer(CompletableFuture.runAsync(() -> {
sendUTF(p, "GetServer");
try {
Thread.sleep(10000); //so the future doesnt complete itself
} catch (Exception e) {
e.printStackTrace();
}
}), p.getUniqueId().toString());
serverSet.add(f);
f.getFutureVoid().join();
return f.getServer();
}

CompletableFuture handle and completeExceptionally cannot work together?

I am trying to set a default value when exceptions happen in CompletableFuture I made it work by handle method as follows:
private static void testHandle() {
String name = null;
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
}).handle((s, t) -> s != null ? s : "Hello, Stranger!" + t.toString());
out.println(completableFuture.join());
}
But when I tried to stop the CompletableFuture using completeExceptionally when bad things happen and track the exception as follows I cannot catch the exception as I did just now.
private static void testCompleteExceptionally() {
String name = "Hearen";
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
}).handle((s, t) -> {
try {
throw t.getCause();
} catch (Throwable e) {
out.println(e.toString()); // I was hoping to record the custom exceptions here;
}
return s != null ? s : "Hello, Stranger!" + t.toString();
});
if (name != null) {
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!")); // when bad things happen, I try to complete it by exception;
}
out.println(completableFuture.join());
}
UPDATED 2018-06-09 Thanks for the help, #Daniele
private static void testCompleteExceptionally() {
String name = "Hearen";
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
});
if (name != null) {
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
}
out.println(completableFuture.handle((s, t) -> s != null ? s : "Hello, Stranger!" + t.toString()).join());
}
The handle enclosed just before join() works as expected. But in this case, the returned value will be null.
Based on the handle API
Returns a new CompletionStage that, when this stage completes either normally or exceptionally, is executed with this stage's result and exception as arguments to the supplied function.
You are building a future, piping with an handle (so getting another future) and then completing exceptionally the future returned by the handle.
You should complete exceptionally the inner future itself, instead of the handle.
The point here is that handle returns another future; and you should not complete the "outer" future exceptionally, because doing that will bypass the handling behavior.
Below the code;
package stackOv;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class TestHandle {
BiFunction<String, Throwable, String> handle2 = new BiFunction<String, Throwable, String>() {
#Override
public String apply(String s, Throwable t) {
try {
throw t.getCause();
} catch (Throwable e) {
// I was hoping to record the custom exceptions here;
System.out.println(e.toString());
}
return s != null ? s : "Hello, Stranger!" + t.toString();
}
};
private void testCompleteExceptionally() {
String name = "Hearen";
Supplier<String> supplier2 = () -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
};
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(supplier2);
if (name != null) {
// when bad things happen, I try to complete it by exception;
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
}
System.out.println(completableFuture.handle(handle2).join());
}
public static void main(String[] args) {
TestHandle th = new TestHandle();
th.testCompleteExceptionally();
}
private static void delay(long milli) {
try { Thread.sleep(milli); } catch (InterruptedException e) {}
}
}

Unit testing a void method with Redis async command in java

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");
}
}
}

how to store handler result into JsonArray vertx?

I am facing a problem,I can't store the handler result into Json Array.Every time the array is empty . I tried to use Future but it still the same problem, here is my code :
static void getCaraReferTypeDocBin(RoutingContext routingContext){
String ids = routingContext.request().getParam("ids");
logger.debug(ids);
String[] idsArray = ids.split(",");
JsonArray caraRefTypeDocBin = new JsonArray();
for (int i = 0; i <idsArray.length ; i++) {
GET.getCaraTypeDocBin(Integer.parseInt(idsArray[i]), res->{
if (res.succeeded()){
logger.debug(res.result());
caraRefTypeDocBin.add(res.result());
}else{
logger.debug(res.cause().getMessage());
}
});
}
logger.debug(caraRefTypeDocBin);
}
this is getCaraReferTypeDocBin implementation :
public static void getCaraTypeDocBin(int id ,Handler<AsyncResult<JsonArray>> resultHandler) {
JsonArray pIn = new JsonArray();
pIn.add(new JsonObject().put("pos", 2).put("type", OracleTypes.NUMBER).put("val", id));
JsonArray pOut = new JsonArray().add(new JsonObject().put("pos", 1).put("type", OracleTypes.CURSOR));
DB.cursor(SQL.LIST_CARA_TYPE_DOC_BIN,pIn,pOut, res -> {
if (res.succeeded()) {
try {
resultHandler.handle(Future.succeededFuture(res.result().getJsonArray("1")));
}catch (Exception e){
logger.error(e);
resultHandler.handle(Future.failedFuture(Error.ERROR_OCCURED.toString()));
}
} else {
resultHandler.handle(Future.failedFuture(res.cause().getMessage()));
}
});
}
In async systems api with futures should be written something like this:
private Future<String> loadFromDb() {
Future<String> f = Future.future();
//some internal loading from db
String result = "fromDb";
//when loading completes, pass it to future result
f.complete(result);
return f;
}
And how it uses:
private void handleSo(RoutingContext routingContext) {
loadFromDb()
.map(new Function<String, JsonArray>() {
#Override
public JsonArray apply(String fromDb) {
//map to json
return new JsonArray(...);
}
})
.setHandler(
new Handler<AsyncResult<JsonArray>>() {
#Override
public void handle(AsyncResult<JsonArray> result) {
routingContext.response().end(result.result().toString());
}
}
);
}
You are using futures wrong. You example simple and you haven't async chains (where result calculates based on previous result etc), so instead futures, you can simple use callback:
private void loadFromDb(Handler<String> handler) {
String result = "fromDb";
handler.handle(result);
}
private void handleSo(RoutingContext routingContext) {
loadFromDb(new Handler<String>() {
#Override
public void handle(String fromDb) {
routingContext.response().end(new JsonArray(...).toString());
}
});
}
Upd You need collect results from multiple async calls doc. Don't know how to implement it with you callback style api. But with futures it's not problem:
private void handleSo(RoutingContext routingContext) {
List<Future> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
//make 10 async calls
futures.add(loadFromDb()
.map(new Function<String, JsonObject>() {
#Override
public JsonObject apply(String fromDb) {
return new JsonObject().put("data", fromDb);
}
}));
}
CompositeFuture.all(futures)
.map(new Function<CompositeFuture, JsonArray>() {
#Override
public JsonArray apply(CompositeFuture compositeFuture) {
JsonArray array = new JsonArray();
List<JsonObject> jsons = compositeFuture.result().list();
jsons.forEach(jsonObject -> array.add(jsonObject));
return array;
}
})
.setHandler(new Handler<AsyncResult<JsonArray>>() {
#Override
public void handle(AsyncResult<JsonArray> res) {
routingContext.response().end(res.result().toString());
}
});
}
GET.getCaraTypeDocBin() runs asynchronously, right? And I assume there's something time-consuming in there like hitting a remote API? So the loop will run in milliseconds, kicking off all the requests, and then the "logger.debugs" will happen, and only then will the callbacks start to do caraRefTypeDocBin.add(res.result());
If I'm right, you should see the results logged before the empty array.

Categories