2016-11-17 5 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 플레이를 사용)은 손실되고 비동기 호출을 수행하면 null이됩니다 (FutureConverters.toJava(futureSite).thenApplyAsync((siteJson), t는 별도의 스레드에 있기 때문입니다.

위의 함수를 사용하는 컨트롤러 코드에서 문제가 발생합니다 ... request()는 이제 사용할 수있는 컨텍스트가 없다는 런타임 예외를 throw합니다.

어떻게 컨텍스트를 보존 할 수 있습니까?

답변

3

컨트롤러에 play.libs.concurrent.HttpExecutionContext를 삽입 한 다음 CompletionStage # thenApplyAsync (.., ..)에 대한 두 번째 인수로 현재 컨텍스트를 지정해야합니다.

public class Application extends Controller { 
@Inject HttpExecutionContext ec; 

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

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

0

나는 또한 에 추가했다. Nick 's 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 -> {...}); 

서로 다른 클래스 사이에 있습니다.

전체 데코레이터 구현 예 is here.