Desarrollar, Aplicar y Optimizar XSLT con Servlets Java

Este artículo explica los patrónes básicos y las técnicas de programación usadas más comumnente cuando se combinan las transformaciones XSL (XSLT), los Servlets Java y XML para crear sofisticadas aplicaciones Web. Entre los tipos de aplicaciones que se benefician de este tipo de aproximación se incluyen:

  • Sitios Web que necesitan dirigirse a muchos navegadores incompatibles o quizás dispositivos sin cable;
  • Aplicaciones Web que necesitan proporcionar datos XML además de interfaces XML;
  • Complejas aplicaciones Web donde tiene sentido el diseño de una forma muy modular; forzando a una separación limpia entre datos, presentación y lógica de programación; y
  • Aplicaciones Web que necesitan dirigirse a diferentes conjuntos de idiomas y juegos de caracteres desde el mismo modelo de datos.

Es difícil de imaginar una situación donde XML y XSLT no podrían funcionar, incluso más elegantemente que las aproximaciones JSP o puro servlet.

XSLT Básico

XSL viene de Extensible Stylesheet Language que es una especificación de dos partes del Worldwide Web Consortium. XSL Formatting Objects, que no se cubre en este artículo, es un lenguaje XML para especificar el formateo de documentos, como fuentes, colores y alineamientos. En el momento de escribir esto, XSL Formatting Objects es un Borrador de Trabajo y está sujeto a modificaciones. Los navegadores web actuales no soportan XSL Formatting Objects, por eso esta no es una tecnología viable para un futuro cercano.

La segunda parte de XSL es XSLT, que fue diseñada para transformar documentos XML bien-formateados en documentos XSL Formatting Objects. XSLT es una recomendación de la W3C, que es equivalente a decir que es un estándar. Aunque XSLT fue diseñado para soportar XSL Formatting Objects, también funcional con transformaciones XML de propósito general. Cuando la gente habla sobre XSL, normalmente se está refierendo a XSLT, ya que se usa muy ámpliamente hoy en día.

Aquí tenemos los elementos básicos de XSLT:

  • Una hoja de estilosXSLT, conforme a la recomendación XSLT 1.0.
  • Una fuente de entrada XML, que también debe ser bien-formateada.
  • Un procesador XSLT, que es una aplicación que sabe como analizar hojas de estilo XSLT y aplicar transformaciones. El servlet de ejemplo de este artículo usará Xalan, una procesar de código abierto de la Fundación Apache.
  • Un árbol de resultado, que es la salida del procesador XSLT.

Como se ve en la figura anterior, la entrada XML se transforma en algo llamado árbol resultante. Este árbol podría ser otro fichero XML, una página HTML, o incluso un fichero de texto normal. Nada une la entrada XML con la hoja de estilo XSLT, por eso hay una clara separación entre los datos y el formato. Esto es un beneficio para programadores de servlets, porque podemos fácilmente dirigirnos a varios navegadores incompatibles simplemente suministrando hojas de estilos diferentes. También podríamos dirigirnos a Wireless Markup Language (WML), de nuevo mediante otra hoja de estilo diferente. Esto es mucho más atractivo que la aproximación tradicional que requiere cambios en la lógica de programación para soportar múltiples objetivos.

Ejemplo 1: Hoja de Estilo XSLT Básica

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:apply-templates select="creditInfo"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="creditInfo">
    Name: <xsl:value-of select="name"/>
    <br/>
    Type: <xsl:value-of select="type"/>
    <br/>
    Number: <xsl:value-of select="number"/>
    <br/>
    Expires: <xsl:value-of select="expires"/>
  </xsl:template>
</xsl:stylesheet>

La hoja de estilo de arriba se usará más adelante en este articulo para transformar un documento XML usando un servlet. El elemento raíz de una hoja de estilo XSLT especifica la versión y el espacio de nombres. La única versión disponible en este momento es 1.0, y el espacio de nombres se selecciona con el prefijo xsl: para todos los elementos XSLT. El siguiente elemento especifica que el documento de salida es HTML, aunque esta línea es opcional en este caso.

