¡¡¡Juira, Pico!!!

Sunday November 26th 2006, 9:24 pm
Filed under: Java, Spring

En el post anterior contaba que había reemplazado Picocontainer en mi aplicación con Spring (de paso lean este post que me causó bastante gracia por lo oportuno).

La verdad es que esperé demasiado para volar a Pico, debería haberlo hecho antes. Pero creo que arrugué porque es un cambio grande, y estaba teniendo demasiada carga de trabajo como para dedicar tiempo en algo que no iba a resultar visible, ya que desde el punto de vista del usuario, nada cambia.
Lo que finalmente me llevó a decidir el cambio, fue lo siguiente: necesitaba usar Quartz Scheduler para programar la generación automática de unos reportes. Como debia quedar programado continuamente, necesitaba que los jobs del scheduler fueran persistentes. Quartz ofrece un jobstore que usa JDBC, pero el problema es que usa su propia conexión a la BD, con su propio pool de conexiones, en este caso DBCP, mientras que yo vengo usando c3p0 desde hace tiempo (ya que Hibernate 3 no soporta más DBCP). Entonces, si lo hubiera usado de esa manera, me hubiera quedado la aplicación con dos connection pools independientes, y encima diferentes. Inaceptable.
Habia dos soluciones: escribir mi propia implementación de JobStore, o aprovechar que Spring ya lo hizo. Y como si hay algo que no me gusta es reinventar la rueda, pues… 

 

Como cualquier tarea complicada, es mejor separarla en partes mas manejables. Entonces, mi primer objetivo fue reemplazar a Pico solamente en la función de Dependency Injection, dejando para más adelante otras integraciones que ofrece Spring. Sin embargo, debido a que Pico estaba integrado con Struts, proveyendo DI a las actions de Struts, de alguna manera tenia que usar la integración de Spring con Struts. Para eso tenía dos opciones:

Usar ContextLoaderPlugin, un plugin de Struts que hace que los actions se manejen como beans de Spring, y que las dependencias se inyecten automáticamente, pero que tiene la enorme desventaja de tener que duplicar la definición de beans: además de definir las actions en struts-config.xml, tambien lo tengo que hacer en action-servlet.xml, para definirles las dependencias. Encima, yo usé wildcards en la definición de las actions para hacer mas chico el archivo, y esto no esta soportado de ninguna manera, lo que implicaría además, reescribir el struts-config.xml para quitar los wildcards. Ni en pedo. Antes dije que la integración de Pico con Struts era un poco mejor que la de Spring, y es justamente por esto, con agregar un servlet filter, Pico inyecta las dependencias directamente en las actions usando autowire, no hay que hacer más nada.

Usar ActionSupport, una clase de la que hay que hacer heredar todas las actions. Esta clase provée un método para obtener el application context, y de ahí los beans necesarios, pero el caso es que al final, la inyección de dependencias la tengo que terminar haciendo yo… tampoco me convence.

Sin embargo, la clave estaba en este última clase, ActionSupport. Si ActionSupport sabe como obtener un appicationContext, debería haber alguna manera de inyectar las dependencias automáticamente, usando autowiring.
En el libro de Rod Johnson, muestran una manera de hacerlo:

 

public class AutowiringActionSupport extends ActionSupport {

  protected void onInit() {
    getWebApplicationContext().getBeanFactory().autowireByType(this,
      AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
  }
}

ActionSupport tiene un método onInit() que se llama desde el constructor. Por lo tanto, si todas las action heredan de esta clase, cada vez que se instancie una de ellas, se ejecuta este código y se inyectan las dependecias. Perfecto, justo lo que necesitaba!. Sin embargo, se vé que este código fue escrito hace mucho, ya que no funciona en Spring 1.2.8 ni en 2.x . El método getBeanFactory() no existe (fue reemplazado por getAutowireCapableBeanFactory()), y autowireByType() tampoco, fue reemplazado por dos métodos, autowire() y autowireBeanProperties(). El primero no me sirve porque devuelve una nueva instancia del bean (y recuerden que yo estoy dentro del bean), y el segundo no permite hacer autowire por tipo, menos aún inyección en el constructor (ya que el bean ya esta instanciado). Pero lo que sí se puede hacer es esto:

 

protected void onInit() {
    getWebApplicationContext().getAutowireCapableBeanFactory().
        autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);
}

