2016-11-18 195 views
0

重新抛出exceptionally内部异常似乎不允许使用CompletionStage方法。Java 8 CompletionStage异常重新抛出异常

我需要检查某一种异常的,如果没有我需要把它重新抛出回:

Future<JsonNode> futureSite = someClient.getSite(siteId, queryParams); 

CompletionStage<JsonNode> outcome = FutureConverters.toJava(futureSite); 

return outcome.thenApplyAsync((siteJson) -> { 
      Site site = Json.fromJson(siteJson, Site.class); 
      try { 
       return function.apply(site); 
      } catch (RequestException e) { 
       return e.result; 
      } 
     }, httpExecutionContext.current()).exceptionally(throwable -> { 
      if(throwable instanceof SomeClientException) { 
       if(((SomeClientException) throwable).httpStatusCode == 404) { 
        return entityNotFound("Site", siteId); 
       } 
      } 


// let JSON parsing failures and other errors bubble up, TODO play2.5 


     throw throwable; 
     }); 

throw throwable错误出来说unhandledException java.lang.Throwable

什么接口将可能允许重新抛出回异常?还是有更好的办法?

更新:

我试图从霍尔格如下建议,但我仍然不知道在什么时候,我其实可以再次抛出这样的:

BaseController.java : 
     protected class RequestException extends Exception { 
      private static final long serialVersionUID = -2154871100650903869L; 

      public Result result; 

      public RequestException(Result result) { 
       this.result = result; 
      } 
     } 

     @FunctionalInterface 
     protected interface RequestFunction<T, R> { 
      R apply(T t) throws RequestException; 
     } 

    protected CompletionStage<Result> performWithSite(final Long siteId, RequestFunction<Site, Result> function) { 
      QueryParams queryParams = QueryParams.create(); 
      Future<JsonNode> futureSite = someClient.getSite(siteId, queryParams); 

      CompletionStage<JsonNode> outcome = FutureConverters.toJava(futureSite); 

      return handleSpecific(
        outcome.thenApplyAsync(siteJson -> { 
         Site site = Json.fromJson(siteJson, Site.class); 
         try { 
          return function.apply(site); 
         } catch (RequestException e) { 
          return e.result; 
         } 
        }, httpExecutionContext.current()), 
        throwable -> throwable instanceof SomeClientException 
          && ((SomeClientException)throwable).httpStatusCode == 404, 
        () -> entityNotFound("Site", siteId)); 
     } 

     protected Result entityNotFound(String entityName, String id) { 
    // building our custom error model. 
    Error e = new Error(
        Http.Status.NOT_FOUND, 
        ErrorCode.ENTITY_NOT_FOUND, 
        ErrorCodes.NOT_FOUND, new String[]{entityName, id}); 

      return notFound(e.asJson()); 
     } 

所以,上面的代码的本质是我需要联系某个客户端来检查该站点是否存在,并且客户端可以抛出SomeClientException异常。

Controller.java 

     public Result destroy(Long siteId, Long productId){ 
      return performWithSite(siteId, site -> { 
       productWriter.deleteProduct(siteId, productId); 
       return noContent(); 
      }).toCompletableFuture().exceptionally(e -> { 
       Logger.error(e+"exception"); 
       throw e.getCause(); 
       // above line errors out as unhandledException java.lang.throwable, I need the NotFoundException which is contained within the CompletionException to be thrown. 
      }); 
     } 

//在上面的控制器代码,我做的远程调用后,客户端检查站点存在,我需要继续前进,删除产品。

productWriter.deleteProduct(siteId, productId)可能仍然抛出NotFoundException或别的东西,我需要重新抛出回来...... 使从控制器代码重新抛出的异常是在调用链拿起我们的自定义异常处理程序。

这里是我的测试案例:

"return 404 when deleting a nonexistent Product" { 
     when(productDAO.findBySiteAndProductId(anyLong(), anyLong())) thenReturn null 

     a[NotFoundException] should be thrownBy { controller.destroy(0L, 1L) } 
    } 
+0

我认为这个问题不是特定游戏 –

回答

3

据我所知,没有内置的解决方案。您可以传递给链接方法CompletionStage的所有函数类型都被限制为未经检查的异常。你可以建立自己的实用方法:

public static <T> CompletionStage<T> handleSpecific(
    CompletionStage<T> previousStage, Predicate<Throwable> p, Supplier<T> s) { 

    CompletableFuture<T> result = new CompletableFuture<>(); 
    previousStage.whenComplete((value,throwable)->{ 
     if(throwable == null) result.complete(value); 
     else { 
      Throwable t = throwable; 
      if(t instanceof CompletionException) { 
       t = t.getCause(); 
       if(t == null) t = throwable; 
      } 
      if(p.test(t)) result.complete(s.get()); 
      else result.completeExceptionally(throwable); 
     } 
    }); 
    return result; 
} 

该解决方案可用于像:

return handleSpecific(
    outcome.thenApplyAsync(siteJson -> { 
     Site site = Json.fromJson(siteJson, Site.class); 
     try { 
      return function.apply(site); 
     } catch (RequestException e) { 
      return e.result; 
     } 
    }, httpExecutionContext.current()), 
    throwable -> throwable instanceof SomeClientException 
       && ((SomeClientException)throwable).httpStatusCode == 404, 
    () -> entityNotFound("Site", siteId)); 
+0

Holger非常感谢您的布局。但我仍然没有得到 _if(p.test(t))result.complete(s.get()); _ 会帮助传播在评估外部_function_时抛出的异常?所以我们定义了一个自定义的异常处理程序,如果在函数中抛出异常,它将导致CompletionException(这是上面的代码),并且我希望重新生成t? – hackmabrain

+0

有没有其他意见? – hackmabrain

+0

在您的原始代码中,您在'if'语句中编写了'return entityNotFound(“Site”,siteId)',所以如果条件满足,则不会传播该异常,而会返回一个值。这正是选址代码,'if(p.test(t))result.complete(s.get());',也是;如果谓词满足,则供应商返回的值将是结果。 – Holger

0

CompletionStage块抛出的所有异常,应裹成CompletionException(除非它已经)。在大多数情况下,CompletionStage将自行处理未检查的异常,并专门处理CompletionException以避免内部双重包装。

另外,在exceptionally中,异常可能会出现缠绕或不依赖于链中先前块的数量。