2012-07-17 14 views
13

In un'applicazione Play Framework 2.0.1 (Scala), stiamo utilizzando una libreria client di servizi Web che restituisce java.util.concurrent.Future come risposte.Come faccio a racchiudere un java.util.concurrent.Future in un futuro Akka?

Invece di bloccare Play app sulla chiamata get(), vorremmo avvolgere il j.u.c.Future in un akka.dispatch.Future, in modo da poter usare facilmente AsyncResult di elaborazione del quadro di gioco.

Qualcuno ha già eseguito questa operazione o dispone di una libreria o di un codice di esempio?


UPDATE: La cosa più vicina che abbiamo trovato è questa discussione gruppi di Google: https://groups.google.com/forum/#!topic/play-framework/c4DOOtGF50c

... se tutto quello che hai è un jucFuture pianura il meglio che si può fare per creare una soluzione non bloccante è prendere jucFuture e una Promessa e assegnarli a qualche thread che esegue un ciclo di polling che completerà la Promessa con il risultato del Futuro una volta terminato.

Qualcuno ha un'implementazione di esempio di questo?

risposta

7

@Viktor Klang: Comprendiamo che j.u.c.Future è un abominio. Ma è quello che stiamo recuperando da un software che dobbiamo accettare per il momento.

Finora, questo è ciò che abbiamo messo insieme:

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 
} 

In altre parole, creare un Akka separata Promise (write-lato di un Future) corrispondente alla j.u.c.Future, prende il via la richiamata pollJavaFutureUntilDoneOrCancelled per aggiornare la Promessa eseguendo il polling dell '"abominio" e restituisce la Promessa al chiamante.

Quindi, come possiamo "eseguire il polling" per aggiornare la Promessa Akka in base allo stato di 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) 
     } 
    } 
    } 
} 

Questo è un tentativo di ciò che è stato accennato nella discussione sui gruppi di Google a cui ho fatto riferimento nella domanda. Usa lo schedulatore Akka per richiamarsi ogni 50 ms per verificare se j.u.c.Future è terminato o cancellato. Ogni volta che ciò accade, aggiorna la promessa Akka con lo stato completato.

@Victor Klang, et al:

È questa best practice? Sai di un modo migliore per farlo? Ci manca un aspetto negativo qui che dovremmo sapere?

Grazie per l'ulteriore aiuto.

+0

Uno svantaggio evidente è, che nella peggiore delle ipotesi questo causerà un alto ritardo della risposta. Se ad esempio hai le impostazioni predefinite e il tuo futuro completa 1 ms dopo il controllo, può causare un ritardo di circa 100ms. Questo può tuttavia essere regolato impostando 'scheduler.impostazione tick-duration' nella config. – drexin

+0

@drexin true, ma in qualsiasi soluzione basata sul polling saranno presenti una durata della battito e un compromesso della frequenza dei sondaggi, giusto? –

+1

Certo, ma come hai chiesto degli svantaggi, volevo solo dirti che non dipende solo dal parametro delay della chiamata 'scheduleOnce', ma anche dall'impostazione nella configurazione di akka. Se riesci a vivere con un ritardo, questa dovrebbe essere una soluzione utilizzabile. – drexin

0

Si dovrebbe usare akka.dispatch.Futures.future() con java.util.concurrent.Callable:

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

Questo essenzialmente si traduce in più thread del necessario, uno dei quali blocchi, e non è meglio che semplicemente chiamando javaFuture.get sul thread principale. Presentare akka future non è utile qui tranne in situazioni estreme in cui la compatibilità dei componenti è assolutamente necessaria. – vishr

Problemi correlati