Wednesday, February 24, 2010

Validar formularios JSF 2.0 con Bean Validation 1.0

En la entrada anterior se ilustro a través de un ejemplo las ventajas de implementar los backing beans JSF 2.0 como clases manejadas por CDI 1.0.

En esta entrada vamos a ver como integrar una tercera tecnología que forma parte de mi pila tecnológica ideal para desarrollo de aplicaciones web Java EE. Esta tecnología se define en el JSR 303 y se conoce como Bean Validation 1.0.

Bean Validation define un modelo de meta-datos basado en anotaciones y una API para realizar validaciones de JavaBeans.

Siguiendo la misma temática que en las entradas anteriores, voy a utilizar un pequeño ejemplo para demostrar las ventajas de Bean Validation.

El código fuente completo del ejemplo JSF-BeanValidation.zip, esta disponible en la plataforma kenai.com. Se creo un proyecto exclusivamente para albergar todos los ejemplos utilizados en este blog.

Aplicación de Ejemplo

La aplicación de ejemplo esta compuesta de tan solo una pantalla. Esta pantalla es un formulario que se utiliza para ingresar usuarios. El usuario solamente tiene dos campos, username y mail.

La aplicacion tiene tres componentes, el primero es una pagina Facelets que es la que presenta el formulario, los otros dos componentes son clases Java. Una representa al backing bean JSF y la otra es el modelo del usuario.

A continuación se ilustra la sección relevante del código fuente de la pagina


<h:form>
<h:panelGrid columns="3">
<h:outputLabel value="Username:" for="username"/>
<h:inputText id="username" value="#{userController.user.username}"/>
<h:message for="username" errorStyle="color: red" infoStyle="color: green"/>
<h:outputLabel value="E-mail:" for="email"/>
<h:inputText id="email" value="#{userController.user.email}"/>
<h:message for="email" errorStyle="color: red" infoStyle="color: green"/>
<h:commandLink action="#{userController.saveUser}" value="Save"/>
</h:panelGrid>
</h:form>
<h:panelGrid>
<h:messages globalOnly="true" errorStyle="color: red" infoStyle="color: green"/>
</h:panelGrid>


Como se puede ver, las cajas de texto están asociadas a campos de una instancia de la clase User.
Se accede a esta instancia a través de un campo del backing bean JSF userController.

El formulario cuenta con un botón que permite dar de alta al usuario. Para mantener a la aplicación lo mas simple posible, realmente no se van a almacenar los usuarios en ningún repositorio de datos, solamente se va a validar el modelo utilizando Bean Validation 1.0.

En una aplicación real, probablemente se utilizaría Java Persistence API 2.0 para realizar la persistencia, en entradas posteriores vamos a ver ejemplos de como integrar JPA con el resto de las tecnologías que vimos hasta ahora.

Validaciones con Bean Validation 1.0

La mayoría de las aplicaciones requieren validar la entrada de datos para permitir que el modelo de datos sea consistente.

Por ejemplo, si tenemos un formulario de alta de usuario en una aplicacion web, deberíamos validar que los datos ingresados sean los correctos. Además, tendríamos que notificar cuando un dato es incorrecto para que este pueda volver a ser ingresado correctamente.

En la practica, varios programadores forman parte del grupo encargado del desarrollo de una aplicación.

El desarrollador encargado de la lógica de negocios de un ingreso de usuario, no puede suponer que el desarrollador del formulario de alta de usuario realizó la validación de los campos en la capa de presentación.
Suponer esto puede hacer que la aplicación no sea robusta, ya que los datos ingresados podrían no ser correctos.
Por lo tanto el desarrollador de la lógica de negocios tiene que volver a validar que los campos sean correctos y este mismo escenario se repite para el desarrollador encargado de la capa de acceso a datos.

Es así como uno termina teniendo validaciones en cada capa de la aplicación como muestra la siguiente figura:



Este proceso no solo es propenso a errores, sino que duplica código y hace que la manutención del software sea mas difícil.

Por lo tanto, Bean Validation 1.0 es un esfuerzo por tener un único esquema de validación que se propague por todas las capas de la aplicacion.