El resto de la hoja de estilo consta de dos elementos <xsl:template ...>. El trabajo de una plantilla es buscar patrones en la entrada XML. La primera plantilla normalmente busca /, que es el propio documento XML. El contenido dentro de la plantilla se copia al árbol de resultado excepto para <xsl:apply-templates ...>.

El elemento apply-templates hace que se busquen patrones adicionales en el documento fuente, está vez dentro del contexto de la plantilla actual. Como los documentos XML forman una estructura de árbol, el procesamiento XSLT sigue un proceso recursivo para buscar patrones con <xsl:template>, y luego busca recursivamente por la estructura del árbol usando <xsl:apply-templates>. En nuestro ejemplo de arriba, el elemento raíz del XML es <creditInfo>, que contiene loselementos <name>, <type>, <number>, y <expires>.

Por supuesto que una explicación completa de XSLT se sale fuera del ámbito de este artículo, pero debería ofrecernos un ligero conocimiento de como deberían ser las las hojas de estilo. Podrías estar preguntándote porque la etiqueta HTML <br/> tiene dentro el caracter /. Esto es obligatorio porque la hojas de estilos XSLT deben ser XML bien-formateado. Si omitimos el caracter / hará que se lance un error. Como se ha seleccionado que el método de salida es HTML, Xalan elimina cualquier caracter extra de la salida, formateando <br> igual que el HTML normal.

Problemas con las Típicas Aproximaciones Servlet

La separación de los datos, la presentación y la lógica de programación siempre ha sido un problemas para los desarrolladores de servlets. Laprimera generación de servlets se diseño para reemplazar a los programas CGI, y normalmente contenían código que se parecía a este:

Ejemplo 2: Tediosa Generación de Código HTML

PrintWriter pw = response.getWriter();
pw.println("<html>");
pw.println("<head><title>Home Page</title></head>");
pw.println("<html>");
pw.println("<h1>Welcome to our web site</h1>");

...se ha omitido el resto del código aburrido!

Esta aproximación es tediosa, propensa a errores, y difícil de mantener. Bloquea completamente la autoría Web para los no-programadores, y requiere un esfuerzo significante para cambiar el look de una site Web generada de esta forma. Aunque generalmente escribiremos el código en fragmentos modulares de código HTML, la aproximación aún requiere que se modifique el código fuente ante cualquier nuevo requerimiento.

Java Server Pages (JSP) proporciona una solución a este problema. Desafortunadamente, JSP sólo invierte el problema. En lugar de embeber código HTML en servlets, normalmente terminamos embebiendo código Java en nuestras páginas HTML. Las etiquetas JSP personalizadas nos permiten eliminar todo el código Java, en teoría, pero no sin la dificultad de crear las etiquetas o encontrar una librería existente que cubra nuestras necesidades.

Ejemplo 3: Usar Etiquetas JSP Personalziadas

<%@ taglib uri="/my_taglib" prefix="abc" %>
<html>
<head>
<title>JSP Tag Library Demonstration</title>
</head>
<body>
  <abc:standardHeader/>
  <abc:companyLogo/>

  <h1>Recent Announcements</h1>
  <abc:announcements filter="recent"/>

  <h1>Job Openings</h1>
  <abc:jobOpenings department="hr"/>
  <abc:standardFooter/>
</body>
</html>

Incluso con etiquetas personalizadas es virtualmente imposible eliminar todo el código Java en un solución JSP pura. Especificamente, la aprooximación JSP pura se viene abajo cuando se envían complejos formularios HTML y la página JSP tiene que realizar validaciones. Por esta razón, los más modernos arquitectos Web utilizan una solución híbrida, en la que los servlets son responsables de interceptar peticiones y validar formularios de datos, y se utiliza JSP para enviar la respuesta de vuelta al navegador.

Como ilustra la figura anterior, una clase llamada RequestDispatcher es la responsable de la coordinación entre el servlet y JSP. Esta clase forma parte del paquete javax.servlet; puede reenviar la petición como se muestra arriba o incluir la salida de otro recurso web en la página actual.

