Spring Framework - InitializingBean

InitializingBean

Esta interfaz de Spring Framework te permite una vez configurado el contexto, ejecutar el método afterPropertiesSet(), y como su nombre lo dice, lo ejecuta luego de ejecutar los métodos setter del bean (post configurar el bean).

1
2
3
4
5
public interface InitializingBean {

void afterPropertiesSet() throws Exception;

}

Algunas aplicaciones de esta interfaz:

  • Validar que los atributos de un Bean estén correctamente inicializados.
  • A partir de los atributos ya setteados, generar otra propiedad en tiempo de configuración. Por ejemplo, se configura el bean con dos atributos de tipo int y en tiempo de configuración quiero calcular la suma de ambos y dejarlo en otra propiedad. ¿ Qué gano con esto ? que esa propiedad no se calcula cada vez que hago el getter sino que ya esta previamente calculada.

Para el primer caso, recomiendo utilizar la clase Assert ya que provee métodos con los que puedes invalidar que el contexto Spring levante si hay errores en la configuración.

Les dejo un código de ejemplo para que le echen un vistazo (le agregué la interfaz al ejemplo del post anterior)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ExampleBean implements InitializingBean {

private Integer _randomValue;

private Integer _newValue;

public final Integer getRandomValue() {
return _randomValue;
}

public final Integer getNewValue() {
return _newValue;
}

public final void setRandomValue(Integer randomValue) {
_randomValue = randomValue;
}

public void afterPropertiesSet() throws Exception {
Assert.notNull(_randomValue, "randomValue properties cannot be null");
_newValue = _randomValue * 10;
}

}

Voy a explicar un poco y a groso modo el como funciona esta clase desde el punto de vista de Spring:

  • Se lee la configuración asociada a la clase ExampleBean
  • Se crea la instancia de la clase ExampleBean
  • Luego vía inyección se le settean los atributos, entre ellos randomValue
  • Luego de la creación del bean ExampleBean se ejecuta el método afterPropertiesSet(), en donde se realiza una validación del randomValue (este NO puede ser nulo, de serlo, el contexto de Spring no quedara operativo), si eso pasa OK, se calculará el atributo newValue y que será randomValue multipliado por 10.
  • Finalmente y si no hay errores en el contexto de Spring, se deja la instancia del bean ExampleBean en el contexto, es decir, el valor randomValue y newValue quedan listos para ser usados.

Este proceso se realiza sólo una vez (salvo que el scope no sea singleton) y es en el momento de la creación del contexto.

Demás esta decir que esta clase/bean (ExampleBean) debe estar configurada en el archivo del contexto de Spring o vía las anotaciones (metadata) provistas para eso.

¿Por que FactoryBean es útil?

En este post voy a tratar de explicar porque FactoryBean es increíblemente útil para alambrar (aplicar DI) aplicaciones hechas con Spring Framework.

La interfaz de FactoryBean dice lo siguiente:

1
2
3
4
5
6
7
8
public interface FactoryBean<T> {

T getObject() throws Exception;

Class getObjectType();

boolean isSingleton();
}

La utilidad de la implementación de esta interfaz y su posterior declaración como Bean dentro del contexto de la aplicación, es que puede ser usada (como su nombre lo indica) como Factory para inyectar algún valor en alguna propiedad de otro Bean.

¿Dónde y cómo funciona? Dentro del ciclo de vida de la carga del Contexto de spring, la ejecución de la implementación de esta interfaz, esta justo antes de hacer los setters de las propiedades de un Bean. El funcionamiento es simple, siempre se ejecutará el método getObject() que retornará la instancia del tipo T.

Veamos una implementación simple y una configuración de contexto de spring:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RandomNumberFactoryBean implements FactoryBean<integer> {

public Integer getObject() throws Exception {
return Integer.valueOf(new Random().nextInt());
}

public Class getObjectType() {
return Integer.class;
}

public boolean isSingleton() {
return true;
}

}

Esta simple implementación de FactoryBean retorna un Integer en el método getObject(), simplemente eso (obviamente en este método debes poner todo tu talento para resolver el valor que andas buscando, esto es un ejemplo simple).

El método getObjectType() es usado internamente por el framework para validar que el retorno de getObject() sea correcto o que este dentro de la jerarquía de clases, esto es en caso de que quieras restringir por tipo.

El último método isSingleton() es si quieres que esta clase (no importa la cantidad de declaraciones que tengas, siempre será la misma), es un tanto dificil de entender pero les sugiero que lean sobre el concepto de Singleton que tiene SpringFramework. En el ejemplo de configuración espero que les quede claro, de lo contrario, bienvenidas las preguntas.

