2011-12-14 1 views
8

Esiste un modo per utilizzare il meccanismo await() all'esterno di un controller?chiamate WS asincrone e waitit() all'esterno di un controller

Non desidero che il codice esegua la chiamata asincrona nel controller, ma invece il codice in una classe di servizio che può essere riutilizzato da più controller tuttavia, non c'è modo di richiamare l'attesa all'esterno di un controller dal quel il metodo è protetto.

Così, per esempio nel controller:

ServiceClass service = new My ServiceClass(); 
    MyObject myObject= service.getMeAnObject(); 
    render(myObject); 

E la classe di servizio:

public class ServiceClass 
    { 
     ... 
     public MyObject getMeAnObject() 
     { 
     String url = "http://..."; 
     Promise<HttpResponse> promise = url(url).getAsync(); 

     // do something here similar to await in a controller 
     // that suspends the code waiting for the response 
     // to be retrieved 

     HttpResponse response = promise.get(); 
     return buildMyObjectFromResponse(reponse); 
     } 
     ... 
    } 

C'è un modo per ottenere qualcosa di simile?

Grazie per il vostro aiuto.


Edit: Ho seguito il consiglio di Pere e ha fatto la classe di servizio implementare controller, funziona però, è necessario che il controller di usarlo essere migliorata. L'unico modo che ho scoperto per ottenere quello fatto è almeno invocare una volta che il metodo await() nella classe controller di chiamata.

Tuttavia, non ho ancora verificato che il codice sia effettivamente sospeso.


Edit 2: Un suggerimento che ho ricevuto dal gruppo Google è che dovrei veramente provare a fare l'attendono nel controller, quindi forse una soluzione migliore sarebbe quella per il servizio per restituire un Prometti e aspetta che il controller attenda questo ma poi non c'è modo per me che todo ci sia?

Così, per esempio nel controller:

ServiceClass service = new My ServiceClass(); 
    Promise<MyObject> promise = service.getMeAnObject(); 
    MyObject myObject = await(promise); 
    render(myObject); 

E la classe di servizio:

public class ServiceClass 
    { 
    ... 
    public Promise<MyObject> getMeAnObject() 
    { 
     String url = "http://..."; 
     // Somehow Build a Promise<MyObject> 
    } 
    ... 
    } 

}

risposta

3

Finora, questo è l'approccio migliore che ho trovato, a meno che non ci sia un problema con la gestione di pool di thread separati all'interno dell'istanza Play.

Ho preso esempio su ciò che Play fa nella classe WSAsync.WSAsyncRequest e sembra funzionare.

In sostanza, l'attesa viene eseguita all'interno del controller, ma la classe di servizio restituisce una Promessa dell'oggetto che mi aspettavo che il servizio restituisse, il che va bene perché non conosco i dettagli di come quell'oggetto è stato recuperato al livello del controller.

Nel controllore:

ServiceClass service = new My ServiceClass(); 
Promise<MyObject> promise = service.getMeAnObject(); 
MyObject myObject = await(promise); 
render(myObject); 

E la classe di servizio:

public class ServiceClass 
{ 
    public Promise<MyObject> getMeAnObject() 
    { 
     String url = "http://..."; 
     return execute(WS.url(url).getAsync()); 
    } 

    private Promise<MyObject> execute(final Promise<HttpResponse> promise) 
    { 
     try 
     { 
      final Promise<MyObject> smartFuture = new Promise<MyObject>(); 
      ScheduledThreadPoolExecutor executor = MyAppThreadPools.getServiceThreadPool(); 

      executor.submit(new Runnable() 
       { 
        @Override 
        public void run() 
        { 
         try 
         { 
          smartFuture.invoke(new MyObject(promise.get())); 
         } 
         catch (Throwable e) 
         {     
          smartFuture.invokeWithException(e); 
         } 
        } 
       }); 

      return smartFuture; 
     } 
     catch (Exception e) 
     { 
      throw new RuntimeException(e); 
     } 
    } 
} 

Quindi questo sembra risolvere il mio problema per ora.

Grazie a tutti.

1

Come lei ha ricordato attendono è accessibile solo da un controller.

Fare in modo che la classe Service estenda Controller, creare un numero di metodi di supporto protetti e utilizzarla come classe padre per i Controller.

In questo modo è possibile condividere l'implementazione tra i controller, pur avendo accesso a tutti i metodi del controller (attendi, esegui il rendering, ecc.).

+3

Grazie per la risposta. Sembra funzionare. Tuttavia, a mio avviso non è la soluzione migliore, in quanto la classe di servizio non è concettualmente un controller. Idealmente, questo meccanismo di interruzione del codice sarebbe disponibile da qualsiasi luogo. –

1

Questo è probabilmente troppo tardi per aiutare voi :) ma il modo per farlo è quello di utilizzare il metodo Promise.onRedeem() per richiamare una nuova promessa che viene restituito e atteso nel controller ...

public Promise<Boolean> deauthorise() { 
    final Promise<Boolean> promise = new Promise<>(); 

    WS.url("https://connect.stripe.com/oauth/deauthorize") 
      .setParameter("client_secret", play.Play.configuration.getProperty("stripe.secretKey")) 
      .setParameter("client_id", play.Play.configuration.getProperty("stripe.clientId")) 
      .setParameter("stripe_user_id", this.stripeUserId) 
      .postAsync() 
      .onRedeem(new Action<Promise<HttpResponse>>() { 
       @Override 
       public void invoke(Promise<HttpResponse> result) { 
        HttpResponse response = result.getOrNull(); 
        promise.invoke(response != null && response.success()); 
       } 
      }); 
    return promise; 
}