La validación se realiza a nivel de modelo y se especifica de manera declarativa.

Bean Validation 1.0 tiene dos componentes:


  • Un modelo declarativo y extensible de validación basado en anotaciones

  • API estándar de consulta de meta-datos y validación en tiempo de ejecución



Como se comento anteriormente, para definir las restricciones de los campos se utilizan anotaciones, Bean Validation incluye varias anotaciones estándar que se encuentran en el paquete javax.validation.constraints. Las anotaciones estándar son:

  • @AssertFalse: El campo Boolean tiene que ser false.
  • @AssertTrue: El campo Boolean tiene que ser true.
  • @DecimalMax: El campo tiene que ser un numero cuyo valor sea menor o igual a un máximo especificado.
  • @DecimalMin: El campo tiene que ser un numero cuyo valor sea mayor o igual a un mínimo especificado.
  • @Digits: El campo tiene que ser un numero cuyo valor se encuentre en un rango definido.
  • @Future: El campo tiene que ser una fecha en un futuro.
  • @Max: El campo tiene que ser un numero cuyo valor sea menor o igual a un máximo especificado.
  • @Min: El campo tiene que ser un numero cuyo valor sea mayor o igual a un mínimo especificado.
  • @NotNull: El campo no puede tener valor null.
  • @Null: El campo tiene que tener valor null.
  • @Past: El campo tiene que ser una fecha en el pasado.
  • @Pattern: La cadena tiene que coincidir con el patrón de expresión regular especificado.
  • @Size: El tamaño del campo tiene que estar dentro de un limite especificado (incluyente).


En la aplicación de ejemplo, la clase User utiliza Bean Validation para validar los campos, en el siguiente cuadro se ilustra el código fuente de la clase User:


public class User {

@NotNull
@Size(min=1,max=30)
private String username;

@Size(max=100)
@Pattern(regexp=".+@.+\\.[a-z]+")
private String email;

/*
getters y setters
omitidos
*/
}