Esta es la definición del Bean de ejemplo, en donde se hará uso del FactoryBean:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ExampleBean {

private Integer _randomValue;

public final Integer getRandomValue() {
return _randomValue;
}

public final void setRandomValue(Integer randomValue) {
_randomValue = randomValue;
}

}

Aquí las configuraciones de contexto de spring.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemalocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="exampleBean1" class="cl.pcollaog.factory.ExampleBean">
<property name="randomValue">
<bean class="cl.pcollaog.factory.RandomNumberFactoryBean" />
</property>
</bean>

<bean id="randomNumberFactoryBean" class="cl.pcollaog.factory.RandomNumberFactoryBean"/>

<bean id="exampleBean21" class="cl.pcollaog.factory.ExampleBean">
<property ref="randomNumberFactoryBean" name="randomValue"></property>
</bean>

<bean id="exampleBean22" class="cl.pcollaog.factory.ExampleBean">
<property ref="randomNumberFactoryBean" name="randomValue"></property>
</bean>

</beans>

Ejemplo 1:

Se declara el Bean exampleBean1 y que tiene como propiedad el atributo randomValue y cuyo valor será producto de la ejecución del Bean RandomNumberFactoryBean en el momento que el contexto de Spring es cargado. En este caso se utiliza la técnica de InnerBean, es decir, no se tiene una instancia del Bean para ser reutilizada sino que esta en la misma declaración del setter del atributo.

Ejemplo 2:

Este ejemplo hace lo mismo indicado arriba, sólo que no se aplica InnerBean sino que se extrae y es reutilizado en el bean exampleBean21 y exampleBean22. En este ejemplo entra en juego el método isSingleton(), si es true, quiere decir que el valor que entregará el FactoryBean siempre será el mismo para ambos Beans. Ahora bien, si se cambia a false, por cada setter, es decir, para exampleBean21 y exampleBean22 se realizara la ejecución de RandomNumberFactoryBean 2 veces. (En este caso, nos entregará dos valores aleatorios para cada bean)

En pocas palabras para cada Bean del ejemplo 2 habrá dos instancias de RandomNumberFactoryBean que entregará cada una un Integer para los atributos de los beans declarados.

Algo interesante de las implementaciones de FactoryBean es que luego de la carga del contexto, estas instancias son desechadas.

Espero les sea útil y cualquier pregunta sera bienvenida.

Algunas APIs Java que te pueden servir: commons-io

commons-io trae un montón de clases que te pueden servir al momento de utilizar lecturas y escrituras de archivos, entre otras cosillas.

Por si no lo sabías una de las malas practicas mas recurrentes programando con java es, no cerrar los Stream y ciertos Readers adecuadamente, o te complicas mucho con los try/catch, vamos a ver un par de ejemplos clásicos:

Primero agregamos la dependencia de maven, ojo que las versiones 2.x están escritas para Java 1.5 y las 1.x para java 1.3 y 1.4:

1
2
3
4
5
<dependency>
<groupid>commons-io</groupid>
<artifactid>commons-io</artifactid>
<version>2.0.1</version>
</dependency>

Caso 1: finally que asegura cerrar el Reader con el método close()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void closeWithFinally(String filename) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filename));
try {
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String contentFile = sb.toString();
System.out.println(contentFile);
} finally {
// Cerrar el reader
reader.close();
}
}

Caso 2: Creando la instancia de BufferedReader dentro del try y validando si es null al momento de cerrar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void closeWithFinallyNullCheck(String filename) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filename));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String contentFile = sb.toString();
System.out.println(contentFile);
} finally {
// Cerrar el reader
if (null != reader) {
reader.close();
}
}
}

Caso 3: Usando commons-io y la clase IOUtils se reduce bastante el código y quedaría algo así:

1
2
3
4
5
6
7
8
9
public void closeWithFinallyCommonsIO(String filename) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filename));
try {
String contentFile = IOUtils.toString(reader);
System.out.println(contentFile);
} finally {
IOUtils.closeQuietly(reader);
}
}

Mas información en el maven site del proyecto:

Mas adelante escribiré un par de ejemplos con otras clases de commons-io que son bastante útiles.

Cómo iterar un Map con Java 1.5

A pedido del Sr: @perrefe les dejo un tip de performance para iterar sobre Map<K,V>:

Supongamos que nuestro Map<K,V> tiene como key un String y como value un Integer, declarado de esta forma:

1
Map<String,Integer> map = new HashMap<String,Integer>();

