我有一个具有端点的rest控制器:
@GET
@Path("/reindex-record")
public String reindexRecord(@QueryParam("id") String id) {
if (StringUtils.isEmpty(id)) {
CompletableFuture.runAsync(
() -> runWithException(Reindexer::reindexAll));
} else {
CompletableFuture.runAsync(() -> runWithException(
() -> Reindexer.reindexOne(id)));
}
// return "ok" or throw WebApplciationException from runWithException method below
}
这是我的包装器方法-两种方法-reindexAll和reindexOne都抛出检查异常,因此决定使用包装器方法和接口:
public interface RunnableWithException {
void run() throws Exception;
}
private void runWithException(RunnableWithException task) {
try {
task.run();
} catch (Exception e) {
log.error("Error occured during async task execution", e);
throw new WebApplicationException(
Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Internal error occurred").build());
}
}
问题是我想使用CompleteableFuture不同步地运行此任务,并且仅在完成给定任务后或在发生错误时抛出WebApplicationException且状态为INTERNAL_SERVER_ERROR的情况下给出响应。
您如何在if / else的用例中实现它?
编辑: 到目前为止,我有这种方法:
@GET
@Path("/reindex-record")
public String reindexRecord(@QueryParam("id") String id) throws ExecutionException,
InterruptedException {
CompletableFuture<Void> task;
if (StringUtils.isEmpty(id)) {
task = CompletableFuture.runAsync(
() -> runWithException(Reindexer::reindexAll));
} else {
task = CompletableFuture.runAsync(() -> runWithException(
() -> Reindexer.reindexOne(id)));
}
return task.thenApply(x -> "ok")
.exceptionally(throwable -> {
log.error("Error occured during async task execution", throwable);
throw new WebApplicationException(Response.status(Response.Status.SERVICE_UNAVAILABLE)
.entity("Internal error occurred. Try again later")
.build());
}).get();
}
但是,如果任何Reindexer方法引发错误,我仍然会获得状态500的数据:
{
"code": 500,
"message": "There was an error processing your request. It has been logged (ID 03f09a62b62b1649)."
}
而不是在我的exceptionally
块中定义的503。
如果重要的话,将dropwizard与JAX-RS一起使用。
答案 0 :(得分:1)
您可以将方法的主体更改为此:
@GET
@Path("/reindex-record")
public String reindexRecord(@QueryParam("id") String id) {
final CompletableFuture<Void> future;
if (StringUtils.isEmpty(id)) {
future = CompletableFuture.runAsync(
() -> runWithException(Reindexer::reindexAll));
} else {
future = CompletableFuture.runAsync(
() -> runWithException(() -> Reindexer.reindexOne(id)));
}
// this will block
future.get();
return "ok";
}
通过存储未来,您可以在其上调用get()
方法,该方法将阻塞直到未来完成。
来自CompletableFuture.get()
的javadoc:
等待将来完成,然后返回结果。
答案 1 :(得分:0)
问题是您正在使用exceptionally()
进行某些非预期的操作。 CompletableFuture
旨在用于CompletableFuture
的链中,其中一个的输出馈入下一个。如果CompletableFuture
之一抛出异常怎么办?您可以使用exceptionally
来捕获该错误,并返回新的后备ComletableFuture
供链的下一步使用。
您没有这样做,您只抛出了WebApplicationException
。 CompleteableFuture将其视为链中的故障,并将您的WebApplicationException
包装在ExecutionException
中。 Dropwizard仅看到ExecutionException(它不检查任何包装的异常),并引发通用500响应。
答案只是执行@Lino答案中的future.get();
,但包裹在try ... catch块中以获取ExecutionException,然后从捕获中抛出WebApplicationException。
try {
// this will block
future.get();
} catch (ExecutioException) {
throw new WebApplicationException(
Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Internal error occurred").build());
}
return "ok";
您也许可以将整个throw new WebApplicationException(...
缩短为简单的throw new InternalServerErrorException()