Java Spark: Non-blocking routes / callbacks with CompletableFutures - java

I have to make calls to long running methods in a Spark API implementation. These methods return CompletableFutures, so I'd like to free up the current Thread by triggering Spark to answer the client request in a callback.
As far as I can tell this is not possible with Spark, but I'd like to make sure I am not overlooking anything.
To illustrate the question, see the small code sample below.
import spark.Spark;
import java.util.concurrent.CompletableFuture;
public class HelloSpark {
public static void main(String[] args) {
Spark.get("/what_i_have_to_do", (req, res) -> {
CompletableFuture<String> f = callToSlowWorker();
return f.get();
});
Spark.get("/what_i_would_like_to_do", (req, res) -> {
CompletableFuture<String> f = callToSlowWorker();
f.whenComplete((answer, throwable) -> {
if(throwable != null){
// send error to requesting client
res.status(500);
res.body(throwable.getMessage());
} else {
// send answer to requesting client
res.body(answer);
}
// trigger spark to return the response to the client
// ...?!?
});
return null; // obviously not what I want, just to make this sample code compile
});
}
static CompletableFuture<String> callToSlowWorker(){
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
return "Hello World";
});
}
}

SparkJava is currently blocking only, thus what you've described is not possible. There is an open enhancement request to add support for a non blocking API.

Related

How to Invoke software.amazon.awssdk Asynchronous Lambda function from Java

I am using software.amazon.awssdk version 2.18.21 to invoke Lambda function from spring boot application. While invoking Lambda function which takes approx 2-3 minutes to finish throws Http status 504: Gateway Time-out Exception.
I have been suggested to use Asynchronous Call to invoke Lambda function and then read the response. How can i convert this existing code to asynchronous call and get response to verify if it was success or error?
//imports from
import software.amazon.awssdk.http.ApacheHttpClient;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.InvokeRequest;
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
//Calling Lambda Function
try{
LambdaClient client = LambdaClient.builder().httpClientBuilder(ApacheHttpClient.builder()
.maxConnections(100)
.socketTimeout(Duration.ofSeconds(60))
.connectionTimeout(Duration.ofSeconds(60))
).build();
InvokeRequest req = InvokeRequest.builder().functionName("abc").build();
InvokeResponse res = client.invoke(req);
String respnonse = res.payload().asUtf8String();
System.out.println(response);
}catch(Exception e){
e.printStackTrace();
}
Edited: Tried below but unable to implement CompletableFuture. Can you suggest how to implement it for Lambda function and get response
try{
SdkAsyncHttpClient client = NettyNioAsyncHttpClient.builder().readTimeout(Duration.ofSeconds(60)).connectionTimeout(Duration.ofSeconds(60)).build();
LambdaAsyncClient lambdaClient = LambdaAsyncClient .builder().httpClient(client).build();
InvokeRequest req = InvokeRequest.builder().functionName("abc").invocationType("EVENT").build();
CompletableFuture<InvokeResponse> request = lambdaClient.invoke(req);
//InvokeResponse res = client.invoke(req);
//String respnonse = res.payload().asUtf8String();
//System.out.println(response);
}catch(Exception e){
e.printStackTrace();
}
To perform Lambda operations using Asynchronous calls with the Java V2 API, use this:
LambdaAsyncClient
The call the invoke method:
https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaAsyncClient.html#invoke(software.amazon.awssdk.services.lambda.model.InvokeRequest)
The patten to use Async methods are similar no matter what the service use. For example, most calls involve using CompletableFuture. Then you code the logic you want within the whenComplete code block.
To learn about the patterns, read this part of the Java V2 DEV Guide:
Asynchronous programming
For example, this code example is from that topic.
public class DynamoDBAsyncListTables {
public static void main(String[] args) throws InterruptedException {
// Create the DynamoDbAsyncClient object
Region region = Region.US_EAST_1;
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
.region(region)
.build();
listTables(client);
}
public static void listTables(DynamoDbAsyncClient client) {
CompletableFuture<ListTablesResponse> response = client.listTables(ListTablesRequest.builder()
.build());
// Map the response to another CompletableFuture containing just the table names
CompletableFuture<List<String>> tableNames = response.thenApply(ListTablesResponse::tableNames);
// When future is complete (either successfully or in error) handle the response
tableNames.whenComplete((tables, err) -> {
try {
if (tables != null) {
tables.forEach(System.out::println);
} else {
// Handle error
err.printStackTrace();
}
} finally {
// Lets the application shut down. Only close the client when you are completely done with it.
client.close();
}
});
tableNames.join();
}
}

Mono.fromFuture triggering doAfterTerminate early in chain. misunderstanding or bug?