Esta aproximación es conocida como Modelo 2 y es muy fácil de implementar. Sus principales limitaciones se manifiestan cuando se deben soportar varios clientes distintos o cuando se desean clientes Wireless Markup Language. Adicionalmente, proporcionar una ristra de datos a un cliente no-navegador se volviendomuy popular, y JSP ayuda poco a conseguir esta necesidad.

La Aproximación XSLT + Servlet

El proceso de diseño para un web site basado-en-XSLT puede estar dirigido por HTML o por la base de datos. Cuando nuestros requerimientos dictan una cierta distribución de la página HTML, el primer paso es crear prototipos HTML y luego analizar las piezas de HTML que son dinámicas y las que son estáticas. El contenido estático formará parte de una o más hojas de estilo XSLT, mientras que el contenido dinámico debe venir desde una fuente de datos XML externa. Por ejemplo, una aplicación que le pide al usuario que introduzca información sobre sutarjeta de crédito podría usar el siguiente documento XML:

Ejemplo 4: Documento XML

<?xml version="1.0"?>
<creditInfo>
  <name>John Q. Public</name>
  <type>Visa</type>
  <number>111-222-333</number>
  <expires>05/2000</expires>
</creditInfo>

La página web obviamente contendrá muchas más información como campos de textos, gráficos y tablas para la distribución. Sin embargo, como la presentación de los datos es la misma para cada usuario, se puede situar fácilmente en un hoja dee stilos. La hoja de estilo mostrada en el ejemplo 1 fue escrita para este documento.

Si nuestro diseño está dictado por una base de datos existente, entonces, en lugar de usar prototipos HTML, generalmente empezaremos con la especificación XML. En ambas aproximaciones, probablemente nos encontraremos iterando entre varios esquemas potenciales antes de finalizar nuestro XML. Una cosa que se nos puede pasa fácilmente es que algunas veces el XML debe contener ítems que no son datos puros. Por ejemplo, si nuestra web site contiene una barra de navegación en la parte superior de la página, la página actual debería estár resaltada de alguna forma. Podría similar pestañas de carpetas, donde la página actual es la pestaña activa. En esta situación, el XML debe específicar cual es la pestaña actual, aunque este dato ciertamente no debería residir en la base de datos. La última solución es extraer los datos "puros" de la base de datos y añadir items de GUI específicos cuando generemos el XML.

Una vez que se ha definido claramente el XML, quizás con una Document Type Definition (DTD) o un esquema de validación asociados, se puede escribir la hoja de estilos XSLT. Esta es otra ventaja de esta arquitectura: la base de datos no tiene que estar presente para poder empezar a trabajar en esta tarea. El XSLT puede desarrollarse completa e independientemente de cualquier código de servlet o de base de datos usando ficheros XML estáticos. Se puede invocar un procesador XSLT desde la línea de comandos para hacer pruebas, o se pueden utilizar herramientas itegradas como XML Spy para acelerar el proceso.

Mientras alguien de nuestro equipo está desarrollando las hojas de estilo XSLT, se puede empezar a trabajar en otros dos frentes. Primero, necesitamos implementar un marco de trabajo servlet básico. Aunque podríamos querer mirar algún marco de trabajo XSLT existente, el modelo es tan simple que funciona lo suficientemente bien para escribir nuestro propio código servlet.

Segundo, alguién más puede escribir código JDOM para generar el XML dinámicamente. Esto podría implicar crear alguna suerte de abstracción a una base de datos relacional, o quizás algún interface a componentes EJB en una aplicación multi-capa. En cualquier caso, el primer paso es escribir las clases Java que sepan cómo convertir un dato fuente, como CreditInfo, a la representación XML que definimos anteriormente. En el ejemplo de abajo, la clase CreditInfo usa JDOM para convertirse a sí misma en el documento <creditInfo> mostrado arriba. En caso de que te lo pregunts, JDOM es un API Java de código abierto para tratar con XML. Esta disponible en jdom.org.

Ejemplo 5: Generación de XML usando JDOM

import java.io.*;
import org.jdom.*;
import org.jdom.output.*;

/**
 * Example of an object that knows how to represent itself as
 * XML using JDOM.
 *
 * @author Eric M. Burke
 */
