Usando guice con java websockets (JSR-356)

La especificación de websockets de Java (JSR-356) soporta, como es evidente, el uso de inyección de dependencias cuando se ejecuta en un entorno JavaEE mediante CDI.

El servidor Tomcat a partir de la versión 7 implementa soporte para esta tecnologia. Pero dado que no es un servidor JavaEE completo no incorpora soporte para CDI.

Si queremos usar una alternativa como Guice para inyección de dependencias tan sólo tenemos que ajustar el mecanismo de creación de las instancias de los endpoints.

public class WSConfigurator extends ServerEndpointConfig.Configurator {  
  @Inject
  private static Injector injector;

  @Override
  public <T> T getEndpointInstance(Class<T> endpointClass)
          throws InstantiationException
  {
    return injector.getInstance(endpointClass);
  }
}

Mediante este WSConfigurator todas las instancias de nuestros puntos de entrada de websockets se crearan inyectando las dependencias utilizando Guice.

Pero hay que acordarse de utilizarlo en cada definición de un servicio websocket. Un ejemplo de una clase que he utilizado en un proyecto reciente:

@ServerEndpoint(
        value = "/ws/sync",
        encoders = JsonEncoder.class,
        decoders = JsonDecoder.class,
        configurator = WSConfigurator.class)
public class WSSync extends AsyncWebSocketServer {  
  private static final Logger log = LoggerFactory.getLogger(WSSync.class);

  private EventBus eventBus;
  private Sincronitzador sincronitzador;

  @Inject
  public WSSync(EventBus eventBus, Sincronitzador sincronitzador) {
    this.eventBus = eventBus;
    this.sincronitzador = sincronitzador;
  }

...

Por último tenemos que cerrar el círculo inyectando el inyector en la clase WSConfigurator. Esta es la parte sucia del invento pero es un patrón que suele aparecer cuando hay que añadir soporte de Guice en una libreria de terceros.

En mi caso utilizo un módulo específico para los websockets.

import com.google.inject.AbstractModule;

public class WebSocketModule extends AbstractModule {  
  @Override
  protected void configure() {
    requestStaticInjection(WSConfigurator.class);
    requestStaticInjection(JsonEncoder.class);
  }
}