2016-03-26 12 views
7

मैं एक साधारण ऐप बनाने के लिए Play 2.5 का उपयोग कर रहा हूं। बेहतर प्रदर्शन के लिए मैं जावा 8 CompletionStage रणनीति के साथ अक्का चंक प्रतिक्रिया का उपयोग कर रहा हूं। यहाँ
प्ले फ्रेमवर्क 2.5 जावाएसिंक फेंकिंग CompletionException

@Singleton 
public class AbstractSource { 

    public Source<ByteString, ?> getChunked(String html) { 

     return Source.<ByteString>actorRef(256, OverflowStrategy.dropNew()) 
       .mapMaterializedValue(sourceActor -> { 
        sourceActor.tell(ByteString.fromString(html), null); 
        sourceActor.tell(new Status.Success(NotUsed.getInstance()), null); 
        return null; 
       }); 

    } 

} 

और मेरे नियंत्रक है: नीचे दिए गए कोड से जो chunked प्रतिक्रिया उत्पन्न हो रही है (यह जब ComperableFuture का उपयोग नहीं कर ठीक काम कर रहा है) है जब मैं चल रहा हूँ अब

@Singleton 
@AddCSRFToken 
public class Application extends Controller { 

    @Inject 
    private AbstractSource abstractSource; 

    public CompletionStage<Result> index() { 


     CompletionStage<Source<ByteString, ?>> source = CompletableFuture.supplyAsync(() -> 
                abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t -> 
                t.value()).orElse("no token")).body() 
                ) 
               ); 

     return source.thenApply(chunks -> ok().chunked(chunks)); 

    } 

} 

एप्लिकेशन यह निम्नलिखित अपवाद फेंक रहा है:

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[CompletionException: java.lang.RuntimeException: There is no HTTP Context available from here.]] 
    at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:269) 
    at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:195) 
    at play.api.GlobalSettings$class.onError(GlobalSettings.scala:160) 
    at play.api.DefaultGlobal$.onError(GlobalSettings.scala:188) 
    at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:98) 
    at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:99) 
    at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:98) 
    at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:344) 
    at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:343) 
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) 
Caused by: java.util.concurrent.CompletionException: java.lang.RuntimeException: There is no HTTP Context available from here. 
    at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) 
    at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) 
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592) 
    at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582) 
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) 
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) 
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) 
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) 
Caused by: java.lang.RuntimeException: There is no HTTP Context available from here. 
    at play.mvc.Http$Context.current(Http.java:57) 
    at play.mvc.Controller.request(Controller.java:36) 
    at com.mabsisa.ui.web.controllers.Application.lambda$index$1(Application.java:31) 
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590) 
    ... 5 common frames omitted 

मैं HTTP संदर्भ कहीं भी उपयोग नहीं कर रहा हूँ, तो क्यों यह काम नहीं कर रहा मैं ' मैं नहीं मिल रहा हूँ खंडित प्रतिक्रिया के साथ सामान्य परिणाम लौटने पर वही कोड काम कर रहा है। कृपया इस

उत्तर

13

के साथ मदद HTTP निष्पादन संदर्भ आपूर्ति करने के लिए जब CompletableFuture/CompletionStage के साथ काम कर आपके पास । स्कैला में संदर्भ जानकारी implicits के माध्यम से पारित की जाती है, ये जावा में उपलब्ध नहीं हैं - यही कारण है कि Play ThreadLocal का उपयोग करता है।

हालांकि थ्रेड स्विच करते समय आप यह जानकारी खो सकते हैं और यही कारण है कि आपको समस्या है। आप सोच सकते हैं कि आप HTTP संदर्भ तक नहीं पहुंचते हैं लेकिन वास्तव में आप करते हैं - आप request() का उपयोग कर रहे हैं।

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#supplyAsync-java.util.function.Supplier-java.util.concurrent.Executor-

इस से:

CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t -> 
                t.value()).orElse("no token")).body() 
                ) 
               ); 
इस के लिए

:

CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t -> 
                t.value()).orElse("no token")).body() 
                ) 
               , ec.current()); 

जहां ec है

तो तुम एक निर्वाहक साथ supplyAsync उपयोग करने के लिए अपने कोड बदलना होगा आपका संदर्भ: @Inject HttpExecutionContext ec;

+0

अच्छी व्याख्या के साथ अजीब जवाब ...... एक और बात यह पूछना चाहती है कि यह खेल में एक अच्छा डिजाइन दृष्टिकोण है या नहीं? सीएन आप कृपया अपनी राय साझा करें? –

+1

'CompletionStage ' लौटने पर _de facto_ मानक है Play 2.5 में एसिंक क्रियाओं को परिभाषित करने के लिए, तो आप यहां ठीक हैं। Play में खंडित प्रतिक्रियाओं के बारे में: https://www.playframework.com/documentation/2.5.x/JavaStream#Chunked-responses पर एक नज़र डालें - अक्का धाराओं का उपयोग करके आप पहले से ही एक अच्छे ट्रैक पर हैं इसलिए चिंता न करें - आपका डिजाइन दृष्टिकोण अच्छा है! – Anton

+0

सलाह के लिए एक स्वर धन्यवाद @ एंटन –

0

मैं एंटोन के उत्तर के अलावा।

आप उपयोग कर जावा एपीआई खेलते हैं एक गैर अवरुद्ध एप्लिकेशन का निर्माण कर रहे हैं, तो यह काफी HttpExecutionContext इंजेक्षन और ec.current()) हर बार जब आप CompletionStage पर तरीकों कॉल करने की आवश्यकता पारित करने के लिए बोझिल हो सकता है।

जीवन को आसान बनाने के लिए आप एक सजावट का उपयोग कर सकते हैं, जो कॉल के बीच संदर्भ को संरक्षित रखेगा।

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; 
    } 
    ... 
} 

तो आप केवल एक बार संदर्भ पारित करने के लिए की आवश्यकता होगी:

return new ContextPreservingCompletionStage<>(someCompletableFuture, context) 
          .thenCompose(something -> {...}); 
          .thenApply(something -> {...}); 
इसके बजाय

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

कि विशेष रूप से उपयोगी है यदि आप एक बहु स्तरीय एप्लिकेशन निर्माण कर रहे हैं, और CompletionStage गुजर अगर की

विभिन्न वर्गों के बीच है।

पूर्ण सजावटी कार्यान्वयन उदाहरण is here

+0

धन्यवाद :) –

संबंधित मुद्दे