Ahora podemos iterar el Map de la siguiente forma:

1
2
3
4
for (Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key [" + entry.getKey() + "]");
System.out.println("Value [" + entry.getValue() + "]");
}

Si ve por ahí algún iterador sobre un Map<K,V> al cual le sacan primero la lista de keys y después iteran el Map para sacar el value, no pierda tiempo escribiendo código de más y haciendo que su aplicación ande mas lenta.

Por favor no haga esto!!!

1
2
3
4
5
6
7
Set<string> keys = map.keySet();

for (String key : keys) {
Integer value = map.get(key);
System.out.println("Key [" + key + "]");
System.out.println("Value [" + value + "]");
}

Espero les sirva.

Usando Apache Velocity

¿Que es Velocity?

Velocity es un proyecto de Apache para el desarrollo de Templates para la capa de vista dentro del modelo MVC, siendo esta última la más conocida de todas las formas de uso de Velocity. Pero es mucho más que eso, podemos usar el motor de Velocity para lo que necesitemos utilizando siempre el concepto de uso de templates.

¿Que es un template?

Como su nombre lo dice es una plantilla, les dejo un ejemplo de como seria una plantilla Velocity para una vista en html:

1
2
3
4
5
6
7
#set ($hola = "Hola Mundo")
<h1>$hola</h1>

## Esto es una macro de Velocity
#d()

<h2>$salida</h2>

Donde:

  • #set es una directiva para settear valores
  • $hola es la variable setteada con el valor Hola Mundo
  • #d() es una Velocimacro
  • $salida es una variable de contexto que sera setteada desde la ejecución de Velocity (ver código fuente de java)

¿Cuándo podemos utilizar Templates?

Hay un sin número de aplicaciones, como por ejemplo:

  • Envío de mails personalizados, en este punto uno podría escribir los templates (email tipo) y dejar ciertos tags de Velocity (llamados directivas) para la personalización.
  • Generar XML’s dinamicos.
  • Generar otro tipo de salidas como por ejemplo JSON.
  • Generar PDF mediante docbook, incluso otro tipo de archivos (rdf, txt)
  • Y el más usado, escribir templates para ser usados como html en la capa de vista.

Usando Velocity Engine

Primero creamos un proyecto con Maven2 para hacer la vida mas fácil y agregamos las siguientes dependencias:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupid>org.apache.velocity</groupid>
<artifactid>velocity</artifactid>
<version>1.6.2</version>
</dependency>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.5</version>
</dependency>
<dependency>
<groupid>log4j</groupid>
<artifactid>log4j</artifactid>
<version>1.2.14</version>
</dependency>
</dependencies>

Luego creamos la clase que hará el trabajo de llamar a VelocityEngine, la carga del template y finalmente la salida.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package cl.firefox.pcollaog.velocity;

import java.io.StringWriter;
import java.io.Writer;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;

/**
* <p>
* </p>
* <p>
* $Id: VelocityEngine.java,v 1.1 2009-04-20 00:21:20 pcollaog Exp $
* </p>
*
* @author pcollaog
* @version $Revision: 1.1 $
*/
public class VelocityEngine {

Logger logger = Logger.getLogger(VelocityEngine.class);

private VelocityContext _ctx;

private void init() throws Exception {
logger.info("Inicializando Velocity");
Properties prop = new Properties();

// Carga archivo de propiedades de velocity
prop.load(getClass()
.getResourceAsStream("/velocity.properties"));

// Inicializa Velocity
Velocity.init(prop);

_ctx = new VelocityContext();
}

public String mergeTemplate() throws ResourceNotFoundException,
ParseErrorException, Exception {

init();

// Carga el template
Template tpl = Velocity.getTemplate("template.vm");

// Agrega una variable al contexto luego puede ser usada
// como $salida
_ctx.put("salida", "OK");

// Creo un Writer para almacenar la salida
Writer writer = new StringWriter();

// Genero la mezcla del template con sus las variables
// del contexto y macros de velocity
tpl.merge(_ctx, writer);

// Finalmente genero un string con la salida
return writer.toString();
}
}

Espero que el código se explique por si solo.

Aquí dejo el ejemplo completo hecho con Maven2, para ver la ejecución, dentro del código fuente va un Test Unitario que imprime la salida de los templates.

1
$ mvn clean compile test

Baja el código fuente aquí:

Mas Información en:

Maven - Parte II (Creando Aplicaciones)

Para crear una aplicación Java Simple con Maven debes ejecutar en tu consola:

1
$ mvn archetype:create -DgroupId=com.firefox -DartifactId=validate

