2012-07-17 6 views
13

In einer Play Framework 2.0.1 (Scala) -Anwendung verwenden wir eine Web-Service-Client-Bibliothek, die java.util.concurrent.Future als Antworten zurückgibt.Wie wickle ich eine java.util.concurrent.Future in eine Akka-Zukunft?

Statt den Play-App auf den get() Anruf blockieren möchten wir die j.u.c.Future in einem akka.dispatch.Future wickeln, so dass wir leicht den AsyncResult Verarbeitung Spiel Rahmen nutzen können.

Hat jemand das schon einmal gemacht oder eine Bibliothek oder einen Beispielcode?


UPDATE: Das nächste, was wir gefunden haben, ist diese Google Groups Diskussion: https://groups.google.com/forum/#!topic/play-framework/c4DOOtGF50c

... wenn alles, was Sie eine einfache jucFuture ist haben das Beste, was Sie erstellen tun können, um Eine nicht blockierende Lösung besteht darin, jucFuture und ein Versprechen anzunehmen und sie einem Thread zu übergeben, der eine Polling-Schleife ausführt, die das Versprechen mit dem Ergebnis der Zukunft vervollständigt, wenn es fertig ist.

Hat jemand eine Beispielimplementierung davon?

Antwort

7

@Viktor Klang: Wir verstehen, dass j.u.c.Future eine Abscheulichkeit ist. Aber das ist es, was wir von einer Software zurückbekommen, die wir vorläufig akzeptieren müssen.

Bisher das ist, was wir zusammen gehackt haben:

def wrapJavaFutureInAkkaFuture[T](javaFuture: java.util.concurrent.Future[T], maybeTimeout: Option[Duration] = None)(implicit system: ActorSystem): akka.dispatch.Future[T] = { 
    val promise = new akka.dispatch.DefaultPromise[T] 
    pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, maybeTimeout.map(_.fromNow)) 
    promise 
} 

Mit anderen Worten, erstellen Sie eine separate Akka Promise (der Schreibseite eines Future) an die j.u.c.Future entspricht, schlägt den Rückruf aus pollJavaFutureUntilDoneOrCancelled, um das Versprechen zu aktualisieren, indem die "Gräuel" abgefragt wird, und das Versprechen an den Anrufer zurückgibt.

Wie also "pollen" wir das Akka Versprechen basierend auf dem Zustand der j.u.c.Future?

def pollJavaFutureUntilDoneOrCancelled[T](javaFuture: java.util.concurrent.Future[T], promise: akka.dispatch.Promise[T], maybeDeadline: Option[Deadline] = None)(implicit system: ActorSystem) { 
    if (maybeDeadline.exists(_.isOverdue)) javaFuture.cancel(true); 

    if (javaFuture.isDone || javaFuture.isCancelled) { 
    promise.complete(allCatch either { javaFuture.get }) 
    } else { 
    Play.maybeApplication.foreach { implicit app => 
     system.scheduler.scheduleOnce(50 milliseconds) { 
     pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, maybeDeadline) 
     } 
    } 
    } 
} 

Dies ist ein Versuch, was in der Google-Gruppen-Diskussion angedeutet wurde, dass ich in der Frage verwiesen habe. Es benutzt den Akka-Scheduler, um sich alle 50 ms selbst zurückzurufen, um zu überprüfen, ob die j.u.c.Future entweder erledigt oder abgebrochen ist. Wann immer das passiert, aktualisiert es das Akka-Versprechen mit dem abgeschlossenen Status.

@Victor Klang, et al:

Ist die beste Praxis? Kennen Sie einen besseren Weg, dies zu tun? Vermissen wir hier einen Nachteil, über den wir Bescheid wissen sollten?

Vielen Dank für weitere Hilfe.

+0

Ein offensichtlicher Nachteil ist, dass im schlimmsten Fall eine hohe Verzögerung der Reaktion verursacht. Wenn Sie zum Beispiel die Standardeinstellungen haben und Ihre Zukunft 1 ms nach der Prüfung abgeschlossen ist, kann es zu einer Verzögerung von etwa 100 ms kommen. Dies kann jedoch durch Einstellen des Schedulers eingestellt werden.tick-duration' Einstellung in der Konfig. – drexin

+0

@drexin wahr, aber eine Tick-Dauer und Poll-Frequenz-Trade-off wird in jeder polling-basierten Lösung vorhanden sein, oder? –

+1

Sicher, aber als du nach Downsides gefragt hast, wollte ich dir nur sagen, dass es nicht nur vom Delay-Parameter des 'scheduleOnce'-Calls abhängt, sondern auch von der Einstellung in der akka config. Wenn Sie mit einer Verzögerung leben können, sollte dies eine brauchbare Lösung sein. – drexin

0

Sie sollten akka.dispatch.Futures.future() mit java.util.concurrent.Callable verwenden:

val akkaFuture: akka.dispatch.Future[String] = akka.dispatch.Futures.future(
    new java.util.concurrent.Callable[String] { 
    def call: String = { 
     return "scala->" + javaFuture.get 
    } 
}, executionContext) 

Gist for complete example

+0

Dies führt im Wesentlichen zu mehr Threads als notwendig, von denen einer blockiert, und ist nicht besser als einfach javaFuture.get auf den Hauptthread aufrufen. Die Einführung von akka future ist hier nicht von Vorteil, außer in extremen Situationen, in denen die Komponentenkompatibilität unbedingt erforderlich ist. – vishr