javadatabaseplaybackcompletable-futurecompletion-stage

Combine CompletionStage in play framework action in Java


I try to combine CompletionStages in play framework and then return a Result like ok(). This is my setup:

AccountDao which has two methods:

public CompletionStage<Account> getUserByEmail(String email) {
    return supplyAsync(() -> ebeanServer.find(Account.class).setUseCache(true).where().eq(EMAIL, email).findOne(), executionContext).thenApply(account -> {
        return account;
    });
}

public CompletionStage<Void> updateAccount(Account account) throws OptimisticLockException{
    return runAsync(() -> {
        ebeanServer.update(account);
    }, executionContext);
}

And then i have my controller with the action:

public CompletionStage<Result> editAccount() {
    Map<String, String[]> form_values = request().body().asFormUrlEncoded();
    return CompletableFuture.completedFuture(ok());
}

So now in the action i want first to execute getUserByEmail and then i want to set some values and Update this with updateAccount method. How can i combine this two stages without blocking play context? I tried different setups with thenCompose and combine but i dont get it ...

Here one of my tries:

public CompletionStage<Result> editAccount() {
    Map<String, String[]> form_values = request().body().asFormUrlEncoded();
    accountDao.getUserByEmail(session().get("accountEmail")).thenCompose(x -> accountDao.updateAccount(x).thenApplyAsync(account -> {
        return ok("Going to save account edits");
    }, httpExecutionContext.current()));
    return CompletableFuture.completedFuture(ok("Fehler am Ende"));
}

The problem here is, that i cannot access the account (x) from before because i cannot set this as function ... like this:

public CompletionStage<Result> editAccount() {
    Map<String, String[]> form_values = request().body().asFormUrlEncoded();
    accountDao.getUserByEmail(session().get("accountEmail")).thenCompose(x -> {
        //Update vars in x and then save to database
        accountDao.updateAccount(x);
    }.thenApplyAsync(account -> {
        return ok("Going to save account edits");
    }, httpExecutionContext.current()));
    return CompletableFuture.completedFuture(ok("Fehler am Ende"));
}

Here i get the error: The target type of this expression must be a functional interface and plays says that i have to include the return statement at the end of the function!

I just dont get it ... Thanks for your help!

@Marimuthu Madasamy Thats no exactly what i want. In your awnser i would update the account twice. On etime in accountDao.updateAccount(account) and in accountDao.saveAccount(account); I want something like this:

return accountDao.getUserByEmail("mail").thenCompose(account -> {
       account.setName("NewName");
       accountDao.save(account);
    } .thenApplyAsync(voidInput -> {
        return ok("Account saved");
    }, httpExecutionContext.current()));

In this case in only update the account once and only return the result on the httpExecutionContext


Solution

  • Ok i found my own awnser here with the support of Marimuthu Madasamy! Thanks. I trie to explain it. First here is the code:

    public CompletionStage<Result> editAccount() {
        Map<String, String[]> form_values = request().body().asFormUrlEncoded();
        return accountDao.getUserByEmail(session().get("accountEmail")).thenApply(account -> {
            System.out.println("Async get Account / "+Thread.currentThread());
            account.setCompany(form_values.get("company")[0]);
            return accountDao.updateAccount(account);
        }).thenApplyAsync(account -> {
            System.out.println("Async resutl / "+Thread.currentThread());
            return ok("Account saved normal");
        }, httpExecutionContext.current()).exceptionally(e ->{
            System.out.println("Async exception / "+Thread.currentThread());
            System.out.println(e.getLocalizedMessage());
            return ok(e.getLocalizedMessage());
        });
    }
    

    Ok at first i execute accountDao.getUserByEmail() as you can see at top in my awnser this returns CompletionStage and is executed in my database execution context. At next with thenApply i get the result and i execute the next Async mehtod. I use thenApply instand of thenApplyAsync so the next call is also executed with the database execution context without setting it explicitly. After the accountDao.updateAccount() i execute the next stage on the httpExecutionContext to replay a result or to quit exceptionally! I really hope it is clear and helps some one!