Donde:

  • groupId: Es el package de referencia de tu aplicación.
  • artifactId: Es el nombre de tu aplicación (en maven se le llama artefacto)

Maven creará un directorio con el nombre del artefacto (aplicación), en este caso validate y la estructura de directorio quedará de la siguiente forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- firefox
| `-- App.java
`-- test
`-- java
`-- com
`-- firefox
`-- AppTest.java

Con esto queda lista la estructura básica de tu aplicación Java. En la estructura de directorios se pueden fijar que existen dos source folder el main y el de test. Además existe un archivo llamado pom.xml que nos permitirá administrar nuestro proyecto java, gestionar las dependencias del proyecto, generar el sitio web del proyecto al estilo maven o simplemente tareas simples como compilar, empaquetar o correr test unitarios.  Estas acciones o tareas son llamadas goals.

Algunas tareas simples (goals)

1
$ mvn compile

Esta acción lo que hace es compilar el proyecto dejando los binarios en el directorio target del proyecto de la siguiente forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
|-- pom.xml
|-- src
| |-- main
| | `-- java
| | `-- com
| | `-- firefox
| | `-- App.java
| `-- test
| `-- java
| `-- com
| `-- firefox
| `-- AppTest.java
`-- target
`-- classes
`-- com
`-- firefox
`-- App.class
1
$ mvn clean

Limpia el proyecto, es decir, eliminar los compilados. Esta acción eliminará todas las clases compiladas ubicadas en el directorio target dentro del proyecto, este directorio es similar a los directorios build o bin que genera eclipse o netbean.

1
$ mvn package

Como su nombre lo dice, esta acción empaqueta la aplicación y deja el paquete en el directorio target.

1
$ mvn test

Esta acción corre los test unitarios guardados en el source folder de los tests.

Para que los test puedan correr, estos deben extender de TestCase de JUnit. Además los nombres de las clases deben seguir un estándar para su ejecución.

  1. Los nombres de las clases del tipo test deben terminar en Test, por ejemplo: ClientTest
  2. Dentro de la clase ClientTest deben existir métodos que hagan los test. Estos métodos deben ser public y ademas empezar con la palabra test.
  3. Dentro de los métodos puedes utilizar los assert para verificar la correcta ejecución del test unitario. Los assert son como su palabra lo dice aciertos, el método te pide como argumentos lo que tu esperas y lo que finalmente te devuelve la ejecucion del test. Si ambos resutados coinciden el test se ejecuta con resultado exitoso. También está la posibilidad de que los resultados sean distintos y en realidad es lo que tu esperas (que sean distintos) para que el test sea exitoso. Un pequeño ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import junit.framework.TestCase;

/**
* @author pcollaog
*
*/
public class MathTest extends TestCase {

public void testSumaBuena() {
int a = 1;
int b = 2;

int resultado = a + b;

int resultadoEsperado = 3;
assertEquals(resultado, resultadoEsperado);
}
}

Este ejemplo sale con un assert en true, es decir, se ejecuta con resultado exitoso. Es lo que se espera del test.

1
$mvn site

Esta acción te permite generar un sitio web estático con información referente al proyecto que estas realizando. La información que necesita para poder crear el sitio es referente al archivo **pom.xml **(más adelante hablaré de este archivo).

El sitio generado lo dejará en un directorio llamado site dentro del **target **, donde encontrarás un index.html que podras ver con tu navegador favorito (espero que sea firefox).

1
$mvn install

Esta acción compila, empaqueta e instala tu proyecto en un repositorio local de maven (generalmente se encuentra en ~/.m2/repository) . De esta forma puedes utilizar ese proyecto dentro de otro agregándolo como dependencia en el archivo pom.xml. Hay que tener como consideración que los proyectos instalados deben ser bien versionados.

1
$mvn eclipse:eclipse

Con esta acción puedes agregar los archivos necesarios para que puedas convertir un proyecto maven en un proyecto maven+eclipse. En lo posible ojalá puedas instalar el plugin de maven para eclipse.

Existen un montón de acciones (goals) más que funcionan con plugins de maven y que complementan algunas otras tareas.

Como verán maven hace muchas tareas tediosas en simples pasos que además te ayudan a optimizar tiempos de desarrollo, integraciones rápidas, actualización de librerías y documentación de tu proyecto.

Maven - Parte I (Instalación)

En esta serie de artículos pretendo explicar el funcionamiento a grandes rasgos de Maven.

Primero… ¿ qué es Maven y para qué sirve ?