Esto significa que estoy inyectando las dependencias en el objeto actual, que las voy a inyectar según el nombre (Spring busca properties en el bean que tengan el mismo nombre que el bean id), y el false del final significa que no voy a verificar si queda alguna alguna propiedad del bean sin setear.

En síntesis, para que esto funcione, tuve que hacer los siguientes pasos:

  • Hacer que mi action padre herede de ActionSupport.
  • Sobreescribir el método onInit() en mi clase padre.
  • Cambiar el nombre de algunas properties de las actions, para que coincidan exáctamente con los id de los beans.
  • Agregar los setters que me faltaban (recuerden que antes usaba exclusivamente constructor injection).

Esto fue relativamente fácil gracias a las opciones de refactoring y creación de código de Eclipse.
El resto fue simplemente terminar de armar el applicationContext.xml como para cualquier aplicación Spring, para lo que simplemente modifiqué los archivos xml de Pico.

Ahora ya tengo la aplicación con Spring en producción y andando sin problemas, algo de lo que estoy muy contento. El próximo paso será  aprovechar las integración con Hibernate, para lo que pienso implementar un DAO genérico, como se explica en este artículo.


| show comment »

Pico vs. Spring

Saturday November 18th 2006, 1:29 pm
Filed under: Java, Spring

Hace mucho que quería escribir esto, pero por una razón u otra lo fui posponiendo, pero ahora que finalmente reemplacé Pico por Spring en la aplicación, creo que es un buen momento.

El proyecto en el que vengo trabajando hace mas de un año, usaba originalmente Picocontainer como container de dependency injection (DI). La razón, sin entrar mucho en detalles, fue que la persona que actuaba como arquitecto en ese momento, es uno de los commiters del proyecto Pico, y por lo tanto lo impuso a pesar que el resto del equipo se inclinaba por Spring, con el que ya teníamos experiencia previa (como comentario al margen, tampoco nos dejó usar Java 5…).

En primer lugar, aclaremos que Pico cumple su cometido impecablemente. Provee DI a una aplicación web sin problemas,  provee  constructor  injection o  setter injection (aunque esta última vaya uno a saber como se hace). Para  poder externalizar  la configuración a un XML externo, o bien a un script Groovy o Jyton, hay que usar Nanocontainer, otro paquete de los mismos autores.  Hasta aquí todo muy bien, pero la pregunta es, por qué un proyecto debería usar Pico en lugar de Spring? Qué tiene mejor Pico que Spring, o que ofrece Pico que no pueda ofrecer Spring?

Veamos un poco en detalle:

Pico es muy “liviano”.

Es cierto, el tamaño de los .jar de Pico + Nano + Nanowar apenas pasa los 300Kb, mientras que el paquete completo de Spring son mas de 2Mb. Sin embargo, Spring también se distribuye en varios .jar separados y uno puede elegir deployar solo lo que uno va a usar. Pero en el peor de los casos, cuál es el problema para una aplicación que corre siempre en un mismo server? Quizá el deployment que hago una vez por semana tarde 20 seg. mas.

Pico provee constructor injection.
Si bien la gente de Spring promueve setter injection, a diferencia de la de Pico que promueve constructor injection (no me voy a explayar en esto, cada uno tiene sus méritos), no significa que que Spring no soporte constructor injection. De hecho, lo soporta mejor que Pico, paso a explicar:
Por default, Pico trabaja con autowire, lo que significa que automágicamente descubre que beans tiene que inyectar según el tipo de los beans y los tipos definidos en el constructor. Pico enfatiza mucho este aspecto, porque según ellos ayuda a mantener el XML de configuración en un tamaño reducido. Por ejemplo, si tenemos la siguiente clase:

package com.sarcobjects.persistence.hibernate;

public class HibernateGamePersister     extends HibernatePersisterSupport     implements GamePersister, Serializable {

    public HibernateGamePersister(SessionFactory factory) {
        super(factory);
    }
    …
}

El constructor necesita una instancia de la clase SessionFactory. Entonces, este fragmento de XML es suficiente para configurarlo:

<component-implementation class=’com.sarcobjects.persistence.hibernate.SessionFactory’ />
<component-implementation class=’com.sarcobjects.persistence.hibernate.HibernateGamePersister’/>

Pico descubre que el tipo que necesita inyectar es SessionFactory, lo busca en el XML y si lo encuentra lo inyecta.
El equivalente en Spring sería:

