I am using vert.x and I am trying to list my DynamoDB tables. Here is how I build the DynamoDB client.
private static DynamoDbAsyncClient buildDynamoDBAsyncClient(final Vertx vertx) {
return VertxSdkClient.withVertx(DynamoDbAsyncClient.builder(), vertx.getOrCreateContext())
.build();
}
And here is the request I make
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) -> {
if (tables != null) {
tables.forEach(System.out::println);
} else {
// Handle error
err.printStackTrace();
}
client.close();
});
tableNames.join();
I get warnings of blocked threads and then a request timeout. What am I doing wrong? Thank you in advance.
As Tim stated in the comment, tablesName.join() blocks the thread, you should let the CompletableFuture handle async.
Try this:
private Future<ListTablesResponse> listTables() {
Promise<ListTablesResponse> promise = Promise.promise();
CompletableFuture<ListTablesResponse> response = client.listTables();
response.handleAsync((result, error) -> {
if (error == null) {
promise.complete(result);
} else {
promise.fail(error);
}
return null;
});
return promise.future();
}
then in your vertx request handler:
this.listTables()
.onSuccess(listTablesResponse -> {...})
.onFailure(throwable -> {...});
Related
I'm new to reactive programming
I want to save the object to the DB when handling an error, but as far as I know, calling the block() method is not the best practice
mailFailureRepository.save(failure).block();
Is there a way to do this without interrupting the flow?
I think I should rebuild the thread chain, but I don't quite understand how to do it in my case
I will also be glad of any information resources
public Mono<Object> sendEmail(SendEmailRequest request)
throws MailTemplateNotSupportedException, ExactTargetException {
log.debug("Send email process started.");
return validateRequestAndSendMail(request)
.onErrorResume(error -> {
if (error instanceof ExactTargetException ex) {
MailFailure failure = MailFailure.builder()
.templateKey(request.getTemplateKey())
.templateParams(request.getTemplateParams() != null ? request.getTemplateParams().toString() : null)
.subscriberHash(request.getSubscriberHash())
.email(request.getEmail())
.responseStatus(ex.getStatus())
.responsePayload(ex.getBody())
.build();
mailFailureRepository.save(failure).block();
return Mono.error(error);
}
return Mono.error(error);
});
Why not save and then return error without blocking:
public Mono <Object> sendEmail(SendEmailRequest request)
throws MailTemplateNotSupportedException, ExactTargetException {
log.debug("Send email process started.");
return validateRequestAndSendMail(request)
.onErrorResume(error -> {
Mono<Object> errorMono = Mono.error(error);
if (error instanceof ExactTargetException ex) {
return mailFailureRepository.save(buildFailureEmail(request))
.thenReturn(errorMono);
}
return errorMono;
});
}
private MailFailure buildFailureEmail(SendEmailRequest request) {
return MailFailure.builder()
.templateKey(request.getTemplateKey())
.templateParams(request.getTemplateParams() != null ? request.getTemplateParams().toString() : null)
.subscriberHash(request.getSubscriberHash())
.email(request.getEmail())
.responseStatus(ex.getStatus())
.responsePayload(ex.getBody())
.build();
}
I am trying to retrive the list of contents inside the S3 bucket using aws-java sdk 2 and S3 async client. However after running the below code, I dont see any output even though the bucket has one object inside it. Am I missing something here?
ListObjectsV2Request request = ListObjectsV2Request .builder().bucket("my-bucket").build();
ListObjectsV2Publisher response = s3AsyncClient.listObjectsV2Paginator(request);
response.contents().subscribe(s3object -> System.out.println(s3object));
To use the S3AsyncClient client to list objects in bucket using the listObjectsV2 method, you can use this code (your Paginator call does not work for me either).
public static void listObjects(S3AsyncClient s3AsyncClient, String bucketName) {
try {
ListObjectsV2Request listReq = ListObjectsV2Request.builder()
.bucket(bucketName)
.build();
CompletableFuture<ListObjectsV2Response> future = s3AsyncClient.listObjectsV2(listReq);
future.whenComplete((resp, err) -> {
try {
if (resp != null) {
List<S3Object> objects = resp.contents();
for (S3Object myValue : objects) {
System.out.print("\n The name of the key is " + myValue.key());
}
} else {
// Handle error.
err.printStackTrace();
}
} finally {
// Only close the client when you are completely done with it.
s3AsyncClient.close();
}
});
future.join();
} catch (S3Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
I am using Vert.x in my project, I used future() to get the results from a MongoDB query. However when I do future().result it returns "null". I want the result to be saved in the future and I will use it for other APIs. Is there any guide for me, I will be very grateful and appreciate if someone give me a solution. Thanks
router.class
rivate void getClazzById(RoutingContext rc) {
Future<JsonObject> future = Future.future();
String clazzId = rc.request().getParam("clazzId");
classService.getClazzById(clazzId, res -> {
System.out.println(res.result());
if (res.succeeded()) {
JsonObject result = res.result();
if (result != null) {
future.complete(res.result());
rc.response().setStatusCode(200).putHeader("Content-Type", "application/json; charset=utf-8")
.end(result.encodePrettily());
} else {
rc.response().setStatusCode(400).putHeader("Content-Type", "application/json; charset=utf-8")
.end(new JsonObject().put("error", "Class not found!").encodePrettily());
}
} else
rc.fail(res.cause());
});
future.setHandler(s -> {
if (s.succeeded()) {
System.out.println("sss: " +s.result()); // print: {"_id":"123", "name":"abc"}
}
else {
System.out.println("fail");
}
});
System.out.println("hhhhhh: " + future.result()); // I want to print {"_id":"123", "name":"abc"}
}
service.class
public void getClazzById(String clazzId, Handler<AsyncResult<JsonObject>> resultHandler) {
JsonObject query = new JsonObject().put("_id", clazzId);
client.findOne(Collection.CLAZZ, query, null, ar -> {
if (ar.succeeded()) {
if (ar.succeeded()) {
JsonObject result = ar.result();
if (result == null) {
resultHandler.handle(Future.failedFuture("Error"));
} else {
resultHandler.handle(Future.succeededFuture(result));
}
} else {
ar.cause();
}
}
});
}
When writing asynchronous code, you are carried to use the framework / runtime semantics and tools for communication.
You are already leveraging one of Vert.x's way of async communication, the Future - but in the wrong manner trying to retrieve its result inline.
Instead of having the Future result accessed within your method, you need to return it as your mean of communication to a caller, which would be able to set a completion handler (Handler) to it:
private Future<JsonObject> getClazzById(RoutingContext rc) {
Future<JsonObject> future = Future.future();
String clazzId = rc.request().getParam("clazzId");
classService.getClazzById(clazzId, res -> {
if (res.succeeded()) {
JsonObject result = res.result();
if (result != null) {
future.complete(res.result()); // set the retrieved result
rc.response().setStatusCode(200).putHeader("Content-Type", "application/json; charset=utf-8")
.end(result.encodePrettily());
} else {
future.complete(null); // you need to provide 'null' result to avoid caller blocking
rc.response().setStatusCode(400).putHeader("Content-Type", "application/json; charset=utf-8")
.end(new JsonObject().put("error", "Class not found!").encodePrettily());
}
} else
rc.fail(res.cause());
});
return future; // return the future to caller
}
An interested caller would be able to set a handler for Future completion as needed:
getClassById(rc).setHandler(s -> {
if (s.succeeded()) {
System.out.println("sss: " +s.result()); // print: {"_id":"123", "name":"abc"}
}
else {
System.out.println("fail");
}
});
As a side note: you are not setting the API boundaries properly in your business logic as you are trying to resolve the HTTP Response result which generally denotes the request processing end while still returning the query result to be handled in some other manner.
I am using executeBlocking from vertx in a for loop to parallelise the processing of a result, and collating the results using a CompositeFuture. Based on all the results, I want to return some value from the method, but lambda function inside CompositeFuture's handler is not letting me do so. How to work with this usecase?
Code for reference:
public Response call() throws Exception {
List<Future> futureList = new ArrayList<>();
//services is a global variable, an arraylist of services
for(String service: services) {
Future serviceFuture = Future.future();
futureList.add(serviceFuture);
vertx.executeBlocking(implementTask(service, serviceFuture), false, asyncResult -> {
if (asyncResult.failed()) {
LOGGER.error("Task FAILED for service {}", service, asyncResult.cause());
}
});
}
CompositeFuture.all(futureList).setHandler(asyncResult -> {
if(asyncResult.succeeded()) {
LOGGER.debug("Task completed successfully");
return new Response(ResponseStatus.SUCCESS);
} else {
LOGGER.error("Task FAILED", asyncResult.cause());
return new Response(ResponseStatus.FAILED);
}
});
}
You can't do this.
Your call() method should return Future<Result> and not the Result. Then you would need to attach the callback handler on your original caller. This is the way that async methods propagate the result in Vert.x.
public Future<Response> call() throws Exception {
Promise<Response> promise = Promise.promise();
List<Future> futureList = new ArrayList<>();
//services is a global variable, an arraylist of services
for(String service: services) {
Future serviceFuture = Future.future();
futureList.add(serviceFuture);
vertx.executeBlocking(implementTask(service, serviceFuture), false, asyncResult -> {
if (asyncResult.failed()) {
LOGGER.error("Task FAILED for service {}", service, asyncResult.cause());
}
});
}
CompositeFuture.all(futureList).setHandler(asyncResult -> {
if(asyncResult.succeeded()) {
LOGGER.debug("Task completed successfully");
promise.complete(new Response(ResponseStatus.SUCCESS));
} else {
LOGGER.error("Task FAILED", asyncResult.cause());
promise.fail("Failed");
}
});
return promise.future();
}
Then the call would look like this:
object.call().onSuccess(resultHandler - > {
//Code when call succeeded
}).onFailure(failureHandler - > {
//Code when call failed
});
Note: This example is using Vert.x 4. If you are using a version of Vert.x less than 4, then the syntax is a bit different, but the point stays the same.
I'm attempting to perform a synchronous write/read in a demux-based client application with MINA 2.0 RC1, but it seems to get stuck. Here is my code:
public boolean login(final String username, final String password) {
// block inbound messages
session.getConfig().setUseReadOperation(true);
// send the login request
final LoginRequest loginRequest = new LoginRequest(username, password);
final WriteFuture writeFuture = session.write(loginRequest);
writeFuture.awaitUninterruptibly();
if (writeFuture.getException() != null) {
session.getConfig().setUseReadOperation(false);
return false;
}
// retrieve the login response
final ReadFuture readFuture = session.read();
readFuture.awaitUninterruptibly();
if (readFuture.getException() != null) {
session.getConfig().setUseReadOperation(false);
return false;
}
// stop blocking inbound messages
session.getConfig().setUseReadOperation(false);
// determine if the login info provided was valid
final LoginResponse loginResponse = (LoginResponse)readFuture.getMessage();
return loginResponse.getSuccess();
}
I can see on the server side that the LoginRequest object is retrieved, and a LoginResponse message is sent. On the client side, the DemuxingProtocolCodecFactory receives the response, but after throwing in some logging, I can see that the client gets stuck on the call to readFuture.awaitUninterruptibly().
I can't for the life of me figure out why it is stuck here based upon my own code. I properly set the read operation to true on the session config, meaning that messages should be blocked. However, it seems as if the message no longer exists by time I try to read response messages synchronously.
Any clues as to why this won't work for me?
The reason this wasn't working for me was because of an issue elsewhere in my code where I stupidly neglected to implement the message response encoder/decoder. Ugh. Anyway, the code in my question worked as soon as I fixed that.
I prefer this one (Christian Mueller : http://apache-mina.10907.n7.nabble.com/Mina-Client-which-sends-receives-messages-synchronous-td35672.html)
public class UCPClient {
private Map<Integer, BlockingQueue<UCPMessageResponse>> concurrentMap = new ConcurrentHashMap<Integer, BlockingQueue<UCPMessageResponse>>();
// some other code
public UCPMessageResponse send(UCPMessageRequest request) throws Throwable {
BlockingQueue<UCPMessageResponse> queue = new LinkedBlockingQueue<UCPMessageResponse>(1);
UCPMessageResponse res = null;
try {
if (sendSync) {
concurrentMap.put(Integer.valueOf(request.getTransactionReference()), queue);
}
WriteFuture writeFuture = session.write(request);
if (sendSync) {
boolean isSent = writeFuture.await(transactionTimeout, TimeUnit.MILLISECONDS);
if (!isSent) {
throw new TimeoutException("Could not sent the request in " + transactionTimeout + " milliseconds.");
}
if (writeFuture.getException() != null) {
throw writeFuture.getException();
}
res = queue.poll(transactionTimeout, TimeUnit.MILLISECONDS);
if (res == null) {
throw new TimeoutException("Could not receive the response in " + transactionTimeout + " milliseconds.");
}
}
} finally {
if (sendSync) {
concurrentMap.remove(Integer.valueOf(request.getTransactionReference()));
}
}
return res;
}
}
and the IoHandler:
public class InnerHandler implements IoHandler {
// some other code
public void messageReceived(IoSession session, Object message) throws Exception {
if (sendSync) {
UCPMessageResponse res = (UCPMessageResponse) message;
BlockingQueue<UCPMessageResponse> queue = concurrentMap.get(res.getTransactionReference());
queue.offer(res);
}
}
}
I had this exact problem. It turns out that it's because I was doing reads/writes in my IoHandler.sessionCreated() implementation. I moved the processing onto the thread that established the connection, instead of just waiting for the close future.
You must not use your login() function in IoHandler Thread :
If you call IoFuture.awaitUninterruptibly() in the override event function of IoHandler,
IoHandler don't work and get stuck.
You can call login() in other Thread and it will be work properly.