I'm trying to set up automatic cleanup of temp files, but some strange things happen when a Mono created from a CompletableFuture (from the S3 sdk) comes into play. The temp file is deleted too early in the chain. I am wondering whether this is a misunderstanding of reactor from my side, or a bug.
I've condensed the code to mimic the behavior I'm experiencing.
I would expect the output of this test to be:
uploaded file response
do other stuff
clean up file
However it is:
clean up file
uploaded file response
do other stuff
When not creating a Mono using Mono.fromFuture, everything works as expected.
#Test
void bla() throws InterruptedException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// "uploading"
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "uploaded file response";
});
Mono<String> createTmpFile = Mono.just("create tempFile")
.doAfterTerminate(() -> System.out.println("clean up file"));
Mono<String> doPersistenceThings = Mono.fromFuture(future)
.doOnNext(System.out::println)
.then(Mono.just("do other stuff"));
Mono.just("data")
.flatMap(d -> createTmpFile)
.flatMap(d -> doPersistenceThings)
.subscribe(System.out::println);
Thread.sleep(2000);
}

Vert.x - GraphQL Subscriptions with DataInputStreams

I have 3rd party code that I connect to via DataInputStream. The 3rd party code continually spits out information as it generates it. When something of interest comes across I want to pass it along to GraphQL Subscription(s)
I'm not sure how to wire the 3rd party code to the server-side GraphQL subscription code given this scenario. Any suggestions would be appreciated.
Some conceptual code is below:
public void liveStream(DataInputStream in) {
// Sit and constantly watch input stream and report when messages come in
while(true) {
SomeMessage message = readFromInputStream(in);
System.out.println("Received Message Type:" + message.getType());
// Convert SomeMessage into the appropriate class based on its type
if (message.getType() == "foo") {
Foo foo = convertMessageToFoo(message);
} else if (message.getType() == "bar") {
Bar bar = convertMessageToBar(message);
} else if (howeverManyMoreOfThese) {
// Keep converting to different objects
}
}
}
// The client code will eventually trigger this method when
// the GraphQL Subscription query is sent over
VertxDataFetcher<Publisher<SomeClassTBD>> myTestDataFetcher() {
return new VertxDataFetcher<> (env, future) -> {
try {
future.complete(myTest());
} catch(Exception e) {
future.fail(e);
}
});
}
OK, I wrapped my liveStream code in an ObservableOnSubscribe using an executorService and I'm getting back all the data. I guess I can now either pass it straight through to the front end or create separate publishers to deal with specific object types and have graphql subscriptions point to their respective publishers.
ExecutorService executor = Executors.newSingleThreadExecutor;
ObservableOnSubscribe<SomeClassTBD> handler = emitter ->
executor.submit(() -> {
try {
//liveStream code here
emitter.onComplete();
}
catch(Exception e) {
emitter.onError(e);
}
finally {
// Cleanup here
}
});
Observable<SomeClassTBD> = Observable.create(handler);

How can I make tests wait for Vert.x Verticle deployment to be completed

