2016-11-17 8 views
1

を呼び出して非同期でコンテキストを保存する2.5を再生する:、我々はいくつかのデータを取得するために別のサービスに手を差し伸べる、当社のコントローラクラスで

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

return FutureConverters.toJava(futureSite).thenApplyAsync((siteJson) -> { 
    Site site = Json.fromJson(siteJson, Site.class); 
    try { 
     return function.apply(site); 
    } catch (RequestException e) { 
     return e.result; 
    } 
}).exceptionally(throwable -> { 
    if(throwable instanceof OurClientException) { 
     if(((OurClientException) throwable).httpStatusCode == 404) { 
      return entityNotFound("Site", siteId); 
     } 
    } 
    return null; 
}); 

私たちはユニットテストに設定されている文脈で気づく何(私たちはscalatestプレイを使用します)は失われ、非同期呼び出しを(FutureConverters.toJava(futureSite).thenApplyAsync((siteJson)にすると、別のスレッド上にあるので、nullになります。

私たちが上記の関数を使用するコントローラコードで問題を引き起こします... request()は、利用可能なコンテキストがないことを示すランタイム例外をスローします。

どのようにコンテキストを保存できますか?

答えて

3

コントローラにplay.libs.concurrent.HttpExecutionContextを挿入し、CompletionStage#thenApplyAsync(..、..)の2番目の引数として現在のコンテキストを指定する必要があります。

public class Application extends Controller { 
@Inject HttpExecutionContext ec; 

public CompletionStage<Result> index() { 
    someCompletableFuture.supplyAsync(() -> { 
     // do something with request() 
    }, ec.current()); 
}} 

P.S. https://www.playframework.com/documentation/2.5.x/JavaAsync#Using-CompletionStage-inside-an-Action

0

さらに、ニックのV答えに加えて。

Play Java APIを使用して非ブロッキングアプリを構築する場合、CompletionStageでメソッドを呼び出す必要があるたびに、HttpExecutionContextを注入してec.current())を渡すのはかなり面倒なことがあります。

人生を楽にするために、デコレータを使用すると、コール間のコンテキストが保持されます。

public class ContextPreservingCompletionStage<T> implements CompletionStage<T> { 

    private HttpExecutionContext context; 
    private CompletionStage<T> delegate; 

    public ContextPreservingCompletionStage(CompletionStage<T> delegate, 
              HttpExecutionContext context) { 
     this.delegate = delegate; 
     this.context = context; 
    } 
    ... 
} 

だから、一度だけ、コンテキストを渡す必要があります:あなたは、マルチティアアプリケーションを構築し、CompletionStageを渡している場合に特に便利です

return someCompletableFuture.thenComposeAsync(something -> {...}, context.current()) 
           .thenApplyAsync(something -> {...}, context.current()); 

の代わりに

return new ContextPreservingCompletionStage<>(someCompletableFuture, context) 
            .thenCompose(something -> {...}); 
            .thenApply(something -> {...}); 

sを異なるクラス間で使用します。

フルデコレータの実装例is here

関連する問題