public class CreditInfo implements Serializable {
    private String name;
    private String type;
    private String number;
    private String expires;

    // transient fields are not serialized.  This prevents the potential
    // overhead of sending too much data between an EJB and the web tier
    private transient Document doc = null;
    private transient Element elem = null;

    /**
     * Construct a new data object.
     */
    public CreditInfo(String name, String type, String number,
            String expires) {
        this.name = name;
        this.type = type;
        this.number = number;
        this.expires = expires;
    }

    /**
     * @return the contents of this object as an XML document.
     */
   public Document getDocument() {
       if (this.doc == null) {
           this.doc = new Document(getElement());
        }
        return this.doc;
    }

    /**
     * This method makes it possible to easily embed the output from
     * this data object into some other larger XML document.
     *
     * @return the contents of this object as an Element, which is just
     * the root element without the XML declaration.
     */
    public Element getElement() {
        if (this.elem == null) {
            this.elem = new Element("creditInfo");
            this.elem.addContent(new Element("name").setText(this.name))
                .addContent(new Element("type").setText(this.type))
                .addContent(new Element("number").setText(this.number))
                .addContent(new Element("expires").setText(this.expires));
        }
        return this.elem;
    }

    /**
     * A simple test program.
     */
    public static void main(String[] args) throws IOException {
        // create an object
        CreditInfo ci = new CreditInfo("John Q. Public", "Visa",
                "111-222-333", "05/2000");

        // convert to XML, then format with two space indent
        Document doc = ci.getDocument();
        new XMLOutputter("  ", true).output(doc, System.out);
    }
}

Seguro que habrás oído algo sobre Document Object Model (DOM), el modelo de documento estándar XML de W3C. Si hemos trabajado con DOM, verás inmediatamente desde el ejemplo anterior que trabajar con JDOM es significativamente más fácil para los programadores Java. Esta es realmente la intención de JDOM. DOM se especificó usando CORBa IDL, por eso puede ser mapeado a muchos lenguajes diferentes. Desafortunadamente, el máquina Java no se aprovecha de las capacidades únicas del lenguaje Java, resultando en mucho más trabajo para los programadores.

Lo único que falta en este punto es el marco de trabajo servlet, que tiene el siguiente diseño:

Parece algo sobrecargado a primera vista, pero está casi todo lo necesario para un web site gobernado por XSLT. Todo el diseño es muy modular, por eso pueden trabajar diferentes programadores en paralelo sobre diferentes peizas.

Primero de todo, se utiliza un sólo servlet en lugar de muchos. Esto significa que sólo tenemos un punto de entrada a nuestra aplicacion, haciendo más sencilla la seguridad, el log y el despliegue. Como un sólo servlet es el responsable de toda la aplicacion, las llamadas a clases RequestHandler se usan para lógica de validación. Todo lo que es servlet hace es analizar la petición para imaginarse a qué subclase RequestHandler invocar. Esto mantiene el servlet pequeño, incluso si nuestra aplicación tiene cientos de páginas.

Hay dos aproximaciones básicas para localizar los manejadores de peticiones. Podemos añadir un parámetro adicional a la petición HTTP o añadir información de path extra. Por ejemplo, esta URL podría invocar al manejador de peticiones SubmitCustomer:

http://hostname/webapp/xsltservlet?requestHandler=SubmitCustomer

Con información de path exta, la URL parecería un poco más clara:

http://hostname/webapp/xsltservlet/SubmitCustomer

En cualquier aproximación, nuestro servlet extraerá el texto "SubmitCustomer" y lo convertirá en un nombre de clase para el manejador de solicitudes. Esto puede conseguirse mediante reflection o mediante una tabla de búsqueda. En cualquier caso, una vez que se ha localizado el objeto, él es el responsable del resto de la petición.

Entonces el objeto RequestHandler podría ejecutar métodos sobre componentes EJB que devuelva objetos de datos a la capa web. Estos objetos de datos se convierten a XML, que luego son pasados a Xalan de Apache o a otro procesador XSLT. La salida del procesador XSLT finalmente es enviada al navegador web del cliente.

