Asynchronous code without Promise - java

Inside an Action, which is the best way to execute some asynchronous code if you don't need a Promise to produce the actual Result?
E.g I've created a RecordedAction to compose with other Actions. It executes an expensive operation (writes to the DB) but doesn't produce something needed by the annotated Action. In other words, I don't need a Promise but I do need a separate ExecutionContext.
I could do something like this:
public class RecordedAction extends Action<Recorded> {
#Override
public F.Promise<Result> call(Context ctx) throws Throwable {
Promise.promise(() -> {
// do asynchronous stuff
return Promise.pure(null, myExecutionContext);
});
return delegate.call(ctx);
}
}
But I'm just exploiting the Promise to use a separate ExecutionContext.

Related

How to transfer data via reactor's subscriber context?

I'm a new for a project reactor, but i have task to send some information from classic spring rest controller to some service, which is interacts with different system. Whole project developed with project reactor.
Here is my rest controller:
#RestController
public class Controller {
#Autowired
Service service;
#PostMapping("/path")
public Mono<String> test(#RequestHeader Map<String, String> headers) throws Exception {
testService.saveHeader(headers.get("header"));
return service.getData();
}
And here is my service:
#Service
public class Service {
private Mono<String> monoHeader;
private InteractionService interactor;
public Mono<String> getData() {
return Mono.fromSupplier(() -> interactor.interact(monoHeader.block()));
}
public void saveHeader(String header) {
String key = "header";
monoHeader = Mono.just("")
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + ctx.get(key)))
.subscriberContext(ctx -> ctx.put(key, header));
}
Is it acceptable solution?
Fisrt off, I don't think you need the Context here. It is useful to implicitly pass data to a Flux or a Mono that you don't create (eg. one that a database driver creates for you). But here you're in charge of creating the Mono<String>.
Does the service saveHeader really achieve something? The call seem transient in nature: you always immediately call the interactor with the last saved header. (there could be a side effect there where two parallel calls to your endpoint end up overwriting each other's headers).
If you really want to store the headers, you could add a list or map in your service, but the most logical path would be to add the header as a parameter of getData().
This eliminates monoHeader field and saveHeader method.
Then getData itself: you don't need to ever block() on a Mono if you aim at returning a Mono. Adding an input parameter would allow you to rewrite the method as:
public Mono<String> getData(String header) {
return Mono.fromSupplier(() -> interactor.interact(header));
}
Last but not least, blocking.
The interactor seems to be an external service or library that is not reactive in nature. If the operation involves some latency (which it probably does) or blocks for more than a few milliseconds, then it should run on a separate thread.
Mono.fromSupplier runs in whatever thread is subscribing to it. In this case, Spring WebFlux will subscribe to it, and it will run in the Netty eventloop thread. If you block that thread, it means no other request can be serviced in the whole application!
So you want to execute the interactor in a dedicated thread, which you can do by using subscribeOn(Schedulers.boundedElastic()).
All in all:
#RestController
public class Controller {
#Autowired
Service service;
#PostMapping("/path")
public Mono<String> test(#RequestHeader Map<String, String> headers) throws Exception {
return service.getData(headers.get("header"));
}
}
#Service
public class Service {
private InteractionService interactor;
public Mono<String> getData(String header) {
return Mono.fromSupplier(() -> interactor.interact(header))
.subscribeOn(Schedulers.boundedElastic());
}
}
How to transfer data via reactor's subscriber context?
Is it acceptable solution?
No.
Your code of saveHeader() method is an equivalent of simple
public void saveHeader(String header) {
monoHeader = Mono.just(header);
}
A subscriberContext is needed if you consume the value elsewhere - if the mono is constructed elsewhere. In your case (where you have all code before your eyes in the same method) just use the actual value.
BTW, there are many ways to implement your getData() method.
One is as suggested by Simon Baslé to get rid of a separate saveHeader() method.
One other way, if you have to keep your monoHeader field, could be
public Mono<String> getData() {
return monoHeader.publishOn(Schedulers.boundedElastic())
.map(header -> interactor.interact(header));
}

Method call after returning Mono<Void>

I want to call the method when previous returned Mono<Void>:
#Override
public Mono<Void> sendEmail(EmailDto emailDto) {
return mailReactiveClient.sendEmail(message ->
createMessage(emailDto, emailDto.getBody(), message))
.doOnNext(saveNotificationLog(emailDto)); //it's not work
}
private void saveNotificationLog(EmailDto emailDto) {
notificationLogReactiveRepository.save(NotificationLog.builder()
...
.build());
}
Method sendEmailreturns Mono<Void>.
So how to call saveNotificationLog?
UPD: Tring to make my question simplier:
#Override
public Mono<Void> sendEmail(EmailDto emailDto) {
return mailReactiveClient.sendEmail(message ->
createMessage(emailDto, emailDto.getBody(), message))
.doOnNext(System.out.print("Hello world!");
}
How to call doOnNextor similar method after sendEmail return Mono<Void>?
The Mono will not emit data, so doOnNext will not be triggered. You should use the doOnSuccess instead.
Also, your Mono need to be consumed. Without the code, we don't know if it is or not.
Some example here: I added subscribe() to consume the mono. Depending on the use of your Mono, you will have to do or not the same thing.
This print nothing:
Mono<String> m=Mono.just("test");
Mono<Void> v=m.then();
v.doOnNext(x->System.out.println("OK")).subscribe();
This print "OK":
Mono<String> m=Mono.just("test");
Mono<Void> v=m.then();
v.doOnSuccess(x->System.out.println("OK")).subscribe();
doOnNext, and in general all doOn* reactor methods are side-effect methods. You're not supposed to call them to do I/O work or chain operations, but rather log things and not do anything that would affect the state of the application.
In your code sample, notificationLogReactiveRepository.save returns Mono<Void>. The saveNotificationLog returns void and does not subscribe to the publisher returned by notificationLogReactiveRepository.save. This means the notification will not be saved, because nothing happens until you subscribe.
In this case, it seems you're trying to chain operations - then operators are just made for that. Your code should look like this:
#Override
public Mono<Void> sendEmail(EmailDto emailDto) {
return mailReactiveClient.sendEmail(message ->
createMessage(emailDto, emailDto.getBody(), message))
.then(saveNotificationLog(emailDto));
}
private Mono<Void> saveNotificationLog(EmailDto emailDto) {
return notificationLogReactiveRepository.save(NotificationLog.builder()
...
.build());
}
Try it this way:
Mono.empty().then()

How to use CompletableFuture.thenCompose() when returning entities from repositories?

I started working with CompletableFuture in Spring Boot, and I'm seeing in some places that the usual repository methods return CompletableFuture <Entity> instead of Entity.
I do not know what is happening, but when I return instances of CompletableFuture in repositories, the code runs perfectly. However when I return entities, the code does not work asynchronously and always returns null.
Here is an example:
#Service
public class AsyncServiceImpl{
/** .. Init repository instances .. **/
#Async(AsyncConfiguration.TASK_EXECUTOR_SERVICE)
public CompletableFuture<Token> getTokenByUser(Credential credential) {
return userRepository.getUser(credential)
.thenCompose(s -> TokenRepository.getToken(s));
}
}
#Repository
public class UserRepository {
#Async(AsyncConfiguration.TASK_EXECUTOR_REPOSITORY)
public CompletableFuture<User> getUser(Credential credentials) {
return CompletableFuture.supplyAsync(() ->
new User(credentials.getUsername())
);
}
}
#Repository
public class TokenRepository {
#Async(AsyncConfiguration.TASK_EXECUTOR_REPOSITORY)
public CompletableFuture<Token> getToken(User user) {
return CompletableFuture.supplyAsync(() ->
new Token(user.getUserId())
);
}
}
The previous code runs perfectly but the following code doesn't run asynchronously and the result is always null.
#Service
public class AsyncServiceImpl {
/** .. Init repository instances .. **/
#Async(AsyncConfiguration.TASK_EXECUTOR_SERVICE)
public CompletableFuture<Token> requestToken(Credential credential) {
return CompletableFuture.supplyAsync(() -> userRepository.getUser(credential))
.thenCompose(s ->
CompletableFuture.supplyAsync(() -> TokenRepository.getToken(s)));
}
}
#Repository
public class UserRepository {
#Async(AsyncConfiguration.TASK_EXECUTOR_REPOSITORY)
public User getUser(Credential credentials) {
return new User(credentials.getUsername());
}
}
#Repository
public class TokenRepository {
#Async(AsyncConfiguration.TASK_EXECUTOR_SERVICE)
public Token getToken(User user) {
return new Token(user.getUserId());
}
}
Why doesn't this second code work?
As per the Spring #Async Javadoc:
the return type is constrained to either void or Future
and it is also further detailed in the reference documentation:
In the simplest case, the annotation may be applied to a void-returning method.
[…]
Even methods that return a value can be invoked asynchronously. However, such methods are required to have a Future typed return value. This still provides the benefit of asynchronous execution so that the caller can perform other tasks prior to calling get() on that Future.
In your second example, your #Async-annotated methods do not return a Future (or ListenableFuture and CompletableFuture which are also supported). However, Spring has to run your method asynchronously. It can thus only behave as if your method had a void return type, and thus it returns null.
As a side note, when you use #Async, your method will already run asynchronously, so you shouldn't use CompletableFuture.supplyAsync() inside the method. You should simply compute your result and return it, wrapped in CompletableFuture.completedFuture() if necessary. If your method is only composing futures (like your service that simply composes asynchronous repository results), then you probably don't need the #Async annotation. See also the example from the Getting Started guide.

Action composition and async in Play Framework 2.5 in Java

I want to create an action that I can use with the #With annotation style. This action will need to proceed to an RPC call so if I understood correctly the documentation I should rather put this in an async way.
This is what I tried to do until now:
public class GetUserIdAction extends play.mvc.Action.Simple {
#Override
public CompletionStage<Result> call(Http.Context context) {
String token = "";
if (StringUtils.isEmpty(token)) {
return delegate.call(context);
}
CompletionStage<Http.Context> promiseOfUpdatedContext = CompletableFuture.supplyAsync(() -> setUserIdForToken(context, token));
return promiseOfUpdatedContext.thenApply(ctx -> delegate.call(ctx));
}
private Http.Context setUserIdForToken(Http.Context context, String token) {
context.args.put("user_id", authenticationManager.getUserIdForToken(token));
// The AuthenticationManager is issuing an RPC call and thus may take some time to complete.
return context;
}
}
Set aside the fact that token is always empty and authenticationManager is not set, this is just a quick meaningless example, my IDE is complaining on the thenApply part. For what I understand, it is expecting a CompletionStage<Result> and gets something more like a CompletionStage<CompletionStage<Result>>.
What is a way to deal with it? Cause here all I want is to put some information in the Context and then continue the delegate.call chain.
Or maybe I'm trying to do something stupid and composed actions are already asynchronous?
You have a CompletionStage<Something> and want to end with a CompletionStage<Result>. The easiest way to achieve that is using thenCompose.
Here is an example, with a small change: I have a CompletableFuture to get the token and only then I add it to the HttpContext
#Override
public CompletionStage<Result> call(final Http.Context context) {
final String token = "";
if (StringUtils.isEmpty(token)) {
return delegate.call(context);
}
return CompletableFuture.supplyAsync(() -> {
// do something to fetch that token
return "your_new_token";
}).thenCompose(tokenReceived -> {
context.args.put("user_id", tokenReceived);
return delegate.call(context);
});
}

Testing an async method call

Below is a simplified setup of my application. It has a class Foobar which calls on a facade method for fetching data. The facade then calls on a web service to actually get the data and then manipulates the data a bit and then returns it to Foobar.
Now because the web service might take a good while to run, the method call to the facade needs to be asynchronous. Hence the facade's method doesn't have a return value, but instead, the method uses a callback object. Look at the example and continue reading below.
public class Foobar {
private List<DTO> dtos;
#Autowired
private Facade facade;
public void refresh() {
facade.refreshFoobar(new CallBack() {
public void dataFetched(List<DTO> dtos) {
setDtos(dtos);
}
});
}
public void setDtos(List<DTO> dtos) {
this.dtos = dtos;
}
}
public class Facade {
...
public void refreshFoorbar(CallBack cb) {
// Fetch data from a web service
List<DTO> dtos = webService.getData();
// Manipulate DTOs
....
// call on the callback method
cb.dataFecthed(dtos);
}
}
I have two ways of making the facade's method asynchronous, either by creating a thread manually or by using springs #Async annotation.
public class Facade {
public void refreshFoorbar(CallBack cb) {
new Thread() {
#Override
public void run() {
....
}
}.start();
}
}
// ... OR ...
public class Facade {
#Async
public void refreshFoorbar(CallBack cb) {
....
}
}
My problem is that I now need to write an integration test for this chain of method calls. I think I need to force the async facade call to be synchronous when the integration test is ran, otherwise I won't know for sure when I can do the appropriate asserts. The only idea for making the method call synchronous is to use manually handled threads AND making the threading conditional (so, for testing purposes, I have an if clause which determines if the facade method should be ran in a separate thread or not).
However, I have a feeling that there could be a better solution to my problem, whether it be a better way of forcing the method to me synchronous, eg with spring, or by testing the multithreading on some way.
This is where I need your suggestions, how would you solve my problem? Note, I'm using junit for both unit and integration tests.
Simple solution would be to return a Future object like this,
#Async
public Future<String> refreshFoorbar(CallBack cb) {
yourHeavyLifting(); //asynchronous call
return new AsyncResult<String>("yourJobNameMaybe");
}
And in your test, take the future reference and call the get() method.
future.get(); // if its not already complete, waits for it to complete
assertTrue(yourTestCondition)
This blog post shows a sample.
When JUnit testing stuff like this, I use a testing callback with a CountDownLatch that gets counted down by the callback and await()ed by the test method.
private static class TestingCallback implements Callback {
private final CountDownLatch latch;
public TestingCallback(CountDownLatch latch) {
this.latch = latch;
}
#Override public void onEvent() {
this.latch.countDown();
}
}
#Test
public void testCallback() {
final CountDownLatch latch = new CountDownLatch(1);
classUnderTest.execute( new TestCallback(latch) );
assertTrue(latch.await(30, TimeUnit.SECONDS));
}
If the callback is invoked (asynchronously) by the code under test, the latch returns true and the test passes. If the callback doesn't get invoked, the test times out after thirty seconds and the assertion fails.

Categories

Resources