MokaByte 50 - Marzo 2001
Foto dell'autore non disponibile
di
Andrea Giovannini
XML e pagine JSP
Come usare XML, XSL e pagine JSP per realizzare applicazioni Web indipendenti dal browser e dalla sorgente dati

Introduzione
JSP (Java Server Pages) è una tecnologia sviluppata da Sun per la realizzazione di pagine Web dinamiche in cui il contenuto del documento HTML o WML viene generato dinamicamente; la caratteristica più interessante delle pagine JSP consiste nell'incapsulare i dettagli di rendering. Nel mondo XML il linguaggio XSLT permette di trasformare documenti XML e uno dei suoi utilizzi più diffusi consiste nel trasformare i documenti in pagine da visualizzare in un browser. La possibilità di incapsulare i dettagli di rendering in uno stylesheet XSL apre la strada, potenzialmente, a qualsiasi tipo di browser o device come palmari, Web-TV e cellulari WAP.
L'unione fra le tecnologie JSP e XML/XSLT è quindi naturale: in questo articolo si approfondirà l'utilizzo delle pagine JSP nelle applicazioni J2EE vedendo come si inserisce XML nell'architettura; quindi si vedrà un esempio specifico di semplice applicazione Web con pagine JSP e trasformazioni XSL.
 
 
 

Evoluzione dell'architettura MVC
L'architettura J2EE fornisce allo sviluppatore tutti gli strumenti, architetturali e tecnologici, per la realizzazione di applicazione enterprise su Web. Più precisamente 

  • i servlet assumono il ruolo di gestori di richieste, o controller; 
  • i componenti EJB incapsulano la logica applicativa e l'accesso a database; 
  • le pagine JSP permettono di gestire gli aspetti di rendering delle pagine Web (HTML o altro). 
In questo modo è possibile realizzare un'architettura Model-View-Controller in cui modello dei dati, la loro rappresentazione e la gestione delle richieste sono completamente separati. 
Quali potrebbero essere i punti all'interno di questa architettura in cui usare XML e le tecnologie correlate ? Se ne possono individuare almeno due 
    Rappresentazione del modello dei dati. In questo modo è possibile astrarre dalla particolare sorgente dati utilizzata; questa potrebbe essere un database, un file di testo o un altra applicazione che espone informazioni in formato XML. 
    Rendering delle informazioni. Anche se i dati non sono esposti in formato XML è comunque possibile convertirli in questo formato ed eseguire il rendering mediante un'opportuna trasformazione XSL. 
E' appunto il rendering delle pagine Web che costituisce il principale campo di applicazione delle tecnologie XML e JSP, e da questo punto di vista l'introduzione di una tecnologia come XML permetta di ottenere notevoli vantaggi architetturali: si consideri ad esempio la possibilità di separare la generazione di un documento dalla sua visualizzazione. 
Come si può realizzare in concreto con tecnologia Java-based ? Si supponga di voler visualizzare su Web le informazioni contenute in un database; riprendendo l'architettura MVC si avranno 
  • un componente che legge i dati; 
  • una pagina JSP che mostra i dati, applicando una trasformazione XSL al documento ottenuto; 
  • un servlet che coordina il flusso di esecuzione; 
La prossima sezione presenta in dettaglio una semplice applicazione di Web publishing con pagine JSP e XML. 
 
 
 

Web publishing di documenti XML
Si è visto in precedenza l'architettura di un'applicazione Web J2EE; riportando l'attenzione sull'uso di XML sorge un problema: in che formato vengono esposti i dati dal componente di middleware? 
Potrebbe essere XML oppure no; si possono individuare al momento le seguenti possibilità 

  • come collezione di Javabean 
  • come documento DOM 
Nel primo caso si dovrebbe prevedere una serie di utility per il marshalling/unmarshalling in XML. Il vantaggio principale nell'utilizzo di una collezione di javabean consiste nella possibilità di eseguire un binding automatico fra il bean e la pagina JSP. 
Per la presentazione si hanno le seguenti possibilità:
  • usare i Javabean 
  • usare una trasformazione XSL 
