Inicialización de Guice en una aplicación web

Si queremos utilizar Guice en una aplicación web tenemos varias maneras de inicializar el entorno. El objetivo final es más o menos el mismo: configurar el filtro de Guice necesario para la integración del elemento esencial, el ServletModule.

La propia documentación del ServletModule nos proporciona algunas alternativas y más o menos proporciona un camino recomendado con el uso del GuiceServletContextListener.

El problema que he encontrado yo con ese listener es la manera de adaptarlo fácilmente al uso de un segundo listener. Porque necesariamente vamos a tener código de inicialización de nuestra aplicación. Una manera sería usar los dos, otra usar sólo el de Guice. Yo he preferido controlar exactamente cuando inicializo Guice así que sólo hay un listener registrado en el sistema, el mio, y de ahí llamo al de Guice.

/**
 * Bootstrap general de l'aplicació.
 */
@WebListener
@Singleton
public class AppContextListener implements ServletContextListener {  
  private static final Logger log = LoggerFactory.getLogger(
          AppContextListener.class);

  @Inject private Provider<DataSource> dataSourceProvider;
  @Inject private ServeiConfig serveiConfig;

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    try {
      ServletContext context = sce.getServletContext();
      configureLogback(context);
      log.info("Inicialitzant aplicació.");
      configuraGuice(sce);
      configuraBaseDeDades();
      context.setAttribute("serveiConfig", serveiConfig);
      log.info("APLICACIÓ INICIADA.");
    } catch (RuntimeException e) {
      log.error("Error inicialitzant aplicació", e);
      throw e;
    }
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    log.info("Aturant l'aplicació.");
    new GuiceBootstrap().contextDestroyed(sce);
    log.info("Aturada aplicació.");
    ((LoggerContext) LoggerFactory.getILoggerFactory()).stop();
  }

  private void configureLogback(ServletContext ctx) {
    // ...
  }

  private void configuraGuice(ServletContextEvent sce) {
    // primer configuro el propi contextlistener de Guice que defineixs els mòduls
    new GuiceBootstrap().contextInitialized(sce);

    // com a cas especial carrego l'injector i m'auto-injecto
    ServletContext ctx = sce.getServletContext();
    Injector injector = 
            (Injector) ctx.getAttribute(Injector.class.getName());
    if (injector == null)
      throw new RuntimeException("Guice Injector not found");
    injector.injectMembers(this);
  }

  private void configuraBaseDeDades() {
    // ...
  }
}

Y la clase GuiceBootstrap es la encargada de inicializar Guice con todos los módulos que utilicemos en nuestra aplicación.

/**
 * Inicialitza Guice i Guice-Servlets.
 * <p>
 * Tot i ser un {@code ServletContextListener} no el defineixo com a tal 
 * ja que l'ordre és important i necessito que s'executi abans que el 
 * listener de l'aplicació.
 */
public class GuiceBootstrap extends GuiceServletContextListener {  
  @Override
  protected Injector getInjector() {
    return Guice.createInjector(
            new AppModule(),
            new WebSocketModule(),
            new DatabaseModule(),
            new ServletModule() {
              @Override
              protected void configureServlets() {
                filter("/*").through(LoginFilter.class);
                serve("/login").with(LoginServlet.class);
                // els servlets migrats del server s'autoinjecten a l'init 
                // de BaseServlet
              }
            });
  }
}