Migración de JSP a Facelets.
0. Índice de contenidos.
- 1. Introducción.
- 2. Configuración de facelets.
- 3. Soporte para facelets de librerías de componentes JSF.
- 4. Modificaciones en los JPSs.
- 5. Implementación de un sistema de plantillas.
- 6. Conclusiones.
1. Introducción
En este tutorial vamos a analizar, paso a paso, cómo migrar una aplicación JEE, basada en el estandar JSF, de JSPs a Facelets.
Facelets es un framework ligero que permite el uso de plantillas en aplicaciones JSF. Se compara al sistema de plantillas de Apache Tiles que se usa con Apache Struts.
Las principales ventajas de Facelets son:
- Construcción de interfaces basadas en plantillas,
- Rápida creación de componentes por composición,
- Fácil creación de funciones y librerías de componentes.
Si tienes alguna aplicación que mantener basada en JSF y construida en JSPs, espero que este tutorial te sirva para evaluar el coste de su migración a Facelets.
2. Configuración de facelets.
-
Añadimos la dependencia, en el descriptor del proyecto de Maven (en el pom.xml), a la última librería de faceletes:
12345<dependency><groupId>com.sun.facelets</groupId><artifactId>jsf-facelets</artifactId><version>1.1.14</version></dependency>Si no tenemos Maven, incluiremos dicha librería en la carpeta WEB-INF/lib de nuestra aplicación web, la podemos descargar desde la home del proyecto facelets
- facelets.REFRESH_PERIOD: indica el intervalo en segundos para revisar si ha existido alguna modificación en los fuentes (xhtml o jspx), si un recurso ha sido modificado se cargará en caliente.
- facelets.DEVELOPMENT: indica que estamos en el entorno de desarrollo, y frente a un error en el programa nos mostrará una página detallada con la información del mismo, así como una renderización del árbol de componentes jsf que tenemos en memoria.
- facelets.SKIP_COMMENTS: por defecto elimina los comentarios xml del fuente de nuestras páginas xhtml o jspx.
Modificamos el prefijo de las páginas con componentes jsf, en el fichero de configuración web.xml:
Antes:
1 2 3 4 |
<context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.jsp</param-value> </context-param> |
Después:
1 2 3 4 |
<context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.jspx</param-value> </context-param> |
Facelets requiere de páginas xhtml con una estructura xml bien formada y válida, la extensión jspx es por comodidad en el desarrollo para que el IDE autocomplete,
podríamos usar igualmente la extensión xhtml.
Cuando se pida al servidor una página jsf ya no buscará un recurso jsp, sino jspx o xhtml.
Podemos incluir los siguientes parámetros de contexto, en el fichero de configuración web.xml:
1 2 3 4 5 6 7 8 9 10 11 12 |
<context-param> <param-name>facelets.REFRESH_PERIOD</param-name> <param-value>2</param-value> </context-param> <context-param> <param-name>facelets.DEVELOPMENT</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>facelets.SKIP_COMMENTS</param-name> <param-value>false</param-value> </context-param> |
El parámetro que elimina los comentarios deberíamos invertirlo (ponerlo a true) y el resto los deberíamos comentar antes de la liberación de la release, puesto que sólo tienen sentido en desarrollo.
Redefinimos el gestor de las vistas en el fichero de configuración faces-config.xml:
1 2 3 4 5 |
<application> ... <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> ... </application> |
Debemos modificar la extensión de los ficheros *.jsp por *.jspx en las reglas de navegación de JSF, en el fichero faces-config.xml o faces-navigation.xml
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<navigation-rule> <description>Menu bar links</description> <from-view-id>*</from-view-id> ... <navigation-case> <from-outcome>inbox</from-outcome> <to-view-id>/pages/inbox.jsp</to-view-id> </navigation-case> ... </navigation-rule> <navigation-rule> <from-view-id>/pages/faq/list.jsp</from-view-id> <navigation-case> <from-outcome>editContact</from-outcome> <to-view-id>/pages/contact/edit.jsp</to-view-id> </navigation-case> ... </navigation-rule> |
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<navigation-rule> <description>Menu bar links</description> <from-view-id>*</from-view-id> ... <navigation-case> <from-outcome>inbox</from-outcome> <to-view-id>/pages/inbox.jspx</to-view-id> </navigation-case> ... </navigation-rule> <navigation-rule> <from-view-id>/pages/faq/list.jspx</from-view-id> <navigation-case> <from-outcome>editContact</from-outcome> <to-view-id>/pages/contact/edit.jspx</to-view-id> </navigation-case> ... </navigation-rule> |
3. Soporte para facelets de librerías de componentes JSF.
Si tenemos librerías de componentes JSF propias, como es nuestro caso, por cada *.tld que tengamos creada, necesitamos dar de
alta una *taglib.xml para todos los componentes de la libería. La ubicación de los ficheros *taglib.xml es el directorio META-INF de la aplicación web.
El contenido del fichero taglib.xml viene a ser como sigue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0"?> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> <facelet-taglib> <namespace>http://www.autentia.com/client/project</namespace> <tag> <tag-name>vacationCalendar</tag-name> <component> <component-type>com.autentia.client.jsf.HtmlVacationCalendar</component-type> <renderer-type>com.autentia.client.jsf.HtmlVacationCalendarRenderer</renderer-type> </component> </tag> </facelet-taglib> |
- el contenido de la etiqueta namespace se corresponde con el nodo taglib/uri de la *.tld.
- el contenido de la etiqueta tag-name se corresponde con el nodo taglib/tag/name de la *.tld
- el contenido de la etiqueta component-type se corresponde con el nodo component/component-type del faces-config.xml
- el contenido de la etiqueta renderer-type se corresponde con el nodo render-kit/renderer/renderer-type del faces-config.xml
Si estamos usando la librería de componentes Apache Tomahawk podemos añadir la siguiente dependencia en el pom.xml de nuestro proyecto:
1 2 3 4 5 |
<dependency> <groupId>com.google.code.tomahawk</groupId> <artifactId>tomahawk-facelets-taglib</artifactId> <version>1.1.6.2</version> </dependency> |
Esta librería no se encuentra en ningún repositorio público de Maven con lo que tendremos que añadirla al repositorio de Maven de nuestra organización. Para su descarga podéis acceder a la home del proyecto tomahawk-facelets
4. Modificaciones en los JPSs.
Todas las modificaciones vienen dadas por la necesidad de tener, en los fuentes de nuestras páginas (xhtml o jspx), un xml bien formado y válido. Son las siguientes:
- Modificación de la extensión de todas las páginas *.jsp a *.jspx.
- Eliminación de las importaciones de tag lib como <@ taglib, ahora son espacios de nombres en el nodo raíz de la página (xmlns:h=»…»)
Antes:
12<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%><%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>Después:
123<htmlxmlns="http://www.w3.org/1999/xhtml"xmlns:ui="http://java.sun.com/jsf/facelets"> - los <@include file=»…» /> deben ser <ui:include src=»/wp-content/uploads/tutorial-data/…» />
- los scriptles <% %> deben suprimirse,
- los comentarios en scriptles <%– –%> tienen que ser comentarios en xml <!– –>
- el acceso al contexto de facletes vía scriptlet <%=facesContext… debe hacerse vía Expression Language #{facesContext…}
- todas las entidades xml deben cambiarse a su formato numérico: los ahora son
- si tenemos algún atributo duplicado en la definición del nodo raíz de alguna etiqueta que renderiza un componente JSF (p.e., dos styleClass en un mismo <h:panelGroup…),
ahora se producirá una excepción de atributo duplicado, - el componente <f:verbatim no puede tener otros componentes jsf en su interior, si es así no se renderiza
- dentro de un atributo de un componente no se pueden incluir llamadas a funciones de jstl:
12<!--Se produce una NullPointerException--><h:panelGroup rendered="fn:length(List) > 1" ... />
Se debe usar:
123<c:if test="fn:length(List) > 1">...</c:if> - Si tenemos alguna expresión con un & hay que escaparla a & o sustituirla por un and:
1 2 |
<!--no lo renderiza--> <f:verbatim><h:outputText value=":" /></f:verbatim> |
1 2 3 |
<h:panelGroup rendered="controller.visible && controller.admin" ... > <!--error!!! --> <h:panelGroup rendered="controller.visible && controller.admin" ... > <!--ok--> <h:panelGroup rendered="controller.visible and controller.admin" ... > <!--ok--> |
5. Implementación de un sistema de plantillas.
Ya hemos visto en adictos al trabajo cómo crear un sistema de plantillas con facelets, me remito al manual de Alejandro para su creación.
Una vez tenemos creado el layout básico de nuestra aplicación y lo hemos incluido en una plantilla de facelets, bastaría con recorrerse una a una las páginas jspx para asilar su contenido individual y encapsularlo dentro una llamada a la plantilla.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:t="http://myfaces.apache.org/tomahawk"> <f:view> <!-- This text above will not be displayed. --> <ui:composition template="/WEB-INF/facelets/templates/defaultLayout.jspx"> <ui:define name="title">Listado de libros</ui:define> <ui:define name="content"> <!-- contenido con el árbol de componentes específico de la página jsf --> <h:form id="books"> ... </h:form> </ui:define> </ui:composition> <!-- This text below will also not be displayed. --> </f:view> </html> |
El trabajo consistirá el eliminar todo el código que sea común y ya esté en la plantilla y, aquél individual a la página, incluirlo en la definición de una variable.
Si teníamos un <@include en todas las páginas JSP ahora solo lo necesitamos en la plantilla como un <ui:include.
6. Conclusiones.
Los beneficios de tener un sistema de plantillas como facelets son evidentes, en la migración sólo se modifica la presentación
y aunque las modificaciones son transparentes para el usuario final, la aplicación gana bastante mantenibilidad.
También aumenta en rendimiento, puesto que ahora las páginas que «pintan» las vistas ya no son JPSs,
con lo que ya no se compilan, ni se genera un servlet por cada una de ellas. Facelets lee el árbol de componentes de la página jspx una sola vez.
El aumento en el rendimiento de la aplicación sí es perceptible por el usuario final.
El coste de la migración es perfectamente asumible, para que tengáis una referencia: una aplicación con 120 páginas jsp se ha migrado en 3 jornadas por
una sola persona, incluido el coste de investigar la mejor manera de llevarlo a cabo, la documentación de la migración (este tutorial) y las pruebas.
Sí es cierto que nosotros tenemos las aplicaciones muy normalizadas y nuestros fuentes son todos muy estandar,
con lo que podemos hacer modificaciones masivas en el código sin miedo.
Si os animáis, contadnos qué tal os ha ido! y si no, pensad que siempre lo podemos hacer nosotros!!!, trabajamos en esto.
Un saludo.
Excelente información, lo voy a poner en practica migrando un sistema.
Espero tener buenos resultados.