2011-12-14 11 views
8

¿Hay alguna manera de usar el mecanismo await() fuera de un controlador?llamadas WS asíncronas y espera() fuera de un controlador

No deseo que el código realice la llamada asíncrona en el controlador, sino que tenga ese código en una clase de servicio que pueda ser reutilizado por múltiples controladores, sin embargo, no hay forma de esperar a la llamada fuera del controlador ya que método está protegido.

Así por ejemplo, en el controlador:

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

Y la clase de servicio:

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

¿Hay una manera de lograr algo por el estilo?

Gracias por su ayuda.


Edición: Me siguió el consejo de Pere e hizo la clase de servicio implementar controlador, funciona sin embargo, es necesario que el controlador de usarla ser mejorada. La única forma en que descubrí que lo hice es invocar al menos el método await() en la clase del controlador llamante.

Sin embargo, todavía no he verificado que el código esté realmente suspendido.


Edición 2: Una sugerencia que tengo del grupo que Google es que realmente debería tratar de hacer la esperan en el controlador, por lo que tal vez una solución mejor sería que el servicio a devolver un Prométeme y haz que el controlador espere esto pero luego no hay forma de que yo todo esté ahí?

Así por ejemplo, en el controlador:

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

Y la clase de servicio:

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

}

Respuesta

3

Hasta ahora, este es el mejor enfoque que he encontrado, a menos que haya un problema con la administración de grupos de hebras independientes dentro de la instancia de Play.

He tomado ejemplo de lo que hace Play en la clase WSAsync.WSAsyncRequest y parece que funciona.

En esencia, la espera se realiza dentro del controlador, pero la clase de servicio devuelve una Promesa del objeto que esperaba que devolviera el servicio, lo cual está bien porque no sé los detalles de cómo se recuperó ese objeto en el nivel de controlador.

En el controlador:

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

Y la clase de servicio:

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

Así que esto parece resolver mi problema por ahora.

Gracias a todos.

1

Como usted ha mencionado esperan sólo es accesible desde un controlador.

Haga que la Clase de servicio extienda el Controlador, cree una cantidad de métodos de soporte protegidos y utilícelo como clase principal para los Controladores.

De esta forma puede compartir la implementación entre controladores, al tiempo que tiene acceso a todos los métodos de controlador (aguardar, renderizar, etc.).

+3

Gracias por su respuesta. Parece que funciona Sin embargo, no es realmente la mejor solución en mi opinión, ya que la clase de servicio no es conceptualmente un controlador. Idealmente, este mecanismo de interrupción del código estaría disponible desde cualquier lugar. –

1

Ésta es probablemente demasiado tarde para ayudarle :) pero la forma de hacerlo es mediante el uso del método Promise.onRedeem() para invocar una nueva promesa que se devuelve y esperaba en el controlador ...

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; 
} 
Cuestiones relacionadas