2012-01-23 24 views
17

Actualmente estoy desarrollando una aplicación en la que necesito administrar el estado de varios servicios y detenerlos/iniciarlos en función de algunos eventos. El problema es que, como se indica en los documentos, el Servicio de Guava es unidireccional, lo que significa que, una vez que se ha detenido, no se puede volver a iniciar.Servicio reiniciable utilizando Guava

Dado que tengo que eludir este problema de alguna manera, me veo frente a un par de alternativas, que me gustaría poner a consideración (sobre todo porque puede haber inconvenientes para cada uno que no conozco bien ahora).

La primera solución obvia a este problema es crear una instancia de un nuevo servicio cuando necesito "reiniciarlo". Esto funciona, pero en mi arquitectura actual complicaría un poco las cosas: actualmente estoy instalando todos los servicios, y basados ​​en eventos de un EventBus, comenzando o deteniéndolos si es necesario. La clase que llama a los métodos de inicio y detención solo guarda una referencia a un Mapa de servicios y llama al método correcto en esas instancias en función del Evento recibido. Si necesito instanciar un nuevo objeto en respuesta a un Evento, tendré que renunciar a parte del desacoplamiento que tengo actualmente (posiblemente manteniendo la clase de cada tipo de Servicio e invocando al constructor usando el reflejo).

Otra posibilidad es implementar la interfaz de servicio como un servicio redireccionado Restartable (o algo similar a esto). Si tomo esta ruta, mi método start() podría crear otro subproceso como si fuera la primera vez y restablecer los estados.

¿Hay alguna clara desventaja para el segundo enfoque? Me temo que me podría estar perdiendo algún inconveniente obvio aquí (además de tener que codificar algo un poco más complicado), especialmente en lo que respecta a la gestión de subprocesos.

Respuesta

3

Recomendaría su primer enfoque, pero hay mejores formas de hacerlo que la reflexión. Usar la inyección de dependencia, o posiblemente pasar alrededor de los objetos Supplier<Service> en lugar de usar serviceClass.newInstance(), es probablemente el camino a seguir aquí.

+0

Gracias por su respuesta, este parece ser el camino a seguir. Si tomo el camino DI, veré mejor los alcances de Guice según lo sugerido por Craig, pero los proveedores parecen prometedores. Desafortunadamente, solo volveré a poner mis manos en esta tarea en un par de días, actualizaré mi pregunta con más detalles/opciones. – pcalcao

+0

Tuve un caso de uso similar, y adopté un enfoque híbrido. Utilicé un caché de elemento único en lugar de un proveedor, ya que me permitió borrar/invalidar la entrada cuando se detuvo. Pero puse mi caché en una clase que implementó el Servicio (y la mayoría solo delegó en el servicio en caché) solo para poder tener la API. – Ray

1

Considere usar los ámbitos de Guice.

1

El mismo problema se realiza un seguimiento en este github: https://github.com/google/guava/issues/418

Tengo un cambio propuesto aquí: https://github.com/okigan/guava/commit/8f51b155f9ce5c60236b9a9bfdc6ca5f8bf5e51d

el quid de la cuestión es añadir un reset() para AbstractService que permite la transición de TERMINATED vuelta a NUEVO:

public final void reset() { 
    lock.lock(); 
    try { 
     switch (snapshot.state) { 
      case TERMINATED: 
      case FAILED: 
       snapshot = new StateSnapshot(State.NEW); 
       break; 
      default: 
       throw new AssertionError("Unexpected state: " + snapshot.state); 
     } 
    } catch (Throwable resetFailure) { 
     notifyFailed(resetFailure); 
    } finally { 
     lock.unlock(); 
     executeListeners(); 
    } 
}