I have a query in mongodb that return a lot of results. I encapsulate the FindIterable into a reactive Flowable and stream the result using spring-boot-webflux.
When the user disconnect, I receive the cancel event in the Flowable but, I don't find a way to stop the mongo iteration.
I create the Flowable like that :
private static Flowable toFlowable(FindIterable cursor){
return Flowable.create(emitter -> {
cursor
.forEach(new Block<Document>() {
#Override
public void apply(Document doc) {
log.debug("Next Block :" + doc.toJson());
emitter.onNext(doc);
}
} , new SingleResultCallback<Void>() {
#Override
public void onResult(Void result, Throwable t) {
log.debug("Completed");
emitter.onComplete();
}
});
}, BackpressureStrategy.BUFFER);
}
and I create the query that way :
public Flowable<SomeType> find(String value){
Bson filter = Filters.eq("key", value);
FindIterable<Document> iterable = collection.find(filter);
return toFlowable(iterable)
.map(doc -> adapter.convert(doc))
.doOnCancel(() -> {
log.info("Cancel Flowable");
**--> iterable.stop????**
});
I am using the 'org.mongodb:mongodb-driver-async:3.6.3'.
Is there a way to stop the async iterable in mongo?
Related
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.
Below is the process I want to achieve in through my code.
When I receive JsonRecord I need to check if it exists in Redis Cache.
I) If record is NOT present:
1. Create a record in external system DB using REST call.
2. Create Cache Record from JsonRecord and response from step1
3. Create the external System using response from step2
II) If record is present:
1. Update a record in external system DB using REST call.
2. Update the Cache Record from JsonRecord and response from step 1
3. Update the external System using response from step2
NOTE 1: Calls to external systems in step 1 and step 3 are different.
Issue1: When transform() method in MessageTransformationHandler returns null, it directly going back to the switchIfEmpty block in RecordHandler.
Expected: When transform() method returns null, it should go back to calling method.
Issue2: When transform method in MessageTransformationHandler returns null, it directly going back to the switchIfEmpty block in RecordHandler.
Expected: When transform method returns null, it should go back to calling method. then from that method to flatMap condition in RecordHandler class.
#Component
public class RecordHandler {
#Autowired
private contentService contentService;
#Autowired
private RedisService redisService;
#Autowired
private MappingService mappingService;
public Mono<String> handleMessage(JsonRecord record, String recordId, String , long startTime) {
try {
return redisService.getObjectFromRedis(record.getRecordId())
.flatMap(redisResponse -> {
return contentService.updateEvent(record, redisResponse)
.flatMap(dBResponse -> mappingService.createOrUpdateMappingData(dBResponse, record, redisResponse))
.filter(mappingResponse -> mappingResponse!=null)
.flatMap(mappingResponse -> contentService.storeOrUpdatePrices(record, mappingResponse, requestId));
}
}).switchIfEmpty(Mono.defer(() -> {
return contentService.createEvent(record, redisResponse)
.flatMap(dBResponse -> mappingService.createOrUpdateMappingData(dBResponse, record , startTime))
.filter(mappingResponse -> mappingResponse!=null)
.flatMap(mappingResponse -> contentService.storeOrUpdatePrices(record, mappingResponse, requestId));
}));
} catch (Exception e) {
return Mono.error(e);
}
}
}
#Service
public class ContentServiceImpl implements ContentService {
#Override
public Mono<EventType> updateEvent(JsonRecord record, EventsMapping mappingData) {
return Mono.just(record).flatMap(record -> {
MessageTransformationHandler messageHandler = messageTransformationHandlerFactory
.getHelper(record.getClassName());
//transform method can return null value
ContentManagementModelGeneric payload = messageHandler.transform(record, mappingData);
if (payload == null) {
return Mono.empty();
}
return ContentRESTService.createRecord(record, payload);
}).flatMap(sportsDBResponse -> Mono.just(getResponseEvent(record, sportsDBResponse)));
}
}
public class MessageTransformationHandler {
public ContentModel transform(JsonRecord record, MappingData mappingData) {
ContentModel cm = null;
List<MarketType> somethingList = record.getSomething().getSomeList_().stream().map(something -> {
//do something and return null.
return null
})
.filter(something -> something != null)
.collect(Collectors.toList());
if(somethingList == null || somethingList.isEmpty()) {
return null;
}
return cm;
}
}
I have never really worked with asynchronous programming in Java and got very confused on the practice is the best one.
I got this method
public static CompletableFuture<Boolean> restoreDatabase(){
DBRestorerWorker dbWork = new DBRestorerWorker();
dbWork.run();
return "someresult" ;
}
then this one which calls the first one
#POST
#Path("{backupFile}")
#Consumes("application/json")
public void createOyster(#PathParam("backupFile") String backupFile) {
RestUtil.restoreDatabase("utv_johan", backupFile);
//.then somemethod()
//.then next method()
}
What I want to do is first call the restoreDatabase() method which calls dbWork.run() (which is an void method) and when that method is done I want createOyster to do the next one and so forth until I have done all the steps needed. Someone got a guideline were to start with this. Which practice is best in today's Java?
As you already use CompletableFuture, you may build your async execution pipeline like.
CompletableFuture.supplyAsync(new Supplier<String>() {
#Override
public String get() {
DBRestorerWorker dbWork = new DBRestorerWorker();
dbWork.run();
return "someresult";
};
}).thenComposeAsync((Function<String, CompletionStage<Void>>) s -> {
CompletableFuture<String> future = new CompletableFuture<>();
try{
//createOyster
future.complete("oyster created");
}catch (Exception ex) {
future.completeExceptionally(ex);
}
return null;
});
As you could see, You can call thenComposeAsync or thenCompose to build a chain of CompletionStages and perform tasks using results of the previous step or make Void if you don't have anything to return.
Here's a very good guide
You can use AsyncResponse:
import javax.ws.rs.container.AsyncResponse;
public static CompletableFuture<String> restoreDatabase(){
DBRestorerWorker dbWork = new DBRestorerWorker();
dbWork.run();
return CompletableFuture.completedFuture("someresult");
}
and this
#POST
#Path("{backupFile}")
#Consumes("application/json")
public void createOyster(#PathParam("backupFile") String backupFile,
#Suspended AsyncResponse ar) {
RestUtil.restoreDatabase("utv_johan", backupFile)
.thenCompose(result -> doSomeAsyncCall())
.thenApply(result -> doSomeSyncCall())
.whenComplete(onFinish(ar))
//.then next method()
}
utility function to send response
static <R> BiConsumer<R, Throwable> onFinish(AsyncResponse ar) {
return (R ok, Throwable ex) -> {
if (ex != null) {
// do something with exception
ar.resume(ex);
}
else {
ar.resume(ok);
}
};
}
In my GWT Application I'm often refering several times to the same server results. I also don't know which code is executed first. I therefore want to use caching of my asynchronous (client-side) results.
I want to use an existing caching library; I'm considering guava-gwt.
I found this example of a Guava synchronous cache (in guava's documentation):
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
This is how I'm trying to use a Guava cache asynchronously (I have no clue about how to make this work):
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
// I want to do something asynchronous here, I cannot use Thread.sleep in the browser/JavaScript environment.
service.createExpensiveGraph(key, new AsyncCallback<Graph>() {
public void onFailure(Throwable caught) {
// how to tell the cache about the failure???
}
public void onSuccess(Graph result) {
// how to fill the cache with that result???
}
});
return // I cannot provide any result yet. What can I return???
}
});
GWT is missing many classes from the default JRE (especially concerning threads and concurrancy).
How can I use guava-gwt to cache asynchronous results?
As I understood what you want to achieve is not just a asynchronous cache but also a lazy cache and to create one the GWT is not a best place as there is a big problem when implementing a GWT app with client side Asynchronous executions, as GWT lacks the client side implementations of Futures and/or Rx components (still there are some implementations of RxJava for GWT). So in usual java what you want to create can be achieved by :
LoadingCache<String, Future<String>> graphs = CacheBuilder.newBuilder().build(new CacheLoader<String, Future<String>>() {
public Future<String> load(String key) {
ExecutorService service = Executors.newSingleThreadExecutor();
return service.submit(()->service.createExpensiveGraph(key));
}
});
Future<String> value = graphs.get("Some Key");
if(value.isDone()){
// This will block the execution until data is loaded
String success = value.get();
}
But as GWT has no implementations for Futures you need to create one just like
public class FutureResult<T> implements AsyncCallback<T> {
private enum State {
SUCCEEDED, FAILED, INCOMPLETE;
}
private State state = State.INCOMPLETE;
private LinkedHashSet<AsyncCallback<T>> listeners = new LinkedHashSet<AsyncCallback<T>>();
private T value;
private Throwable error;
public T get() {
switch (state) {
case INCOMPLETE:
// Do not block browser so just throw ex
throw new IllegalStateException("The server response did not yet recieved.");
case FAILED: {
throw new IllegalStateException(error);
}
case SUCCEEDED:
return value;
}
throw new IllegalStateException("Something very unclear");
}
public void addCallback(AsyncCallback<T> callback) {
if (callback == null) return;
listeners.add(callback);
}
public boolean isDone() {
return state == State.SUCCEEDED;
}
public void onFailure(Throwable caught) {
state = State.FAILED;
error = caught;
for (AsyncCallback<T> callback : listeners) {
callback.onFailure(caught);
}
}
public void onSuccess(T result) {
this.value = result;
state = State.SUCCEEDED;
for (AsyncCallback<T> callback : listeners) {
callback.onSuccess(value);
}
}
}
And your implementation will become :
LoadingCache<String, FutureResult<String>> graphs = CacheBuilder.newBuilder().build(new CacheLoader<String, FutureResult<String>>() {
public FutureResult<String> load(String key) {
FutureResult<String> result = new FutureResult<String>();
return service.createExpensiveGraph(key, result);
}
});
FutureResult<String> value = graphs.get("Some Key");
// add a custom handler
value.addCallback(new AsyncCallback<String>() {
public void onSuccess(String result) {
// do something
}
public void onFailure(Throwable caught) {
// do something
}
});
// or see if it is already loaded / do not wait
if (value.isDone()) {
String success = value.get();
}
When using the FutureResult you will not just cache the execution but also get some kind of laziness so you can show some loading screen while the data is loaded into cache.
If you just need to cache the asynchronous call results, you can go for a
Non-Loading Cache, instead of a Loading Cache
In this case you need to use put, getIfPresent methods to store and retrieve records from cache.
String v = cache.getIfPresent("one");
// returns null
cache.put("one", "1");
v = cache.getIfPresent("one");
// returns "1"
Alternatively a new value can be loaded from a Callable on cache misses
String v = cache.get(key,
new Callable<String>() {
public String call() {
return key.toLowerCase();
}
});
For further reference: https://guava-libraries.googlecode.com/files/JavaCachingwithGuava.pdf
Hello I'm trying to write async code for MongoDB async driver (3.0) http://mongodb.github.io/mongo-java-driver/3.0/driver-async/ with Play Framework 2.4 (Java) in controller with Async result https://www.playframework.com/documentation/2.3.x/JavaAsync , when I'm testing it the Promise results is outside the Async call to MongoDB so sometimes I have empty json in the response, please can you help me with it ?
public F.Promise<Result> list() {
final List<Document> accounts = new ArrayList<Document>();
F.Promise<List<Document>> promiseOfAccounts = F.Promise.promise(
new F.Function0<List<Document>>() {
public List<Document> apply() {
accountRepository.getCollection().find().into(accounts,
new SingleResultCallback<List<Document>>() {
#Override
public void onResult(final List<Document> result, final Throwable t) {
}
});
return accounts;
}
}
);
return promiseOfAccounts.map(
new F.Function<List<Document>, Result>() {
public Result apply(List<Document> i) {
return ok(i);
}
}
);
}
When you return accounts, the SigleResultCallback closure hasn't been executed. That results in the list being empty when the it's serialized in the ok(i) expression. To make it work, you have to resolve the promise yourself inside the SingleResultCallback. Remember Play Promises sit over scala Futures and scala Promises (which are different from Play F.Promises). This is how you'd do it:
Promise<List<Document>> accountsPromise = Promise$.MODULE$.apply();
ArrayList<Document> accounts = new ArrayList<Document>();
accountRepository.getCollection().find().into(accounts,
new SingleResultCallback<List<Document>>() {
#Override
public void onResult(final List<Document> result, final Throwable t) {
accountsPromise.success(result);
}
});
promiseOfAccounts=F.Promise.wrap(accountsPromise.future());
return promiseOfAccounts.map(
new F.Function<List<Document>, Result>() {
public Result apply(List<Document> i) {
return ok(i);
}
}
);
the moment you call the success method of the scala Promise resolves, and so the value of the future becomes available, but you return the play F.Promise before that happens, the awesomeness of the reactive programming.