了解 java 播放中的异步调用
understanding async calls in java play
正在尝试在 java play framework 2.8 中制作 API。
我不知道我写的代码有什么问题,但它不起作用。
我在 localhost:9000
中收到此回复
{"result":null,"stack":null,"done":false,"cancelled":false,"completedExceptionally":false,"numberOfDependents":0}
我想我在异步调用方面遗漏了一些东西,因为当我尝试单独制作 运行 代码时它可以工作(使用邮递员或另一个游乐场 class)。
我正在上传代码,很乐意知道它有什么问题。
提前致谢!
package utils;
import com.fasterxml.jackson.databind.JsonNode;
import models.Commit;
import play.libs.ws.WSResponse;
import javax.inject.Inject;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
public class CommitsService {
private BBService bbService;
@Inject
public CommitsService(BBService bbService) {
this.bbService = bbService;
}
public CompletionStage<CompletableFuture<List<Commit>>> retrieveCommitsForUser(String username) {
CompletionStage<List<String>> reposPromise = getRepos();
return reposPromise
.thenApplyAsync(repos -> sequence(repos.stream()
.map(repo -> getRepoCommits(repo))
.collect(Collectors.toList()))
.thenApplyAsync(responses -> {
List<Commit> allCommits = responses.stream()
.map(wsResponse -> extractCommits(wsResponse))
.flatMap(List::stream).filter(commit -> isByUser(commit, username) && isLastDay(commit))
.collect(Collectors.toList());
return allCommits;
}));
}
private CompletionStage<List<String>> getRepos() {
return bbService.getRepos()
.thenApplyAsync(response -> extractRepos(response));
}
private List<String> extractRepos(WSResponse wsResponse) {
Vector<String> repos = new Vector<>();
JsonNode reposAsJson = wsResponse.asJson().findValue("values");
for (JsonNode repo : reposAsJson) {
String repoName = repo.path("name").asText();
repos.add(repoName);
}
return repos;
}
private CompletableFuture<WSResponse> getRepoCommits(String repo) {
return bbService.getRepoCommits(repo).toCompletableFuture();
}
private boolean isByUser(Commit commit, String username) {
return commit.getAuthor().equals(username);
}
private boolean isLastDay(Commit commit) {
String sDate = commit.getDate().substring(0, commit.getDate().indexOf('T'));
String sTime = commit.getDate().substring(commit.getDate().indexOf('T'), commit.getDate().length() - 1);
return true;
}
private List<Commit> extractCommits(WSResponse wsResponse) {
Vector<Commit> allCommits = new Vector<>();
JsonNode commits = wsResponse.asJson().findValue("values");
for (JsonNode commit : commits) {
String HASH = commit.path("hash").asText();
String date = commit.path("date").asText();
String repo = commit.path("repository").path("name").asText();
String message = commit.path("message").asText();
String author = commit.path("author").path("user").path("display_name").asText();
Commit currCommit = new Commit(HASH, date, repo, message, author);
allCommits.add(currCommit);
}
return allCommits;
}
public <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futuresList) {
return CompletableFuture
.allOf(futuresList.toArray(new CompletableFuture[0])) // (1)
.thenApply(v ->
futuresList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
}
package controllers;
import play.libs.Json;
import play.mvc.*;
import utils.CommitsService;
import javax.inject.Inject;
import java.util.concurrent.CompletionStage;
/**
* Commits controller is handling in and out http traffic regarding bitbucket commits.
* Supporting get but can be expanded to other crud operations.
*/
public class CommitsController extends Controller {
private final CommitsService commitsService;
@Inject
public CommitsController(CommitsService commitsService) {
this.commitsService = commitsService;
}
public CompletionStage<Result> showCommits(String username) {
return commitsService.retrieveCommitsForUser(username)
.thenApplyAsync(res -> ok(Json.toJson(res)));
}
}
package utils;
import com.fasterxml.jackson.databind.JsonNode;
import play.libs.ws.*;
import play.mvc.Result;
import javax.inject.Inject;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
public class BBService {
private final String BB_BASE_URL = "https://api.bitbucket.org/2.0/";
private final String URL = BB_BASE_URL + "repositories/******/";
private final String COMMITS = "commits";
private final String bbUsername = "*******";
private final String bbPassword = "*******";
private final WSClient ws;
@Inject
public BBService(WSClient ws) {
this.ws = ws;
}
public CompletionStage<WSResponse> getAsync(String url) {
WSRequest req = ws.url(url).setAuth(bbUsername, bbPassword);
return req.get();
}
public List<String> extractNames(WSResponse wsResponse) {
Vector<String> usersNames = new Vector<>();
JsonNode users = wsResponse.asJson().findValue("values");
for (JsonNode user : users) {
String userName = user.findValue("display_name").asText();
usersNames.add(userName);
}
return usersNames;
}
public CompletionStage<WSResponse> getRepos() {
return getAsync(URL);
}
public CompletionStage<WSResponse> getRepoCommits(String repoName) {
return getAsync(URL + repoName + "/" + COMMITS);
}
public Result withFullAccess(Result noAccessibleRes) {
return noAccessibleRes.withHeader("Access-Control-Allow-Origin", "*");
}
public <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futuresList) {
return CompletableFuture
.allOf(futuresList.toArray(new CompletableFuture[0])) // (1)
.thenApply(v ->
futuresList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
}
考虑到您得到的响应,问题是您给出的响应对象类型为 CompletableFuture<List<Commit>>
而不是您可能想要的 List<Commit>
,在 CommitsController
中:
retrieveCommitsForUser(username)
.thenApplyAsync(res -> ok(Json.toJson(res)))
您在其中提供 res
类型 CompletableFuture<List<Commit>>
- 这就是为什么您在响应中看到此 class.
中的所有字段的原因
你想要做的是在方法CommitsService.retrieveCommitsForUser
中return到returnCompletionStage<List<Commit>>
而不是CompletionStage<CompletableFuture<List<Commit>>>
.
CompletionStage.thenComposeAsync
可以帮助您 - 它可以将当前 CompletionStage
的执行与下一个 CompletionStage
的执行链接起来。
所以最终的解决方案可能是这样的:
public CompletionStage<List<Commit>> retrieveCommitsForUser(String username) {
CompletionStage<List<String>> reposPromise = getRepos();
return reposPromise
.thenComposeAsync(repos ->
sequence(repos.stream().map(repo -> getRepoCommits(repo)).collect(Collectors.toList()))
.thenApplyAsync(responses -> {
List<Commit> allCommits = responses.stream()
.map(wsResponse -> extractCommits(wsResponse))
.flatMap(List::stream).filter(commit -> isByUser(commit, username) && isLastDay(commit))
.collect(Collectors.toList());
return allCommits;
}));
}
希望对您有所帮助!
正在尝试在 java play framework 2.8 中制作 API。 我不知道我写的代码有什么问题,但它不起作用。
我在 localhost:9000
中收到此回复{"result":null,"stack":null,"done":false,"cancelled":false,"completedExceptionally":false,"numberOfDependents":0}
我想我在异步调用方面遗漏了一些东西,因为当我尝试单独制作 运行 代码时它可以工作(使用邮递员或另一个游乐场 class)。
我正在上传代码,很乐意知道它有什么问题。 提前致谢!
package utils;
import com.fasterxml.jackson.databind.JsonNode;
import models.Commit;
import play.libs.ws.WSResponse;
import javax.inject.Inject;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
public class CommitsService {
private BBService bbService;
@Inject
public CommitsService(BBService bbService) {
this.bbService = bbService;
}
public CompletionStage<CompletableFuture<List<Commit>>> retrieveCommitsForUser(String username) {
CompletionStage<List<String>> reposPromise = getRepos();
return reposPromise
.thenApplyAsync(repos -> sequence(repos.stream()
.map(repo -> getRepoCommits(repo))
.collect(Collectors.toList()))
.thenApplyAsync(responses -> {
List<Commit> allCommits = responses.stream()
.map(wsResponse -> extractCommits(wsResponse))
.flatMap(List::stream).filter(commit -> isByUser(commit, username) && isLastDay(commit))
.collect(Collectors.toList());
return allCommits;
}));
}
private CompletionStage<List<String>> getRepos() {
return bbService.getRepos()
.thenApplyAsync(response -> extractRepos(response));
}
private List<String> extractRepos(WSResponse wsResponse) {
Vector<String> repos = new Vector<>();
JsonNode reposAsJson = wsResponse.asJson().findValue("values");
for (JsonNode repo : reposAsJson) {
String repoName = repo.path("name").asText();
repos.add(repoName);
}
return repos;
}
private CompletableFuture<WSResponse> getRepoCommits(String repo) {
return bbService.getRepoCommits(repo).toCompletableFuture();
}
private boolean isByUser(Commit commit, String username) {
return commit.getAuthor().equals(username);
}
private boolean isLastDay(Commit commit) {
String sDate = commit.getDate().substring(0, commit.getDate().indexOf('T'));
String sTime = commit.getDate().substring(commit.getDate().indexOf('T'), commit.getDate().length() - 1);
return true;
}
private List<Commit> extractCommits(WSResponse wsResponse) {
Vector<Commit> allCommits = new Vector<>();
JsonNode commits = wsResponse.asJson().findValue("values");
for (JsonNode commit : commits) {
String HASH = commit.path("hash").asText();
String date = commit.path("date").asText();
String repo = commit.path("repository").path("name").asText();
String message = commit.path("message").asText();
String author = commit.path("author").path("user").path("display_name").asText();
Commit currCommit = new Commit(HASH, date, repo, message, author);
allCommits.add(currCommit);
}
return allCommits;
}
public <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futuresList) {
return CompletableFuture
.allOf(futuresList.toArray(new CompletableFuture[0])) // (1)
.thenApply(v ->
futuresList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
}
package controllers;
import play.libs.Json;
import play.mvc.*;
import utils.CommitsService;
import javax.inject.Inject;
import java.util.concurrent.CompletionStage;
/**
* Commits controller is handling in and out http traffic regarding bitbucket commits.
* Supporting get but can be expanded to other crud operations.
*/
public class CommitsController extends Controller {
private final CommitsService commitsService;
@Inject
public CommitsController(CommitsService commitsService) {
this.commitsService = commitsService;
}
public CompletionStage<Result> showCommits(String username) {
return commitsService.retrieveCommitsForUser(username)
.thenApplyAsync(res -> ok(Json.toJson(res)));
}
}
package utils;
import com.fasterxml.jackson.databind.JsonNode;
import play.libs.ws.*;
import play.mvc.Result;
import javax.inject.Inject;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
public class BBService {
private final String BB_BASE_URL = "https://api.bitbucket.org/2.0/";
private final String URL = BB_BASE_URL + "repositories/******/";
private final String COMMITS = "commits";
private final String bbUsername = "*******";
private final String bbPassword = "*******";
private final WSClient ws;
@Inject
public BBService(WSClient ws) {
this.ws = ws;
}
public CompletionStage<WSResponse> getAsync(String url) {
WSRequest req = ws.url(url).setAuth(bbUsername, bbPassword);
return req.get();
}
public List<String> extractNames(WSResponse wsResponse) {
Vector<String> usersNames = new Vector<>();
JsonNode users = wsResponse.asJson().findValue("values");
for (JsonNode user : users) {
String userName = user.findValue("display_name").asText();
usersNames.add(userName);
}
return usersNames;
}
public CompletionStage<WSResponse> getRepos() {
return getAsync(URL);
}
public CompletionStage<WSResponse> getRepoCommits(String repoName) {
return getAsync(URL + repoName + "/" + COMMITS);
}
public Result withFullAccess(Result noAccessibleRes) {
return noAccessibleRes.withHeader("Access-Control-Allow-Origin", "*");
}
public <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futuresList) {
return CompletableFuture
.allOf(futuresList.toArray(new CompletableFuture[0])) // (1)
.thenApply(v ->
futuresList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
}
考虑到您得到的响应,问题是您给出的响应对象类型为 CompletableFuture<List<Commit>>
而不是您可能想要的 List<Commit>
,在 CommitsController
中:
retrieveCommitsForUser(username)
.thenApplyAsync(res -> ok(Json.toJson(res)))
您在其中提供 res
类型 CompletableFuture<List<Commit>>
- 这就是为什么您在响应中看到此 class.
你想要做的是在方法CommitsService.retrieveCommitsForUser
中return到returnCompletionStage<List<Commit>>
而不是CompletionStage<CompletableFuture<List<Commit>>>
.
CompletionStage.thenComposeAsync
可以帮助您 - 它可以将当前 CompletionStage
的执行与下一个 CompletionStage
的执行链接起来。
所以最终的解决方案可能是这样的:
public CompletionStage<List<Commit>> retrieveCommitsForUser(String username) {
CompletionStage<List<String>> reposPromise = getRepos();
return reposPromise
.thenComposeAsync(repos ->
sequence(repos.stream().map(repo -> getRepoCommits(repo)).collect(Collectors.toList()))
.thenApplyAsync(responses -> {
List<Commit> allCommits = responses.stream()
.map(wsResponse -> extractCommits(wsResponse))
.flatMap(List::stream).filter(commit -> isByUser(commit, username) && isLastDay(commit))
.collect(Collectors.toList());
return allCommits;
}));
}
希望对您有所帮助!