Maven es una herramienta para la gestión de proyectos java desde el lado del desarrollador, es decir, un automatizador de tareas al estilo de ant task. Más adelante veremos las cosas que puede hacer con mas detalle, pero entre las actividades diarias que hace de un desarrollador java están:

  • compilar
  • correr test unitarios
  • empaquetar
  • levantar webserver o application server
  • manejo de dependencias del proyecto

Instalando Maven

Primero debes descargar el binario de maven desde su pagina oficial.

  1. Debes descomprimir el archivo y guardarlo en un directorio conocido.
  2. Luego debes agregar al PATH el directorio bin para que puedas ejecutar maven.
1
2
3
4
5
6
7
8
9
10
11
12
13
$ tar zxvf maven-2.0.7-bin.tar.gz
maven-2.0.7/conf/
maven-2.0.7/boot/classworlds-1.1.jar
maven-2.0.7/LICENSE.txt
maven-2.0.7/NOTICE.txt
maven-2.0.7/README.txt
maven-2.0.7/bin/m2.bat
maven-2.0.7/bin/m2.conf
maven-2.0.7/bin/mvn.bat
maven-2.0.7/bin/m2
maven-2.0.7/bin/mvn
maven-2.0.7/conf/settings.xml
maven-2.0.7/lib/maven-core-2.0.7-uber.jar

Luego agregar al PATH el directorio bin:

1
$ export PATH=$PATH:/home/usuario/maven/bin

Debemos comprobar la versión de java que estamos utilizando en el sistema:

1
2
3
4
$ java -version
java version "1.5.0_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05)
Java HotSpot(TM) Client VM (build 1.5.0_13-b05, mixed mode, sharing)

Ahora podemos comprobar que maven esta bien instalado:

1
2
3
4
$ mvn -v
Maven version: 2.0.7
Java version: 1.5.0_13
OS name: "linux" version: "2.6.22-14-generic" arch: "i386"

Resolución de algunos problemas

A veces es muy recomendable agregar en el script que ejecuta maven el JAVA_HOME, asi maven no se confunde de java si es que tienen varias JVM instaladas.

Editar el siguiente archivo /home/usuario/maven/bin/mvn:

1
2
3
4
5
6
7
8
9
# ------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#

export JAVA_HOME=/usr/lib/jvm/java-1.5.0-sun

También puedes configurar tu JVM por defecto de la siguiente manera en ubuntu/debian:

1
2
3
4
5
6
7
8
# update-alternatives --config java
Hay 2 alternativas que proveen `java'.
Selección Alternativa
-----------------------------------------------
  1 /usr/bin/gij-4.2
*+ 2 /usr/lib/jvm/java-1.5.0-sun/jre/bin/java
Pulse <Intro> para mantener el valor por omisión
[*] o pulse un número de selección:

Para no olvidar: Así funciona Struts 2

Así funciona Struts2 para una petición del tipo Request:

  1. Llega un Request a la aplicación.
  2. El Request es interpretador por el DispatcherFilter y determina que Action y que conjunto de Interceptors invocar.
  3. Cada Interceptor ejecuta sus acciones previas a la ejecución del método de Action a invocar.
    • Si el Interceptor I18nInterceptor intercepta el Action: Se ubicará en la sesión del usuario un objeto Locale para utilizar i18n.
    • Si el Interceptor ValidationInterceptor intercepta el Action: Se ejecutan la reglas de validación definidas sobre el Action
    • Si el Interceptor AnnotationValidationInterceptor intercepta el Action: Se chequea en el método a invocar del Action si tiene la anotación @SkipValidation, en cuyo caso no se realizan validaciones
  4. Es ejecutado el método anotado con @Before en el Action
  5. Es invocado el método del Action.
  6. Es ejecutado el método anotado con @After en el Action
  7. Es ejecutado el método anotado con @BeforeResult en el Action
  8. Cada Interceptor ejecuta sus acciones posteriores a la ejecución del método de Action a invocar
    • Si el Interceptor ModelDrivenInterceptor intercepta el Action: Luego de la ejecución del Action se ubicara en el value stack el modelo que provee el Action.
    • Si el Interceptor ParametersInterceptor intercepta elAction: Los parametros provenientes del Request se ubican en el value stack
  9. Se examina el resultado obtenido del Action y se determina el Result correspondiente.
  10. Mediante el Result determinado se genera la vista, y según la configuración definida sober el se invoca el proceso de generación de la vista.
  11. La vista generada retorna al cliente.

Extracto de esta página

Ahora “The Big Picture” o la arquitectura de Struts2.

Referencias:

Escuchando: “AC/DC“ - “The Honey Roll