Nel caso in cui i dati fossero esposti in formato XML e si volesse usare il binding fra bean e pagine JSP si dovrebbe provvedere in un qualche modo al popolamento della collezione di bean. Si vuole invece qui approfondire una soluzione completamente basata su XML che faccia uso di XSL per generare il documento finale. 
Come semplice case-study si implementerà un report XML di dati presenti su database. Il componente server si occuperà quindi dell'estrazione dei dati e della loro conversione in documento DOM. Per semplicità il componente che legge i dati per questo esempio non sarà implementato come EJB ma come semplice classe Java ; non verrà inoltre implementato alcun servlet per gestire la logica di interazione sempre per mantenere l'esempio semplice.

L'applicazione mostra i dati di una tabella che mantiene memorizzate le informazioni su alcuni libri come codice, autore e titolo. Per visualizzare il report è necessario eseguire un logon all'applicazione mediante una pagina JSP con associato un relativo bean. Il rendering avviene mediante una trasformazione XSL avendo come input un documento DOM ritornato dal bean. Per le trasformazioni XSL si userà Xalan, l'engine dell'XML Apache Group. 

Ecco Segue ora il codice del bean LogonForm

public class LogonForm {
  // user name
  private String usrName;

  // password
  private String password;

  // flag che indica se il logon non è 
  // stato eseguito correttamente
  private boolean logonFailed;

  public LogonForm(){
    usrName = "";
    password = "";
    logonFailed= false;
  }

  public String getUsrName(){
    return usrName;
  }

  public void setUsrName(String usrName) { 
   this.usrName = usrName;
  }

  public String getPassword(){
    return password;
  }

  public void setPassword(String password){
    this.password = password;
  }

  /**
  * Questo metodo controlla se le informazioni inserite 
  * sono corrette.
  * Per semplicità user name e password sono cablate 
  * nel codice; tutti i dettagli di una reale 
  * implementazione sarebbero comunque incapsulati
  * in questo metodo.
  *
  * @return boolean  true se l'utente è autorizzato, 
  *                  false altrimenti.
  */
  public boolean process() {
    if ((usrName.trim().equals("moka")) && 
            (password.trim().equals("moka"))) {
      return true;
    }
    else {
      logonFailed = true;
      return false;
    }
  }

  /**
  * Questo metodo genera un documento DOM corrispondente al bean.
  *
  * @return Document il documento DOM corrispondente a 
                     questo bean.
  */
  public Document toXML() {
    try {
      DocumentBuilderFactory dbf;
      dbf=DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = dbf.newDocumentBuilder();
      Document doc = builder.newDocument();

      Element root = doc.createElement("LOGON_FORM"),
      usrNameEl = doc.createElement("USR_NAME"),
      passwordEl = doc.createElement("PASSWORD");
      usrNameEl.appendChild(doc.createTextNode(usrName));
      passwordEl.appendChild(doc.createTextNode(password));

      root.appendChild(usrNameEl);
      root.appendChild(passwordEl);

      if (logonFailed) {
        Element errorEl = doc.createElement("ERROR");
        errorEl.appendChild(doc.createTextNode("Logon failed"));
        root.appendChild(errorEl);
     }

     doc.appendChild(root);
     return doc;
    }
    catch (Exception e) {
      return null;
    }
  }
}

Oltre alle normali property per user name e password la classe precedente contiene il metodo process() per l'autenticazione (per semplicità cablata nel codice) e il più interessante metodo toXML() che ritorna un documento DOM con le informazioni della form. 
Per ottenere un'istanza del parser XML si usa JAXP, la API Sun per l'elaborazione dei documenti XML; in questo modo non si ha alcun riferimento specifico al particolare parser utilizzato (che in questo caso è Xerces di Apache). Il documento DOM viene quindi costruito usando la API standard DOM appendendo i campi con le informazioni di logon e sull'eventuale errore verificatosi.
Segue ora il codice della pagina JSP

<%@ page language="java" %>
<%@ page import="org.apache.trax.*" %>
<%@ page import="org.xml.sax.InputSource" %>

<jsp:useBean id="logon" scope="page" class="LogonForm">
    <jsp:setProperty name="logon" property="*"/>
</jsp:useBean>

<%
    if (logon.getUsrName() != "" && logon.process()) {
        // l'utente ha effettuato un corretto login
        response.sendRedirect("books.jsp");
    }

    Processor processor = Processor.newInstance("xslt");
    Templates templates = processor.process(new 
                    InputSource("http://" + 
                    request.getServerName() + 
                    ":" + request.getServerPort() +
                    request.getContextPath() + 
                    "/logon.xsl"));
    Transformer transformer = templates.newTransformer();
    transformer.transformNode(logon.toXML(), new Result(out));
%>

La pagina precedente, oltre ai tag per il binding con il bean, contiene anche la logica per la gestione delle richieste in quanto i dati, una volta inseriti, vengono inviati con un POST alla stessa pagina. 
L' esempio è abbastanza semplice e la logica è altrettanto semplice: se i dati inseriti sono corretti il browser viene rediretto alla pagina con il report, altrimenti viene ripresentata la pagina di logon. In quest'ultimo caso il documento XML corrispondente avrà anche un elemento di errore e questo verrà visualizzato dalla pagina. Si osservi che per semplicità il codice che esegue la trasformazione XSL è stato inserito nella pagina; in un'applicazione più ingegnerizzata si sarebbe potuto realizzare un'apposito tag JSP. 
Un'altro aspetto importante è relativo allo stylesheet XSL: in questo caso si usa un unico file XSL che trasforma i dati in HTML mentre un'applicazione realmente multi-device avrebbe previsto l'utilizzo di diversi stylesheet in funzione del client.
Per completare il quadro questo è lo stylesheet XSL usato per la form di logon

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <HTML>
        <HEAD>
               <TITLE>The Books demo - logon</TITLE>
        </HEAD>
 

        <BODY STYLE="{font-family:verdana; font-size:16px; font-style:normal; color:black;}">
            <xsl:apply-templates select="LOGON_FORM"/>
        </BODY>

        </HTML>
    </xsl:template>

    <xsl:template match="LOGON_FORM">
        <xsl:apply-templates select="ERROR"/>
            <FORM name="logon" id="logon" method="POST" action="logon.jsp">
            <TABLE border="0" cellpadding="0" cellspacing="0" align="center">
                <TR><TD align="center">User name</TD></TR>
                <TR><TD><INPUT type="text" name="usrName" id="usrName">
                    <xsl:attribute name="value">
                        <xsl:value-of select="USR_NAME"/>
                    </xsl:attribute>
                 </INPUT></TD></TR>
                 <TR><TD align="center">Password</TD></TR>
                 <TR><TD><INPUT type="password" name="password" id="password">
                    <xsl:attribute name="value">
                        <xsl:value-of select="PASSWORD"/>
                    </xsl:attribute>
                 </INPUT></TD></TR>
                 <BR/>
                 <TR><TD align="center"><INPUT type="submit" value="Submit"/></TD></TR>
            </TABLE>
            </FORM>
    </xsl:template>

    <xsl:template match="ERROR">
        <center><font color="red"><xsl:value-of select="."/></font></center>
    </xsl:template>
</xsl:stylesheet>

Lo stylesheet XSL formatta una semplice form HTML mostrando l'eventuale messaggio d'errore nel caso in cui il logon non sia stato eseguito con successo.

I dati per il report vengono prelevati da una classe che li espone come documento DOM usando codice analogo a quello visto in precedenza: la classe si chiama Books e il suo metodo query() incapsula questo procedimento

    public Document query() throws Exception {

        Connection c = null;
        Statement stm = null;
        ResultSet rs = null;
        Document result = null;

        try {
            // carica il driver JDBC
            Class.forName(jdbcDriver).newInstance();
        } catch (Exception e) {
            System.err.println("Unable to load driver");
            return null;
        }

        try {
            // crea il parser XML
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dbf.newDocumentBuilder();
            result = builder.newDocument();
            Element books = result.createElement("BOOKS");

            c = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
            stm = c.createStatement();
            rs = stm.executeQuery("SELECT code, author, title FROM books");

            while (rs.next()) {
                // per ogni record crea un corrispondente elemento DOM
                Element book = result.createElement("BOOK"),
                    code = result.createElement("CODE"),
                    author = result.createElement("AUTHOR"),
                    title = result.createElement("TITLE");

                code.appendChild(result.createTextNode(rs.getString("code")));
author.appendChild(result.createTextNode(rs.getString("author")));
title.appendChild(result.createTextNode(rs.getString("title")));

                book.appendChild(code);
                book.appendChild(author);
                book.appendChild(title);

                books.appendChild(book);
            }

                result.appendChild(books);
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println("Exception reading and 
                                    formatting data");
                throw e;
            } finally {
                try {
                    // clean up everything
                    if (rs != null) { rs.close(); }
                    if (stm != null) { stm.close(); }
                    if (c != null) { c.close(); }
                } catch (SQLException sqle) {}
            }
            return result;
    }

Il codice precedente esegue una query su database, preleva i dati dal resultset e costruisce un documento DOM. Si osservi che l'accesso diretto a database potrebbe essere sostituito dall'utilizzo di appositi oggetti, ad esempio EJB.

La pagina JSP riceverà come input il documento restituito dal metodo precedente e genererà un documento HTML ancora attraverso una trasformazione XSL. Segue ora il codice della pagina

<%@ page language="java" %>
<%@ page import="org.apache.trax.*" %>
<%@ page import="org.xml.sax.InputSource" %>

<jsp:useBean id="books" scope="page" class="Books">
    <jsp:setProperty name="books" property="jdbcDriver" 
        value="org.enhydra.instantdb.jdbc.idbDriver"/>
    <jsp:setProperty name="books" property="jdbcUrl" value="jdbc:idb=sample.prp"/>
    <jsp:setProperty name="books" property="jdbcUser" value="sa"/>
    <jsp:setProperty name="books" property="jdbcPassword" value=""/>
</jsp:useBean>

<%
    Processor processor = Processor.newInstance("xslt");
    Templates templates = processor.process(new InputSource("http://" + 
        request.getServerName() + ":" + request.getServerPort() +
        request.getContextPath() + "/books.xsl"));

    Transformer transformer = templates.newTransformer();
    transformer.transformNode(books.query(), new Result(out));
%>
La classe Books è un bean ed espone una serie di property per la configurazione dell'accesso a database; in questo caso è stato usato InstantDB. Segue ora il codice dello stylesheet utilizzato per generare la pagina HTML 
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <HTML>
        <HEAD><TITLE>Books report</TITLE></HEAD>
        <BODY>
            <xsl:apply-templates select="BOOKS"/>
        </BODY>
        </HTML>
    </xsl:template>

    <xsl:template match="BOOKS">
        <HR/>
        <TABLE width="100%">
        <TH>Code</TH><TH>Author</TH><TH>Title</TH>

        <xsl:for-each select="BOOK">
            <xsl:apply-templates select="."/>
        </xsl:for-each>
        </TABLE>
        <HR/>
    </xsl:template>

    <xsl:template match="BOOK">
        <TR>
        <TD><xsl:value-of select="CODE"/></TD>
        <TD><xsl:value-of select="AUTHOR"/></TD>
        <TD><xsl:value-of select="TITLE"/></TD>
        </TR>
    </xsl:template>

</xsl:stylesheet>

Lo stylesheet è molto semplice e converte il documento in una tabella HTML. In questo esempio non ci si è preoccupati del look'n feel delle pagine HTML; le varie informazioni di rendering (font, dimensioni, colori, ...) andrebbero comunque specificate in un file CSS come per le normali pagine Web.

Conclusioni
XML, fra i suoi vari utilizzi, è particolarmente utile per il Web publishing e il suo utilizzo con le pagine JSP è quindi una tappa obbligata per sviluppare applicazioni Web. In questo articolo si è visto solo un semplice esempio di reporting ma comunque significativo. La librerie e i framework esistenti sono comunque in continua evoluzione e anche l'architettura J2EE sta integrando XML; ad esempio l'ultimo draft presentato per la versione 1.2 di JSP prevede che una pagina JSP sia un documento XML descritto da un apposito schema. XML è inoltre alla base della nuova iniziativa ONE presentata recentemente da Sun.

I sorgenti presentati nell'articolo sono disponibili qui.
 

 

Vai alla Home Page di MokaByte
Vai alla prima pagina di questo mese


MokaByte®  è un marchio registrato da MokaByte s.r.l.
Java®, Jini®  e tutti i nomi derivati sono marchi registrati da Sun Microsystems; tutti i diritti riservati
E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a
mokainfo@mokabyte.it