<bean id=”gamePersister” class=’com.sarcobjects.persistence.hibernate.HibernateGamePersister’ autowire=”constructor”/>

La diferencia es que le tengo que agregar autowire=”constructor” porque para Spring el default es setter injection.
Hasta acá todo muy lindo, pero el problema aparece cuando le tengo que pasar además, por ejemplo,  una constante, o bien el archivo de configuración contiene mas de una definición del mismo tipo.  Ahí ya pierdo todo lo automágico y no me queda mas remedio que explícitamente definir que quiero inyectar. Por ejemplo:

    <component-implementation class=”com.sarcobjects.web.admin.dashboard.QuartzDashboardScheduler”>
        <parameter key=”service” />
        <parameter key=”factory” />
        <parameter key=”gameStatus” />
        <parameter>
            <int>10</int>
        </parameter>
    </component-implementation>

Aquí necesito pasarle un valor directamente desde la configuración, pero si le paso solamente el parámetro int, Pico no sabe de donde sacar los otros parámetros, y por lo tanto tengo que definirlos explícitamente. La misma configuración en Spring sería:

<bean class=”com.sarcobjects.web.admin.dashboard.QuartzDashboardScheduler”     autowire=”constructor”>
    <constructor-arg index=”3″ value=”6″ />
</bean>

Sólo tengo que incluir el parámetro que Spring no puede deducir. No es que Spring sea mas inteligente, pero la configuración nos permite mezclar parámetros implícitos con explícitos, con el objeto de mantener el tamaño del XML al mínimo. 

Pico soporta diferentes scopes para los beans: application, session y request.
Esto era cierto hace un año, pero ahora Spring 2 también soporta session y request, además del clásico application. Sin embargo, en esta aplicación jamás usamos otros scope que no fuera application.

Pico se integra con Struts y WebWorks, con Hibernate y tiene soporte para AOP.
La integración con Struts esta bien, hasta quizá sea levemente superior a la de Spring. La integración con Hibernate no pasa de soportar un SessionFactory, y la de AOP no tengo idea, quiza si hubiera documentación…
Spring obviamente se integra con estos y muchos mas Frameworks, y de una manera mucho mas completa.

 

En síntesis, Spring puede hacer todo lo que Pico hace. Sin embargo, si hacemos la pregunta al revés, puede hacer Pico todo lo que Spring hace?
Definitivamente, no. Para empezar, la documentación. Nosotros usamos Pico en un nivel aceptable porque teníamos a uno de los autores en el equipo. Pero no veo cómo alguien que quiera usar Pico pueda sacarlo andando razonablemente con la documentación que se ofrece en el sitio. En contraposición, Spring tiene una muy buena documentación, varios libros, y miles de desarrolladores en todo el mundo que nos pueden ayudar.
No voy a enumerar todas las facilidades que además aporta Spring (se pueden encontrar por toda la web) para desarrollar una aplicación, pero por ejemplo, el soporte para demarcación declarativa de transacciones me parece buenísimo. De hecho, la arquitectura impuesta por este arquitecto no permite tener transacciones que abarquen mas de una tabla! O sea que básicamente nuestra aplicación no es transaccional.

En fin, cuándo se debería usar Pico? Quizá en aplicaciones chicas, puntuales, que no necesiten integrarse con demasiados otros frameworks, que necesite ser distribuida a muchos puntos.
Pero en aplicaciones mediana a grandes, transaccionales, con integración a otros frameworks, con acceso a web services, y que pueda escalar., no me cabe duda que Spring es una solución muy recomedable.

En el próximo post voy a contar lo que tuve que hacer para volar Pico e  instalar Spring en la aplicación.

Update 26/11: Olvidé comentar que el SpringIDE para Eclipse, facilita todo aún mas al manejar los archivos .xml de Spring, con el autocompletado de elementos, y nombres de clases. Permite clickear en un bean y abrir la clase respectiva en el editor. Nos avisa si hay una referencia a un bean que no existe, o si falta algun setter o constructor para poder inyectar todas las dependencias. Y como si esto fuera poco, además nos genera un gráfico con el mapa de los beans, aunque claro, esto solo funciona cuando las dependencias estan marcadas explícitamente, y no con autowire (aunque podría, supongo que una próxima versión lo incluira).


| show comment »