I am implementing tests for my Vert.x application, but I am having issues in making Vert.x wait for the deploy of the Verticle in a graceful way.
This is my #BeforeClass Method:
#BeforeClass
public static void before(TestContext context)
{
vertx = Vertx.vertx();
DeploymentOptions options = new DeploymentOptions();
byte[] encoded;
JsonObject config;
try {
encoded = Files.readAllBytes(Paths.get("src/main/resources/config.json"));
config = new JsonObject(new String(encoded, Charset.defaultCharset()));
options.setConfig(config);
jdbc = JDBCClient.createShared(vertx, config , "PostgreSQL");
deployVerticle((result) -> loadTestData((result), jdbc), options);
while (true)
{
if (vertx.deploymentIDs().size() > 0)
break;
}
} catch
(IOException e)
{
e.printStackTrace();
}
}
Also, here is the implementation for the deployVerticle and loadTestData methods:
private static void deployVerticle(Handler<AsyncResult<Void>> next, DeploymentOptions options) {
vertx.deployVerticle(PostgreSQLClientVerticle.class.getName(), options, deployResult ->
{
if (deployResult.succeeded())
next.handle(Future.succeededFuture());
});
}
private static void loadTestData(AsyncResult<Void> previousOperation, JDBCClient jdbc)
{
if (previousOperation.succeeded())
{
jdbc.getConnection(connection -> {
if (connection.succeeded())
{
connection.result().query(deleteTestDataGeneration, queryResult ->
{
connection.result().close();
});
}
});
}
}
As you can see, right now I have a while (true) on the beforemethod to hold the process and make sure the verticle is actually deployed.
Otherwise, when the tests start running, the verticle is not yet fully deployed and I get a NullPointerException trying to reach the resources.
I've tried many different approaches like using CompositeFuture or using Future.compose method to make the "before tasks" sequential and make the program hold for completion.
I achieved in making those tasks sequential but failed on holding the process until they are completed.
One of the issues is, I think, the fact that the deployVerticle method returns the AsyncResult with succeeded == true after every step of the "deploy procedure" is done, instead of when the Verticle is totally up.
Meaning that the process gets a successful result before everything is actually up...but this is just a wild guess.
Bottom-line: I would like to find a way to wait for the Verticle to be totally deployed before proceeding to perform the tests, without having to do the while (true)loop that I currently have in there.
What you are missing is the Async async = context.async();. With that the unittest stays in the method until it is not set to complete. Then you are able to orchestrate your asychronous code to:
first deploy the verticle
then execute the loadtestGeneration
set the async to complete so that, the other unittest methods already can access to your verticle without nullpointerexception
I also made some cleanup, check it out:
BeforeClass
#BeforeClass
public static void before2(TestContext context){
Async async = context.async();
vertx = Vertx.vertx();
DeploymentOptions options = new DeploymentOptions();
byte[] encoded;
JsonObject config;
try {
encoded = Files.readAllBytes(Paths.get("src/main/resources/config.json"));
config = new JsonObject(new String(encoded, Charset.defaultCharset()));
options.setConfig(config);
jdbc = JDBCClient.createShared(vertx, config , "PostgreSQL");
deployVerticle2(options)
.compose(c -> loadTestData2(jdbc))
.setHandler(h -> {
if(h.succeeded()){
async.complete();
}else{
context.fail(h.cause());
}
});
} catch (IOException e){
context.fail(e);
}
}
DeployVerticle
private static Future<Void> deployVerticle2(DeploymentOptions options) {
Future<Void> future = Future.future();
vertx.deployVerticle(PostgreSQLClientVerticle.class.getName(), options, deployResult -> {
if (deployResult.failed()){
future.fail(deployResult.cause());
}else {
future.complete();
}
});
return future;
}
LoadTestData
private static Future<Void> loadTestData2(JDBCClient jdbc){
Future<Void> future = Future.future();
jdbc.getConnection(connection -> {
if (connection.succeeded()) {
connection.result().query(deleteTestDataGeneration, queryResult -> {
if(queryResult.failed()){
connection.result().close();
future.fail(queryResult.cause());
}else{
connection.result().close();
future.complete();
}
});
} else {
future.fail(connection.cause());
}
});
return future;
}

How to wait for async operation (any I/O) in Android java plugin side?

For a unity game I need to use libs in android-plugin that will send websocket requests. I found out that I have no idea how make the c# code wait for the async operation in android plugin!
I provide a proof of concept case (with simple http get request) to ask my quesiton the simple way. Here are my code that didnt' work:
package com.example.plug2unity1;
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class Plug1Class {
static OkHttpClient client = new OkHttpClient();
static String doGetRequest(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
public static String GetPlug2Text() throws IOException {
String res = "";
try {
res = doGetRequest("http://www.google.com");
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
}
Unity script will have to call the plugin:
void Start () {
TextMesh txtm = GetComponent<TextMesh> ();
var plugin = new AndroidJavaClass("com.example.plug2unity1.Plug1Class");
txtm.text = plugin.CallStatic<string>("GetPlug1Text");
}
Edit:
The question is "not" how to make http call, it is obvious that from c# I can do it, I would like to learn "how c# could wait an async operation result from plugin, being it an http call or an I/O operation, same way we do by "promises" in javascript.
Result:
My TextMesh does not change the text, while if I do a POC without any async in plugin side, it works. How could I get this to work?
Use a callback to do this. Call the Java unction from C#. In the Java function, start new Thread to perform that task. When that task has finished, do a callback from Java to C# to let you know that the task has finished.
C# sample code:
void makeRequestOnJava()
{
TextMesh txtm = GetComponent<TextMesh> ();
var plugin = new AndroidJavaClass("com.example.plug2unity1.Plug1Class");
txtm.text = plugin.CallStatic<string>("GetPlug1Text");
}
//Will be called from C# when the request is done
void OnRequestFinished()
{
}
Then on the Java side when your task has finished, use UnityPlayer.UnitySendMessage to call the OnRequestFinished function on the C# side.
UnityPlayer.UnitySendMessage("GameObjectName", "OnRequestFinished", null);
You can see how to setup and use the UnityPlayer.UnitySendMessage function here.

Categories