Usando Guice con el cliente REST de Jersey

En un artículo anterior expliqué como integrar Guice con JAX-RS / Jersey como servidor. En esta entrada mostraré como utilizar Guice con el cliente REST de Jersey.

La idea de base es la misma: utilizar el guice-bridge proporcionado por HK2. El mecanismo exacto difiere ligeramente.

Primero definimos una clase que realiza el enlace entre las dos librerias de inyección de dependencia.

import com.google.inject.Injector;  
import org.glassfish.hk2.api.ServiceLocator;  
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;  
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;

import javax.inject.Inject;  
import javax.ws.rs.core.Feature;  
import javax.ws.rs.core.FeatureContext;

public class JerseyClientGuiceInjector implements Feature {  
  private static final Logger log = LoggerFactory.getLogger(
          JerseyClientGuiceInjector.class);

  @com.google.inject.Inject private static Injector injector;
  private ServiceLocator serviceLocator;

  @Inject
  public JerseyClientGuiceInjector(ServiceLocator serviceLocator) {
    this.serviceLocator = serviceLocator;
  }

  @Override
  public boolean configure(FeatureContext context) {
    log.debug("Configurant Guice en client Jersey.");
    GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
    GuiceIntoHK2Bridge guiceBridge = 
            serviceLocator.getService(GuiceIntoHK2Bridge.class);
    if (injector == null)
      throw new RuntimeException("Guice Injector not found");
    guiceBridge.bridgeGuiceInjector(injector);
    log.trace("Configurat Guice en client Jersey.");
    return true;
  }
}

La clase es sencilla pero tiene su dificultad comprender como funciona. Si nos fijamos en el constructor vemos que está anotado con un @Inject y su único parámetro es un ServiceLocator. El ServiceLocator es el equivalente al Injector de Guice. Esta clase la creará el cliente de Jersey y por tanto las dependencias las inyectará HK2 y no Guice.

Por otro lado tenemos el inyector de Guice definido como un atributo estático de la clase. Eso es así para poder romper el círculo ya que no podemos crear una clase que reciba ambos inyectores (el de Guice y el de HK2). El de Guice lo inyectamos de manera estática. Yo lo hago en el AppModule, mi módulo Guice principal de la aplicación.

requestStaticInjection(JerseyClientGuiceInjector.class);  

Una vez tenemos ambos inyectores ya podemos crear el puente entre ambos como se puede ver en el método configure.

Una vez lista la clase JerseyClientGuiceInjector ya podemos utilizarla en nuestros clientes REST.

ClientBuilder.newClient()  
             .register(JerseyClientGuiceInjector.class)
             .register(JerseyClientGson.class)
             .register(ApiRequestFilter.class)
             .target(serveiConfig.string("servidor.api.url"))
             .path("articles")
             .request()
             .put(entitat, ApiResult.class);