La clase tiene dos campos username y email. Las validaciones especifican las siguientes restricciones para cada campo:

  • username: El campo no puede ser nulo (@NotNull) y su tamaño tiene que estar dentro del limite 1 a 30 (@Size(min=1,max=30).
    En el caso de una cadena, el tamaño se evalúa como longitud de la cadena, aunque tiene semántica distinta para otros tipos de datos (Collection, Array, Map, etc).
  • email: El tamaño del campo no puede ser mayor a 100 (@Size(max=100). Además, la cadena tiene que coincidir con el patrón definido por una expresión regular (@Pattern(regexp=".+@.+\\.[a-z]+")). Esta expresión regular especifica el formato válido para las direcciones de correo electrónico.


Como se comento anteriormente, Bean Validation esta compuesto de dos elementos. El primero es un modelo de meta-datos basado en anotaciones que se utiliza para decorar las clases y definir restricciones en los campos. Esto es lo que acabamos de ver.

El segundo componente es la API de consulta de meta-datos y validación. Esta API es la que se utiliza para verificar si una instancia de una clase cumple con todos los requisitos definidos con las anotaciones de Bean Validation.

Para validar las instancias de los JavaBeans se utiliza una implementación que implemente la interfaz:


javax.validation.Validator


La interfaz tiene un método validate() que es el que se utiliza para validar las instancias. Se obtiene una instancia de la implementación a través de un método factory:


Validator validator = Validation.buildDefaultValidatorFactory().getValidator();


A continuación, mostramos un ejemplo de como verificar si un usuario cumple con todas las restricciones. De no cumplir con alguna restricción, imprime en la salida estándar el nombre del campo que no cumple con la restricción y su respectivo mensaje de error.


Set<constraintviolation><user>> constraintViolations = Validation.buildDefaultValidatorFactory().getValidator().validate(this.user);

for(ConstraintViolation<user> constraintViolation : constraintViolations){
System.out.println(constraintViolation.getPropertyPath());
System.out.println(constraintViolation.getMessage());
}


Invocar explícitamente el método validate() debería ser la excepción y no la regla, ya que la idea es definir la validación en el modelo y utilizarla en todas las capas. Por lo tanto la mayoría de los frameworks de capa de presentación, tendrían que integrar automáticamente el mecanismo de validación de Bean Validation.

JavaServer Faces 2.0 lo integra automáticamente. Lo único que uno tiene que hacer es definir la validación en el JavaBean que contiene los campos asociados a los componentes GUI JSF y definir tags <message> o <messages> que procesen los mensajes de error.

Por ejemplo, en el formulario de alta de usuarios, hay una caja de texto asociada al campo username de la clase User:


<h:inputText id="username" value="#{userController.user.username}"/>
<h:message for="username" errorStyle="color: red" infoStyle="color: green"/>


Además, hay un tag <message> que esta asociado al <inputText> asociado al campo username.

Por lo tanto, si ocurre un error de validación en ese campo, JSF actualiza automáticamente el componente UI JSF correcto con el mensaje de error correspondiente.

Si queremos que los mensajes de error sean globales, entonces tenemos que definir un tag <messages> que no este asociado a ningún componente GUI.


<h:messages globalOnly="true" errorStyle="color: red" infoStyle="color: green"/>


Entonces, todos los mensajes de error de validación van a ser agregados a este componente UI, sin importar cual campo es el que no cumple con sus restricciones de validación.

Eso es todo lo que necesitamos para implementar validaciones de modelo en nuestras paginas JavaServer Faces 2.0.

Al hacer clic en el botón:


<h:commandLink action="#{userController.saveUser}" value="Save"/>


Primero se va a realizar la validación del modelo y si algún campo no cumple con sus restricciones de validación, se va a notificar mostrando los mensajes de error en los tags de mensajes.
Solamente si se cumplen todos los requisitos de validación, se va a ejecutar el método saveUser().

En esta entrada vimos un uso básico de Bean Validation, en próximas entradas vamos a ver conceptos mas avanzados como tener varios grupos de validación, crear restricciones de validación propias y el uso de Bean Validation para validar clases persistibles a través de JPA 2.0.

Thursday, February 18, 2010

Ejemplo Aplicación JSF 2.0 con CDI 1.0

Como había comentado en una entrada anterior, Java EE 6 nos ofrece varias opciones para guardar el estado de los componentes UI cuando utilizamos JavaServer Faces.

Mi opción preferida es utilizar Contexts and Dependency Injection (CDI) para los backing beans JSF. Por lo tanto, en esta entrada voy a presentar un pequeño ejemplo de como utilizar JSF + CDI y voy a ilustrar algunas de las bondades de CDI.
No voy a explicar como configurar el proyecto para que utilice CDI, eso pueden verlo en esta entrada del blog.

El código fuente del ejemplo completo JSF-CDI.zip se encuentra en el proyecto creado, para los ejemplos del blog, en la plataforma kenai.com.

Aplicación de Ejemplo

La aplicacion de ejemplo es sumamente sencilla, esta compuesta solamente de dos paginas. La primera pagina tiene una caja de texto en el que se puede definir una entrada. Al presionar un botón se muestra en una etiqueta el texto ingresado. La segunda pagina permite ver el texto ingresado en la primera pagina y su función es ilustrar como se puede guardar el estado entre peticiones de distintas paginas.

JSF 2.0 utiliza como tecnología de vista Facelets ya que se integra mejor con JSF que JSP, por lo tanto las paginas del ejemplo son Facelets. A continuación se muestra la porción relevante del código fuente de ambas paginas:


<h:form>
<h:panelGrid columns="3">
<h:outputLabel value="Please enter an input:"/>
<h:inputText value="#{controller.input}"/>
<h:commandButton action="#{controller.setOutputAction}"
value="Set Output"/>
<h:outputLabel value="You entered: "/>
<h:outputText value="#{controller.output}"/>
</h:panelGrid>
<br/>
<h:commandButton action="/result" value="See output in another page"/>
</h:form>



<h:form>
<h:panelGrid columns="3">
<h:outputLabel value="You entered: "/>
<h:outputText value="#{resultController.output}"/>
</h:panelGrid>
<br/>
<h:commandButton action="/index" value="Go to main page"/>
</h:form>


No hay grandes cambios aquí, las paginas Facelets lucen igual que en JSF 1.2, están compuesta de componentes UI JSF y utilizan expresiones EL (Expression Language) para referirse a instancias de las clases que se utilizan como backing beans.

El cambio está en los backing beans, que son clases manejadas por CDI.

CDI como Backing Bean JSF

CDI ofrece un conjunto de servicios que permiten que clases manejadas puedan existir durante el ciclo de vida de la aplicación en ámbitos bien definidos.

Cuando un jar cuenta con un archivo beans.xml el contenedor registra todas las clases del jar con el motor CDI automáticamente. No es necesaria ninguna anotación para especificar que una clase tiene que ser manejada por CDI.

De todas maneras, si queremos que las instancias de las clases sean accesibles a través de expresiones EL, tenemos que decorar las clases con la anotación


@javax.inject.Named


De esta manera podemos acceder a los miembros de las clases manejadas a través de expresiones EL.
Por defecto el nombre de la instancia va a ser el nombre de la clase con su primera letra en minúscula, aunque podemos especificar otro nombre para referirnos a la instancia de la clase manejada. Ej:


@javax.inject.Named
public class Controller { ... }

#{controller.input}

@javax.inject.Named("testController")
public class Controller { ... }

#{testController.input}


CDI permite que las instancias existan en ámbitos bien definidos. Para marcar el ámbito de una clase manejada CDI, se utilizan anotaciones que se encuentran en el paquete javax.enterprise.context. Los ámbitos soportados por CDI son los siguientes:


  • @ApplicationScoped (Ámbito de Aplicación): Las instancias existen durante todo el ciclo de vida de la aplicacion. Estas instancias son globales y accesibles desde cualquier sesión HTTP.

  • @SessionScoped (Ámbito de Sesión): Las instancias son accesibles por todas las peticiones que ocurren durante una sesión HTTP.

  • @ConverstationScoped (Ámbito de Conversación): Las instancias existen durante una conversación. Una conversación esta compuesta de varias peticiones y pueden existir varias conversaciones por sesión.
    Realmente, cada petición tiene asociada una conversación. Solamente que si la conversación no es marcada como "long-running" el contexto de conversación se libera al termino de la petición.
    Por el contrario si la conversación fue marcada como "long-running" en alguna petición, el contexto de conversación se propaga a las siguientes peticiones hasta que este contexto sea destruido explícitamente.

  • @RequestScoped (Ámbito de Petición): Las instancias solamente existen durante la peticion HTTP. Una vez que termina el ciclo de vida de la petición, también termina su contexto.


El ámbito de petición (@RequestScoped) es muy común, por lo que existe una anotación que incluye las anotaciones @Named y @RequestScoped, esta anotación es:


@javax.enterprise.inject.Model


En el ejemplo, queremos que la aplicacion tenga el siguiente comportamiento: cuando se presione el botón, se establezca el valor del campo asociado con la caja de texto, a la etiqueta de salida. Además, queremos que ese valor sea accesible desde otra pagina.

Con estos requerimientos, el valor tendría que poder existir durante varias peticiones HTTP. Por lo tanto la instancia del backing bean de la pagina principal tiene que ser anotada con una de las anotaciones @SessionScoped o @ConversationScoped, para que sea accesible durante varias peticiones.

Para este ejemplo se usa @SessionScoped para hacer mas claro el ejemplo y en próximas entradas se utilizara otro ejemplo para mostrar como funcionan las conversaciones en CDI.

Por lo tanto la clase backing bean de la pagina principal se llama Controller y esta definida de la siguiente manera:


@javax.inject.Named
@javax.enterprise.context.SessionScoped
public class Controller implements Serializable {
private String input;
private String output;

@EJB UpperCaseEJB testEJB;

/*
Resto de la declaración de la clase omitida.
*/
}


Implementa la interfaz Serializable porque CDI exige que las clases que se definen en un ámbito que no sea @RequestScoped, implementen esta interfaz.
Los campos input y output están asociados a los controles UI inputText y outputText respectivamente.
Las clases manejadas por CDI cuentan con varios servicios, la inyección es una de ellas y por eso utilizamos un EJB (UpperCaseEJB) para demostrar eso.
La instancia testEJB va a ser inyectada por el contenedor cuando se instancie la clase Controller.

Cuando hacemos click en el botón:


<h:commandButton action="#{controller.setOutputAction}"
value="Set Output"/>


se ejecuta el método setOutputAction() de la clase Controller, el método es el encargado de establecer el valor de la leyenda para mostrar en pantalla:


this.output = testEJB.toUpper(this.input);


El método toUpper() del EJB TestEJB convierte la cadena a mayúsculas, esto se utilizo solamente con fines académicos para demostrar lo sencillo que es utilizar un EJB desde una clase CDI.

Eso es todo, cuando se visualice la pagina nuevamente se va a ver el valor del campo output:


<h:outputLabel value="You entered: "/>
<h:outputText value="#{controller.output}"/>


Contextos e Inyección en CDI

La verdad que hasta ahora no vimos nada tan novedoso, después de todo los @ManagedBean JSF 2.0 también utilizan anotaciones para registrarse y definir su ámbito, son accesibles desde expresiones EL y también pueden inyectar instancias de EJBs.

Una de las novedades de CDI es su mecanismo de biyeccion, es decir la capacidad de publicar una instancia de una clase en algún ámbito y poder referirse a esa instancia desde otra clase.

En el ejemplo, queremos que se pueda ver el valor ingresado en la caja de texto, pero desde otra pagina.
Para esto, el backing bean de la pagina result.xhtml tiene que poder acceder a este valor.

Entonces, lo que queremos es publicar la instancia de la clase Controller que tiene este valor en un ámbito y que luego sea inyectado en la clase ResultController. Cuando se crea una instancia de la clase ResulController, automáticamente el contenedor deberia asignar la referencia a la instancia de la clase publicada.

La clase Controller tiene un ámbito de Sesión ya que fue decorada con la anotacion @SessionScoped.
Además, todas las clases son manejadas por CDI automáticamente, recordemos que la anotacion @Named es tan solo para hacer que la clase sea visible por las expresiones EL.
Por lo tanto, lo único que tenemos que hacer en la clase ResultController es inyectar una instancia de la clase Controller con la anotacion:


@javax.inject.Inject


Ej:


@Named
public class ResultController {

private @Inject Controller controller;

public String getOutput()
{
return controller.getOutput();
}
}
Eso es todo! El contenedor automáticamente inyecta una instancia de la clase Controller.

Por ultimo obtenemos el valor de output con el método getOutput().

Puede darse el caso de que tengamos mas de una instancia de una clase en particular. Por ejemplo, podríamos tener un sistema de control de versiones que tenga que comparar la diferencia entre dos archivos.

En estos casos tenemos que tener una manera para discriminar cual es la instancia que queremos inyectar.

Otros frameworks como Jboss Seam utilizan cadenas de texto como identificadores, pero esto es propenso a errores y los problemas se identifican recien en fase de ejecución.

Por el contrario, CDI utiliza un esquema de discriminación fuertemente tipado. Se utilizan anotaciones para especificar cual es la instancia que queremos inyectar.

En el ejemplo de los archivos tendríamos dos anotaciones, una que se podría llamar @Modificado y otra que podría llamarse @Base.

No solo tenemos que utilizar la anotacion a la hora de inyectar una instancia, sino que la publicación de una instancia ya no puede ser automática.
Tenemos que declarar explícitamente que tipo de instancia estamos publicando.

Por lo tanto, modificamos un poco la aplicacion de ejemplo para utilizar inyección fuertemente tipada. Lo primero que tenemos que hacer es definir una anotacion para discriminar a nuestra instancia que queremos publicar.

La anotacion que creamos se llama @SetupController, esta anotacion nos dice que el Controller que queremos inyectar es el utilizado para establecer el valor del input.

La manera de publicar instancias en CDI es definiendo un método cuyo valor de retorno este decorado con la anotacion:


@javax.enterprise.inject.Produces


Por ejemplo si quisiéramos publicar explícitamente nuestra instancia de la clase Controller podemos definir el siguiente método en la misma clase Controller:


public @Produces @SetupController Controller getController()
{
return this;
}


La semántica del método es que esta produciendo una instancia de una clase Controller con tipo @SetupController.

Cuando uno quiere inyectar una instancia de un tipo en particular, el contenedor busca en todas las instancias que existen en algún ámbito, aquella que cuenta con un método que produce el tipo de instancia que nosotros queremos inyectar.
Una vez que encuentra el método, lo ejecuta, obtiene el valor y lo inyecta en la instancia definida en nuestra clase.

Entonces, si quisiéramos inyectar la instancia de tipo @SetupController en nuestra clase ResultController, tendríamos que cambiar la declaración del campo a la siguiente:


private @Inject @SetupController Controller controller;


También podríamos haber publicado e inyectado tan solo el String que representaba nuestro output, pero me pareció mas interesante ver como se publicaba la propia clase Controller.

Eso es todo, en las siguientes entradas veremos como funcionan las conversaciones en CDI y otras cuestiones como agregar validaciones a nivel de modelo utilizando Bean Validation 1.0.

Monday, February 15, 2010

Utilizar Contexts and Dependency Injection (CDI) en proyectos JSF 2.0

En la entrada anterior se explicaron cuales eran las distintas maneras de implementar un JavaServer Faces (JSF) 2.0 backing bean. En esta entrada vamos a ver como crear un proyecto web JSF 2.0 en Netbeans 6.8 que soporte Context and Dependency Injection (CDI) Managed Beans.

Lo primero que tenemos que hacer es crear un proyecto web:



En el paso de elegir Frameworks, seleccionar JavaServer Faces. En la pestaña de Libraries seleccionar la librería JSF que viene por defecto (JSF 2.0) y en la pestaña de Configuration, seleccionar el lenguaje de pagina preferido como Facelets.



A diferencia de otros motores de manejo de beans, CDI no necesita de ninguna anotación para que una clase se registre y sea manejada. Por defecto todas las clases de un archivo (jar) son manejadas por CDI. La anotación @Named tan solo hace que los atributos de la clase sean accesible desde expresiones EL.

La búsqueda de clases y el registro de las mismas en el motor de CDI hace que el deploy de una aplicación sea mas lento. Por lo tanto, necesitamos de una manera de decirle al container si queremos o no usar CDI. La manera de especificar esto es creando un archivo xml vacío (beans.xml) en los directorios WEB-INF o META-INF.

La sola existencia del archivo WEB-INF/beans.xml o MET-INF/beans.xml ya activa CDI.

Por lo tanto, el ultimo paso consiste en crear el archivo WEB-INF/beans.xml.



Se puede por ejemplo tener varios jar, donde uno solo contiene un archivo beans.xml. De esta manera, el contenedor solo registraría las clases de este jar y no de los otros, haciendo que el deploy de la aplicacion sea mas rápido. Gavin King en su blog explica el porque de la necesidad del archivo beans.xml.

En la Figura podemos ver como queda la jerarquía de directorios del proyecto y como el archivo beans.xml esta vacío, la sola presencia del archivo ya es suficiente para activar el motor CDI.


Esto es todo, ahora lo único que tenemos que hacer es agregar clases y estas ya van a ser manejadas por CDI.

Si deployamos la aplicación, vamos a ver en el log del glassfish que se esta utilizando CDI, cuales son las clases manejadas y el ámbito de cada una.

En la siguiente entrada voy a explicar como utilizar las clases manejadas por CDI como backing beans JSF y como estas pueden ser accesibles desde expresiones EL en paginas Facelets. Para esto voy a utilizar un ejemplo que va a ilustrar las facilidades que nos brinda CDI.

Para hacer mas útil el blog, cree un proyecto en la plataforma Kenai donde voy a ir subiendo los ejemplos de las entradas que vaya publicando.

El primer ejemplo (JSF-CDI) es una pequeña aplicación que utiliza CDI Managed Beans para persistir el estado de los componentes UI de paginas JSF 2.0 con Facelets.

Actualización:

Si se realiza una actualizacion del Netbeans 6.8, ahora al crear un proyecto web, hay una opcion para habilitar CDI en el proyecto.



En la siguiente imagen podemos ver que en la sección de configuraciones, hay un checkbox para elegir si activar o no activar CDI. Por lo tanto lo único que se tiene que hacer es seleccionar esa opción y automáticamente se crea el archivo WEB-INF/beans.xml.

Thursday, February 11, 2010

JSF 2.0 Backing Bean: @ManagedBean o @Named

Dos de los cambios de Java EE 5 y EJB 3.0 que mejoraron la productividad de los desarrolladores, fueron el uso de anotaciones y la configuración por excepción.

Antes de la llegada de Java EE 6 y JSF 2.0, la capa de presentación web no contaba con estas facilidades. Se tenia que editar varios XMLs para definir cuales eran las clases que tenían que ser manejadas por el motor de JavaServer Faces, las clases que se iban a utilizar para realizar conversiones, validaciones y la navegación entre paginas.

JSF 2.0 tiene varias mejores sobre JSF 1.x y entre la mas importante esta la posibilidad de decorar las clases que van a ser manejadas con anotaciones en vez de definirlas en archivos XML.

Lastimosamente no existe una sola manera de definir una clase (Bean) que va a ser manejada (Managed). De hecho Java EE 6 cuenta con tres maneras de definir una clase como un Managed Bean (cuatro de hecho, si tomamos en cuenta faces-config.xml).

Las tres maneras están definidas por distintas especificaciones, se utilizan distintas anotaciones, las prestaciones de cada manera son diferentes e incluso se manejan las clases por distintos contextos de ejecución.

El hecho de tener varias maneras de definir una misma cosa (Backing Bean JSF) hace que la curva de aprendizaje sea mas pronunciada para nuevos desarrolladores e puede incluso hacer mas complicado el desarrollo para programadores con experiencia (Dos anotaciones tienen el mismo nombre @ManagedBean).

A continuación analizamos cada una de las anotaciones:

JSF 2.0 Managed Bean (JSR 314)

JavaServer Faces 2.0 define sus propias anotaciones para registrar las clases que van a ser Managed Beans. La anotación es @javax.faces.bean.ManagedBean y nos libra de tener que especificar esta información en el archivo XML faces-config.xml.

También permite definir el ámbito de las clases manejadas y las hace accesibles desde expresiones EL.

Ej:


@javax.faces.bean.ManagedBean
@javax.faces.bean.SessionScoped
public class Bean implements Serializable{ ... }


También existe otra anotación que permite inyectar en una referencia una instancia de una clase que este siendo manejada en algún ámbito.

Ej:


@javax.faces.bean.ManagedProperty(value="#{bean}")
private Bean bean;

public void setBean(Bean bean)
{
this.bean = bean;
}


Cuando se instancia la clase que contiene el campo bean, se evalúa la expresión y se llama al método setBean().

Managed Bean 1.0 (JSR 316)

Debido a que existían muchos modelos de componentes para definir clases manejadas (JSF Managed Beans, EJB, CDI, etc), el grupo de trabajo de EJB 3.1 definio un modelo de componentes general. Se definió una anotación (@javax.annotation.ManagedBean) que marca a una clase como un "ManagedBean". Estas clases automáticamente cuentan con un conjunto de servicios básicos (inyección de recursos, callbacks del ciclo de vida e interceptores).

Lastimosamente se eligió el mismo nombre para definir ambos tipos de clases manejadas (Managed Bean 1.0 y JavaServer Faces 2.0).

Otro problema de los ManagedBeans es que no son accesibles desde expresiones EL. La única razón por la que se incluyo en esta lista es para evitar que uno se confunda y utilice la anotación:


@javax.annotation.ManagedBean


en vez de


@javax.faces.bean.ManagedBean


Context and Dependency Injection 1.0 (JSR 299)

Context and Dependency Injection (CDI) es un conjunto de servicios que permiten que clases manejadas puedan existir durante el ciclo de vida de la aplicación en contextos bien definidos.

Es otra manera de registrar POJOs como clases manejadas. CDI tiene dos ventajas sobre los otros enfoques:

  1. JSF 2.0 Managed Beans esta muy orientado a un entorno de ejecución web. Al igual que los ManagedBean de JSF 316, CDI pretende ser mas general por lo que ofrece mas prestaciones que los JSF Managed Beans.
  2. CDI permite crear objetos (outjection) e inyectar recursos (injection) en varios ámbitos y utiliza tipos en vez de expresiones, además las clases manejadas por CDI pueden ser accedidas desde expresiones EL (a diferencia de los ManagedBeans JSR 316).
Varias de las ideas de CDI fueron tomadas de frameworks pragmáticos como Jboss Seam, Spring y Google Guice.

La anotación que se utiliza para registrar una clase en CDI es @javax.inject.Named.

Ej:


@javax.inject.Named
@javax.enterprise.context.SessionScoped
public class Controller implements Serializable {...}


Para inyectar una instancia de algún ámbito en una referencia se utiliza el tipo.

Ej:


@Inject private Controller controller;


La inyección es fuertemente tipada y se verifica en tiempo de compilación, no en tiempo de ejecución.

Personalmente creo que CDI es la mejor manera de utilizar clases manejadas en Java EE 6, en próximas entradas veremos cuales son los requisitos para utilizar CDI Managed Beans con el stack de Sun y cuales son sus ventajas.

Tuesday, February 2, 2010

Pila de Tecnologías para proyecto Web Java EE 6

Leí una vez en algún lugar, que decir que uno es programador de "Java" no dice realmente mucho. Es lo mismo que uno diga que sabe "programación". Esto es porque Java es mucho mas que un lenguaje de programación, de hecho la plataforma Java es un universo aparte, con cientos de estándares, especificaciones, plataformas, frameworks, librerías y convenciones.

Mientras mas complejo es el universo de Java, mas se necesita de especialización. El principal problema cuando uno inicia un proyecto Java es el seleccionar frameworks, IDE, build tool y librerías que se van a utilizar.

JSF o Seam?
Spring o EJB3?
JPA o Hibernate?
Eclipse o Netbeans?
Glassfish o Jboss?
Ant o Maven?

Nos encontramos con estas preguntas y necesitamos las respuestas. No creo que exista una respuesta correcta, ya que siempre va a depender de varios factores (requerimientos del software a desarrollar, conocimiento previo del equipo de desarrollo, imposición de cierta plataforma, etc).

De todas maneras, las opciones que uno elija van a tener incidencia en el proceso de desarrollo.

A continuación voy a detallar la pila tecnológica que yo creo es la mas adecuada para un proyecto nuevo en el que se va a desarrollar una aplicación Java web empresarial.

  • Capa de presentación: JSF 2.0 + Facelets + CDI 1.0 + PrimeFaces
  • Capa de lógica de negocios: EJB 3.1
  • Capa de persistencia: JPA 2.0
  • Validaciones: Bean Validation 1.0
  • Web Services: JAX-WS + JAX-RS
  • Reportes: JasperReports
  • Servidor de Aplicaciones: Glassfish V3
  • IDE: Netbeans 6.8
  • Build tool: Ant


La mayoría de estas tecnologías son estándares y forman parte de la plataforma Java EE 6.

Esta decisión es básicamente por dos motivos:

  1. Al utilizar una pila tecnológica de un mismo vendor, uno no pierde el tiempo en integrar varias tecnologías. Es el vendor el que se encarga de que todo se integre de la mejor manera y que la experiencia del desarrollador sea lo mas placentera posible.

  2. El uso de estándares maximiza las probabilidades de que el framework utilizado tenga una comunidad, vaya creciendo, mejorando y no se vuelva obsoleto. Además, nos da cierta garantía para migrar a otro vendor en el caso que necesitemos.


En los próximas entradas, voy a explicar el porque de cada tecnología, por ejemplo CDI Managed Beans y no JSF Managed Beans, etc.