Técnicas de Optimización

La principal desventaja de la aproxiamción XSLT podría ser el rendimiento en tiempo de ejecución. Cada petición al servlet requiere una transformación XSLT. Para la mayoría de las aplicaciones el rendimiento es suficientemente rápido; unas pocas técnicas de optimización pueden mejorar la situación donde se requiere la máxima potencia. Primero, las hojas de estilo XSLT son simpels ficheros XML estáticos. Puede cargarse una vez en memoria y reutilizarlos una y otra vez. Esto mejora substancialmente el rendimiento porque el XML sólo tiene que se analizado una vez.

La segunda mejora es utilizar el Document Object Model (DOM) para nuestros objetos de datos, en lugar de convertirlos primero a ficheros de texto XML. Por ejemplo, el siguiente coódigo toma la salida JDOM de la clase CreditInfo y la convierte a DOM, sin escribirla a un fichero de texto XML.

Ejemplo 6: Convertir JDOM a DOM

DOMOutputter domOutputter = new DOMOutputter();
org.w3c.dom.Document domDoc = domOutputter.output(jdomDoc);

El objeto domDoc puede pasarse a Xalan directamente, que debería analizarlo mucho más rápido que un fichero de texto XML. El siguiente ejemplo es un simple servlet de prueba que crea algo de XML usando JDOM, luego le aplica una hoja de estilos XSLT. La hoja de estilo es analizada una sóla vez y luego es almacenada en memoria, y el JDOM es convertido a un árbol DOM que se pasa directamente a Xalan. Los comentarios que hay en el código explican cada paso:

Ejemplo 7: Un Servlet de Ejemplo

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.xalan.xslt.*;
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;

public class JDomServlet extends HttpServlet {
    // reuse the same processor over and over
    private XSLTProcessor processor = XSLTProcessorFactory.getProcessor(
            new org.apache.xalan.xpath.xdom.XercesLiaison());

    // initialize the Servlet.  This code is executed once.
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        ServletContext context = config.getServletContext();

        try {
            // read the XSLT stylesheet and cache it for future reuse
            InputStream xsltStream = context.getResourceAsStream(
                    "/WEB-INF/viewCreditInfo.xslt");
            StylesheetRoot parsedStylesheet = processor.processStylesheet(
                    new XSLTInputSource(xsltStream));
            processor.setStylesheet(parsedStylesheet);
        } catch (Exception ex) {
            throw new UnavailableException(ex.toString());
        }
    }

    // handle a single request from the client
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        try {
            response.setContentType("text/html");

            // in a real app, the CreditInfo object would be retrieved
            // from an EJB component
            CreditInfo ci = new CreditInfo("John Q. Public",
                    "Visa", "111-222-333", "05/2000");

            // convert the JDOM into DOM
            Document jdomDoc = ci.getDocument();
            org.w3c.dom.Document domDoc = 
                    new DOMOutputter().output(jdomDoc);
            
            // transform the XML into HTML
            processor.process(new XSLTInputSource(domDoc),
                              null,  // use pre-compiled stylesheet
                              new XSLTResultTarget(response.getWriter()));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Sumario

La aproximación XSLT al desarrollo web tiene claras ventajas sobre las aproximaciones tradicioanles. No nos bloquea ningún API o marco de trabajo específico del vendedor. Quizás el mayor bloqueo para la amplica aceptación de XSLT es la resistencia de los programadores. Mucha gente tiene la impresión de que XSLT es simplemente muy difícil de utilizar, aunque muchas de estas opiniones están basada en una mala experiencia en lugar de la propia complejidad de XSLT. La principal razón para la complejidad percibida de XSLT es su sintaxis. que es un resultado directo de XML.

Más allá de los problemas de entrenamiento y aceptación, una aproximación XML/Java/XSLT tiene la clara ventaja de que las partes constituyentes están claramente separadas. Desde la prespectiva de un control de proyecto, esto permite a cada miembro del equivo trabajar de forma paralela, en vez de sobrecargar al guru de servlets mientras el resto del equipo se queda mirando.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR