Como hacer un componente de JSF

2
31939

Creación: 27-06-2007

Índice de contenidos

1. Introducción

Ya hemos visto algunas cosas sobre JSF (https://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=jsf). Básicamente podríamos decir que se trata de un estándar que ya forma parte de la especificación de Java EE 5, donde el desarrollo se hace en base a componentes.

Lo ideal es usar los componentes estándar o usar librerías de componentes ya construidas, como Tomahawk (http://myfaces.apache.org/tomahawk/) o ICEfaces (http://www.icesoft.com/products/icefaces.html). Pero en algunas ocasiones no encontraremos ningún componente que cubra nuestras necesidades.

Para estas ocasiones siempre nos queda la opción de construirnos nuestros propios componentes. Esto es lo que vamos a ver en este tutorial.

En concreto vamos a construir un componente que, indicándole un POJO, nos pinte un formulario con una etiqueta y un campo de entrada para cada una de las propiedades de tipo String de ese POJO.

¡¡¡ Atención, el componente que vamos a construir sólo pretende ilustrar la construcción de un componente, pero todavía le quedará mucho para poder ser usando en producción !!!

Y ya, entrando en harina, podemos adelantar que para la construcción de un componente vamos a desarrollar tres piezas:

  • el componente: esta clase es realmente el componente, y es donde estará implementada la lógica asociada.
  • el render: es la clase que se encarga de pintar el componente y de recuperar la información introducida por el usuario para “inyectarla” en el componente. El render está asociado a un medio concreto (web, wap, …), mientras el componente es único. Es decir, podemos tener varios render para un mismo componente, o incluso reescribir los render si queremos cambiar el “como se pinta”.
  • el tag: lo normal (aunque no obligatorio) es que nuestro componente lo queramos usar cómodamente en páginas JSP. El tag será la clase que implemente eso, una etiquetad de JSP.

En los siguientes apartados se ira viendo como construimos cada una de estas piezas.

Vamos a aprovechar y vamos a recordar el ciclo de vida de JSF. Para el desarrollo en JSF (bien sea de aplicaciones o de componentes) es fundamental comprender y dominar las 6 fases del ciclo de vida de JSF:

  1. Restore view: Se reconstruye el árbol de componentes.
  2. Apply request values: Se leen los valores de la request y se aplican sobre los componentes. En este momento es cuando se llama a los converters. Si hay algún error en la conversión se irá directamente a la fase “render response”. Si un componente tiene “immediate=true” su validación (y el procesamiento de los eventos provocados por esa validación) se hará en esta fase .
  3. Process validations: se validan todos los componentes (todos los que tienen “immediate=false”, ya que los que lo tienen a true ya se validaron en la fase anterior). Si falla alguna de las validaciones se irá directamente a la fase “render response”.
  4. Update model values: Los valores de los back beans de JSF se actualizan con los valores que hasta ahora estaban guardados sólo en los componentes.
  5. Invoke application: Se invoca a los métodos de los back beans.
  6. Render response: se pinta la respuesta al usuario. Se pinta el árbol de componentes.

Podéis encontrar más información en
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSFIntro10.html.

Aquí podéis encontrar el código completo de las tres clases necesarias para construir el componente.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Asus G1 (Core 2 Duo a 2.1 GHz, 2048 MB RAM, 120 GB HD).
  • Sistema Operativo: GNU / Linux, Debian (unstable), Kernel 2.6.21, KDE 3.5
  • Máquina Virtual Java: JDK 1.6.0-b105 de Sun Microsystems
  • Eclipse 3.2.2
  • MyFaces 1.1.5
  • Maven 2.0.7

3. El componente

A la hora de construir un componente debemos fijarnos si ya hay alguno que hace algo parecido, ya sea dentro del estándar, o de alguna librería que hayamos adquirido. Si ya tenemos un componente que hace algo parecido la mejor opción suele ser que nuestra clase extienda de ese componente para aprovechar las funcionalidades del padre. Si no encontramos ningún componente que podamos reutilizar tendremos que extender de la clase javax.faces.component.UIComponentBase.

En nuestro ejemplo vamos usar el segundo camino, extenderemos la clase UIComponentBase. Vamos a ir viendo paso a paso esta clase (recordar que en al final de la introducción tenéis acceso un tar.gz con el código completo):

Estas dos constantes son cadena arbitrarias, aunque hemos decidido usar los nombres de las clases, se podría haber puesto cualquier otra cosa. Luego las utilizaremos para determinar el render que se tiene que usar para pintar el componente.

En este atributo guardaremos el bean a partir del cual vamos a pintar el formulario, y donde se guardaran los valores introducidos por el usuario (en la fase “update model values”). Es de tipo ValueBinding para poder usar lenguaje de expresiones de JSF para darle valor en la JSP.

Según hemos visto en el ciclo de vida, los valores de la request se almacenan en el componente hasta alcanzar la fase “update model values”. Este atributo lo usaremos para guardar esos valores recuperados de la request. Ya hemos dicho que nuestro componente sólo trabajará con las propiedades de tipo String, así que en este mapa guardaremos como clave el nombre de la propiedad y como valor, el valor introducido por el usuario.

Constructor de la clase. Es importante llamar a setRenderType(), este es un método de nuestro padre que permite definir cual será el identificador del render a usar. Si no hacemos esto luego JSF no será capaz de elegir el render apropiado para pintar el componente.

Este es el único método que estamos obligados a implementar por extender la clase UIComponentBase. Este método devuelve el identificador de la familia a la que pertenece este componente. JSF, en función de este valor y del tipo de render (lo hemos definido en el constructor), elegirá el render apropiado para pintar este componente.

Este método devuelve el POJO que hemos asociado al componente (el POJO que usaremos para pintar el formulario y posteriormente para guardar los valores). Nótese como se extrae el valor del atributo beanBinding (dijimos que este atributo va a hacer referencia al POJO mediante lenguaje de expresiones de JSF). También hay “setter” correspondiente para fijar el valor del POJO (se puede ver en el código completo).

Este método se va a invocar en la fase de “update modelvalues”, y es el que se tiene que encargar de actualizar el backbean (en nuestro caso el POJO que tenemos referenciado con elatributo beanBinding) con los valores que estánguardados localmente en el componente (en nuestro caso los valoresque tenemos en el atributo localVlaues). Igual que estémétodo, tenemos métodos similares por si tenemos quehacer cosas en el resto de las fases (processRestoreState,processDecodes, processValidators, …).El código lo que hace es recorrer el mapa con los valoreslocales que ha recogido el componente de la request y buscar esaspropiedades en el POJO por introspección. En cualquier caso noes más que un ejemplo y no importa tanto su implementacióncomo que quede clara la responsabilidad del método, y que estese invoca en la fase “update model values”.Con esto hemos terminado con el componente (se han omitido algunosgetter y setter que podéis ver en el código completo).Cabría destacar especialmente los métodos para guardarel estado del componente entre request (restoreState,saveState) y el método que actualiza los valoresde los back beans (processUpdates).

4. El render

Esta clase será la que se encargue de recuperar losparámetros de la request http y pasárselos alcomponente, y de consultar el componente para “pintar” el HTML.Esto lo podría haber hecho el propio componente pero no esrecomendable. Lo bueno de los componentes de JSF es que podemosreutilizarlos para diferentes dispositivos, y esto lo conseguiremosteniendo diferentes renders para un sólo componente.Para implementar un render extenderemos de la clasejavax.faces.render.Renderer.

Este método decode se encarga de extraer losparámetros de la request de http, y guardarlos en la variablelocal del componente. Igual que antes, no es tan importante laimplementación del algoritmo, como que quede claro que estemétodo es el que se llama en la fase de “apply requestvalues” para guardar los parámetros de la request en elcomponente.

Para “pintar” el componente tenemos los métodosencodeBegin, encodeChildren y encodeEnd.Estos tres métodos permiten pintar la etiqueta inicial, pintarel cuerpo o etiquetas anidadas, y pintar el final de la etiqueta. Enlos casos en los que no es necesario hacer esta separación(como en nuestro caso), se mete todo el código en el métodoencodeEnd. Nuevamente recordar que no es más queun ejemplo.

5. El tag

Como último paso vamos a crear un tag de JSP para poderusar cómodamente nuestro componente en una página JSP.Para ello vamos a extender de la clasejavax.faces.webapp.UIComponentTag. Veamos como quedanuestra clase:

El tag admitirá un parámetro donde se recogeráel lenguaje de expresiones de JSF que indica el POJO que se usaráen el componente. En el atributo bean será dondeguardemos la cadena con este lenguaje de expresiones.

Estos dos métodos estamos obligados a sobreescribirlos porextender de UIComponent. getComponentType()sirve para indicar la familia del componente, y getRenderType()el tipo de render que sabe pintarlo. Estos dos métodosdeterminarán el render que hay que usar para pintar elcomponente cuando se procese el tag. Se están usando lasconstantes que habíamos definido en la clase del componente.<

Sobreescribimos este método para procesar los parámetrosindicados en el tag en la JSP. Se comprueba que el atributo beantenga valor, y que este realmente esté apuntando a un backbean. No se nos debe olvidar llamar a super. Nótesecomo el parámetro component es nuestro componenteya creado por JSF.

Los tags se reutilizan, así que es muy convenientesobreescribir el método release() para noencontrarnos con sorpresas.Toda librería de tags necesita un descriptor (*.tld) paraque el servidor sepa manejarlo. Para nuestro tag tendremos elsiguiente descriptor:

Básicamente estamos indicando el nombre del tag”beanFormComponent la clase que lo implementa, yque tiene un atributo “bean” de tipo String que esobligatorio.

6. Ejemplo de uso

Ya tenemos todas las piezas para usar nuestro componente a medida.Una JSP de ejemplo podría ser la siguiente:

Arriba se incluye el taglib uri="app-jsf-tags" (paraver el significado de esta uri ver el web.xml del tar.gz con elcódigo completo), y le damos el prefijo “a“.Ahora para usar la etiqueta sólo tenemos que hacer:

Donde “#{contact}” es el lenguaje de expresiones deJSF que indica el POJO que vamos a vincular al componente. Ojo”contact” deberá existir en algúnámbito (request, session, aplicación), o estar definidoel faces-config.xml.Y hablando del faces-config.xml, no se nos puede olvidar dar dealta en este fichero tanto el componente como el render que lo va apintar:

Primero hemos dado de alta el componente hemos definido la clase quelo implementa y el tipo del componente (corresponde con la constanteCOMPONENT_TYPE que habíamos definido en la claseFormBeanComponent).Luego damos de alta el render, indicamos su clase, el tipo derender (corresponde con la constante DEFAULT_RENDER_TYPEque habíamos definido en la clase FormBeanComponent),y la familia de componentes que sabe pintar (corresponde con laconstante COMPONENT_TYPE que habíamos definido enla clase FormBeanComponent).

7. Conclusiones

Recordamos que este componente que hemos construido es simplementeun ejemplo, y que hay cosas que no se han tenido en cuenta (como porejemplo las validaciones, o uso de los converters). Cuandoimplementéis vuestros componentes tenéis que prestarmucha atención sobre el ciclo de vida completo.Y ya sabéis, intentar usar algo que ya exista (siempre semás fácil y más barato integrar quedesarrollar), construir a partir de algo que ya exista, y si no osqueda más remedio hacerlo de cero. Y siempre tendréisuna cuarta opción: contactar con nosotros en www.autentia.compara que os echemos una mano 😉

8. Sobre el autor

Alejandro Pérez García, Ingeniero en Informática(especialidad de Ingeniería del Software)Socio fundador de Autentia (Formación, Consultoría,Desarrollo de sistemas transaccionales)mailto:alejandropg@autentia.comAutentia Real Business Solutions S.L. – “Soporte a Desarrollo”http://www.autentia.com 

2 Comentarios

  1. Saludos, Muy bueno el ejemplo,

    Ahora tengo una duda siguiendo tu ejemplo cree mi propio componente, el cual funciona perfecto, pero se me presento un detalle, que a incluirlo en una pagina .xhmtl con facelets el componente no funciona correctamente, ya que por lo visto el facelest no ejecuta la clase manejadora del tag es decir la que hereda de (javax.faces.webapp.UIComponentTag) si no que trabaja directamente con el component y el renderer directamente, pero a no utilizar esta clase no ejecuta el metodo setProperties(UIComponent component); y este es el cual realiza el seteo de los atributos del componente, y hace que en el renderer de un error de NULL al no ser seteado los atributos, si tienes alguna sugerencia, seria de mucha hayuda y gracias de antemano

  2. Tengo una consulta, como hago para integrar esto a paginas xhtml, segun otro articulo publicado aqui mismo (http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=migrateJSF2Facelets) , hice todos los pasos, pero no encuentro la forma que los componentes en el \\\”xhtml\\\” me desplieque las propuedades que necesito asignarles, solo me despliequa el id y yo tengo varias propiedades en el tld (name, values, label,requerida, etc.) y programadas en una clase \\\”InputEntryTag\\\” pero esto no lo puedo habilitar ejmp de como se me muestra acutalmenete en le xhtml: \\\”\\\”

Dejar respuesta

Please enter your comment!
Please enter your name here