Facelets es el mecanismo de plantillas empleado por JavaServer Faces (JSF). Esencialmente es un "JSP" mejorado para ofrecer más funcionalidad, convertir el documento en XML estricto y orientarlo totalmente al mundo de JSF.
En un principio Facelets fue el desarrollo de un único programador, que lo ofreció como código abierto y con una documentación bastante buena. En poco tiempo otros desarrolladores lo adoptaron en sus proyectos y pronto pasó a ser empleado como un estándar de facto para JSF. Algunas compañías se unieron también -notablemente JBoss, que desde el comienzo apostó por él en Seam- y rápidamente emplear JavaServer Faces se hizo sinónimo de emplear esta librería.
Finalmente la especificación de JSF 2 lo añadió como el mecanismo de plantillas por defecto, convirtiéndolo en un estándar oficial.
Facelets ofrece un API sencillo y muy limpio para implementar funciones EL. ¿Qué son exactamente "funciones EL"? Código que podemos invocar desde las plantillas de nuestras páginas. Facelets eliminó por completo la posibilidad de añadir scriptlets (fragmentos de código) típicos de las páginas JSP. Muchos desarrolladores -especialmente los menos experimentados- estaban abusando de ellos y las páginas se hacían difíciles de leer y mantener -cuando no introducían directamente problemas de arquitectura-. Facelets limitó ese uso de código puro potenciando el empleo de funciones y etiquetas que el programador podía diseñar fuera de la própia página para ser invocadas desde la misma.
Las etiquetas son exactamente eso: componentes estándar de JSF. Por ejemplo, un caso sencillo de seguridad en donde sólo quisieramos mostrar un contenido a un determinado rol podría ser:
La otra posibilidad sería escribir una función -código que se invocará-. En este caso podría ser:
Desde que la especificación del estándar JEE 6 fue presentada, quedó claro que habría aspectos en que su funcionalidad se solaparía con Spring: la plataforma empresarial de Java había aprendido mucho de SpringSource, pero seguiría un camino distinto. En su implementación actual, la inyección de dependencias es diferente, los APIs obviamente también lo son y, logícamente, las empresas detrás de ambos frameworks compiten en este espacio.
La reacción de Spring está siendo ignorar en parte algunos de los elementos presentes en JEE 6. Ambos frameworks son perfectamente integrables, pero si Spring ha sido siempre un líder en relación a la calidad de integración, se hace extraño ahora encontrar carencias en las librerías de Spring Security para JSF 2, la ausencia de apoyo firme para Facelets o que, en la documentación de uno de sus productos -Spring Roo, en su versión actual (XXX.XXX)- la sección referida a la generación de código para JSF 2 esté totalmente en blanco, los beans que se anotan empleando JSF 2 no son visibles para Spring, etc-.
En el caso concreto de Spring Security, se ofrece un pequeño conjunto de componentes y funciones para gestionar la seguridad. En algunos casos sin embargo se hace necesario ampliarlos y, como se puede ver, es algo trivial.
Una función básica requiere dos ficheros: uno en XML con la declaración de la función y otro en Java con la implementación.
El archivo XML debe contener este formato:
Hasta JSF 1.2
El DOCTYPE varía en función de si estamos empleando JSF 1.2 o JSF 2)
namespaceEs el nombre de este espacio de nombres. Podemos imaginar el espacio de nombres como el identificador único para estas funciones y componentes. Se emplea para evitar que dos funciones desarrolladas con el mismo nombre y parámetros por dos desarrolladores distintos colisionen. Más abajo lo veremos en acción y quedará más claro.
function-name - Es simplemente el nombre la función. Coincidirá con el nombre del método en Java.
function-class - Nombre cualificado de la función
function-signature - Firma de la función, con los parámetros y el tipo de retorno
Definir una función sencilla -comprobar si un usuario tiene un rol concreto en Spring Security- sería entonces:
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "facelet-taglib_1_0.dtd"> <facelet-taglib> <namespace>http://www.juanmedin.com/spring-security/taglibs</namespace> <function> <function-name>tieneRol</function-name> <function-class>com.juanmedin.security.taglibs.TieneRol</function-class> <function-signature>boolean tieneRol(java.lang.String)</function-signature> </function> </facelet-taglib>
La clase que contiene la implementación en Java es sencilla. En nuestro caso la dividiremos en dos partes: la implementación que satisface el contrato de las funciones para JSF (la clase TieneRol) y que delega las tareas en sí mismas a una segunda (XXX)
package com.juanmedin.security.taglibs; // Imports public class TieneRol extends TagHandler { private final TagAttribute rol; public TieneRol(ComponentConfig componentConfig) { super(componentConfig); this.rol = this.getRequiredAttribute("rol"); if(this.rol == null) { throw new TagAttributeException(this.rol, "Tienes que especificar el `rol`."); } } public void apply(FaceletContext faceletContext, UIComponent uiComponent) throws IOException, FacesException, FaceletException, ELException { if(this.rol == null) { throw new FaceletException("Tienes que proporcionar un rol. Ahora mismo es null."); } String roles = this.rol.getValue(faceletContext); if(roles == null || roles.trim().isEmpty()) { throw new FaceletException("Debes indicar un rol"); } if(isGranted(rols)) { this.nextHandler.apply(faceletContext, uiComponent); } } public static boolean tieneRol(String rol) { GrantedAuthority[] authorities = TagUtilities.getUserAuthorities(); for (GrantedAuthority authority : authorities) { if(authority.getAuthority().equals(rol)) { return true; } } return false; } }