Skip to content

6. XML e Java

In questo capitolo, introduciamo l'uso dei documenti XML con Java. Lo faremo nel contesto dell'applicazione fiscale studiata nel capitolo precedente.

6.1. File XML e fogli di stile XSL

Consideriamo il seguente file XML, simulations.xml, che potrebbe rappresentare i risultati delle simulazioni di calcolo delle imposte:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Se visualizzato con IE 6, si ottiene il seguente risultato:

Image

IE6 riconosce che si tratta di un file XML (grazie all'estensione .xml del file) e lo formatta a modo suo. Con Netscape, si ottiene una pagina vuota. Tuttavia, se si guarda il codice sorgente (Visualizza/Sorgente), è possibile vedere il file XML originale:

Image

Perché Netscape non visualizza nulla? Perché ha bisogno di un foglio di stile che gli indichi come trasformare il file XML in un file HTML che possa poi visualizzare. A quanto pare, IE 6 dispone di un foglio di stile predefinito quando il file XML non ne fornisce uno, come nel caso in questione.

Esiste un linguaggio chiamato XSL (eXtended StyleSheet Language) che consente di descrivere le trasformazioni necessarie per convertire un file XML in qualsiasi file di testo. XSL supporta numerose istruzioni e assomiglia molto ai linguaggi di programmazione. Non entreremo nei dettagli qui, poiché ciò richiederebbe decine di pagine. Descriveremo semplicemente due esempi di fogli di stile XSL. Il primo è quello che trasformerà il file XML simulations.xml in codice HTML. Modifichiamo quest'ultimo in modo che specifichi il foglio di stile che i browser possono utilizzare per trasformarlo in un documento HTML, che potranno poi visualizzare:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Il comando XML

<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>

indica il file simulations.xsl come foglio di stile XML di tipo text/xsl, ovvero un file di testo contenente codice XSL. Questo foglio di stile verrà utilizzato dai browser per trasformare il testo XML in un documento HTML. Ecco il risultato ottenuto con Netscape 7 durante il caricamento del file XML simulations.xml:

Image

Quando visualizziamo il codice sorgente del documento (Visualizza/Sorgente), vediamo il documento XML originale anziché il documento HTML visualizzato:

Image

Netscape ha utilizzato il foglio di stile simulations.xsl per trasformare il documento XML sopra riportato in un documento HTML visualizzabile. È ora il momento di esaminare il contenuto di questo foglio di stile:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>
  • Un foglio di stile XSL è un file XML e quindi segue le regole XML. Tra le altre cose, deve essere "ben formato", il che significa che ogni tag di apertura deve essere chiuso.
  • Il file inizia con due direttive XML che possono essere incluse in qualsiasi foglio di stile XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

L'attributo encoding="ISO-8859-1" consente l'uso di caratteri accentati nel foglio di stile.

  • Il tag <xsl:output method="html" indent="yes"/> indica all'interprete XSL che si desidera generare codice HTML "indentato".
  • Il tag <xsl:template match="element"> viene utilizzato per definire l'elemento nel documento XML a cui verranno applicate le istruzioni presenti tra <xsl:template ...> e </xsl:template>.
    <xsl:template match="/">
................
        </xsl:template>

Nell'esempio sopra riportato, l'elemento "/" indica la radice del documento. Ciò significa che non appena viene individuato l'inizio del documento XML, verranno eseguiti i comandi XSL situati tra i due tag.

  • Tutto ciò che non è un tag XSL viene incluso così com'è nel flusso di output. I tag XSL stessi vengono eseguiti. Alcuni di essi producono un risultato che viene incluso nel flusso di output. Esaminiamo il seguente esempio:
    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
          </center>
        </body>
      </html>
        </xsl:template>

Si noti che il documento XML analizzato è il seguente:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

A partire dall'inizio del documento XML analizzato (match="/"), il processore XSL genererà il testo

<html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>

Si noti che nel testo originale avevamo <hr/> e non <hr>. Nel testo originale non potevamo scrivere <hr>, che, pur essendo un tag HTML valido, è un tag XML non valido. Tuttavia, in questo caso abbiamo a che fare con un testo XML che deve essere "ben formato", il che significa che ogni tag deve essere chiuso. Scriviamo quindi <hr/> e, poiché abbiamo scritto <xsl:output text="html ...> il processore XSL trasformerà il testo <hr/> in <hr>. A seguire questo testo ci sarà il testo prodotto dal comando XSL:

      <xsl:apply-templates select="/simulations/simulation"/>

Vedremo più avanti di cosa si tratta. Infine, l'interprete aggiungerà il testo:

                    </table>
          </center>
        </body>
      </html>

La direttiva <xsl:apply-templates select="/simulations/simulation"/> indica al processore XSL di applicare il "modello" all'elemento /simulations/simulation. Verrà eseguita ogni volta che l'interprete XSL incontra un tag <simulation>..</simulations> o <simulation/> all'interno di un tag <simulations>..</simulations> nel testo XML analizzato. Quando incontra il tag <simulation>, l'interprete eseguirà le istruzioni del seguente modello:

    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>

Si considerino le seguenti righe XML:

<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

La riga <simulation ..> corrisponde al modello per l'istruzione XSL <xsl:apply-templates select="/simulations/simulation">. L'interprete XSL tenterà quindi di applicare le istruzioni che corrispondono a questo modello. Troverà il modello <xsl:template match="simulation"> e lo eseguirà. Ricordiamo che tutto ciò che non è un comando XSL viene trasmesso senza modifiche dall'interprete XSL, mentre i comandi XSL vengono sostituiti dal risultato della loro esecuzione. L'istruzione XSL <xsl:value-of select="@champ"/> viene quindi sostituita dal valore dell'attributo "champ" del nodo analizzato (in questo caso, un nodo <simulation>). L'analisi della riga XML precedente produrrà il seguente output:

XSL
output
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
</td><td>
</td><td>
<xsl:value-of select="@children"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salary"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@tax"/>
22504
</td></tr>
</td></tr>

In totale, la riga XML

<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

verrà convertito nella seguente riga HTML:

<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>

Tutte queste spiegazioni sono un po' rudimentali, ma ora dovrebbe essere chiaro al lettore che il seguente testo XML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

accompagnato dal seguente foglio di stile XSL simulations.xsl:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>

genera il seguente testo HTML:

<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<tr>
<td>oui</td><td>2</td><td>200000</td><td>22504</td>
</tr>
<tr>
<td>non</td><td>2</td><td>200000</td><td>33388</td>
</tr>
</table>
</center>
</body>
</html>

Il file XML simulations.xml, insieme al foglio di stile simulations.xsl, se visualizzato in un browser moderno (in questo caso, Netscape 7), viene visualizzato come segue:

Image

6.2. Applicazione per il calcolo delle imposte: versione 6

6.2.1. I file XML e i fogli di stile XSL dell'applicazione per il calcolo delle imposte

Torniamo all'applicazione web per il calcolo delle imposte e modifichiamola in modo che la risposta inviata ai client sia in formato XML anziché HTML. Questa risposta XML sarà accompagnata da un foglio di stile XSL affinché i browser possano visualizzarla. Nella sezione precedente abbiamo presentato:

  • il file simulations.xml, che è un prototipo di una risposta XML contenente simulazioni di calcolo delle imposte
  • il file simulations.xsl, che sarà il foglio di stile XSL che accompagna questa risposta XML

Dobbiamo anche considerare il caso di una risposta contenente errori. Il prototipo per la risposta XML in questo caso sarà il seguente file errors.xml:

<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Il foglio di stile errors.xsl utilizzato per visualizzare questo documento XML in un browser sarà il seguente:

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

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
          </center>            
          <hr/>
          Les erreurs suivantes se sont produites :
          <ul>
                  <xsl:apply-templates select="/erreurs/erreur"/>
              </ul>            
        </body>
      </html>
        </xsl:template>

    <xsl:template match="erreur">
          <li><xsl:value-of select="."/></li>
        </xsl:template>
</xsl:stylesheet>    

Questo foglio di stile introduce un comando XSL che non abbiamo ancora incontrato: <xsl:value-of select="."/>. Questo comando restituisce il valore del nodo analizzato, in questo caso un nodo <error>text</error>. Il valore di questo nodo è il testo compreso tra i tag di apertura e di chiusura, in questo caso "text".

Il codice errors.xml viene trasformato dal foglio di stile errors.xsl nel seguente documento HTML:

<html>
<head>
<title>Simulations de calculs d'impots</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
</center>
<hr>
          Les erreurs suivantes se sont produites :
<ul>
<li>erreur 1</li>
<li>erreur 2</li>
</ul>
</body>
</html>

Il file errors.xml, insieme al suo foglio di stile, viene visualizzato dal browser come segue:

Image

6.2.2. Il servlet xmlsimulations

Creiamo un file index.html e lo inseriamo nella directory dell'applicazione impots. La pagina visualizzata è la seguente:

Image

Questo documento HTML è un documento statico. Il suo codice è il seguente:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
          // raz du formulaire
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//with
      }//effacer

      function calculer(){
          // vérification des paramètres avant de les envoyer au serveur
        with(document.frmImpots){
          //nbre d'enfants
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // le modéle n'est pas vérifié
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//if
          //salaire
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // le modéle n'est pas vérifié
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//if
          // c'est bon - on envoie
          submit();
        }//with
      }//calculer        
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/xmlsimulations" method="POST">
          <table>
            <tr>
              <td>Etes-vous marié(e)</td>
            <td>
                <input type="radio" name="optMarie" value="oui">oui
              <input type="radio" name="optMarie" value="non" checked>non
            </td>
          </tr>
          <tr>
              <td>Nombre d'enfants</td>
            <td><input type="text" size="3" name="txtEnfants" value=""></td>
          </tr>
          <tr>
              <td>Salaire annuel</td>
            <td><input type="text" size="10" name="txtSalaire" value=""></td>
          </tr>
          <tr></tr>
          <tr>
              <td><input type="button" value="Calculer" onclick="calculer()"></td>
            <td><input type="button" value="Effacer" onclick="effacer()"></td>
          </tr>
        </table>
      </form>
    </center>
 </body>
</html>

Si noti che i dati del modulo vengono inviati all'URL /impots/xmlsimulations. Questa applicazione è un servlet Java configurato come segue nel file web.xml dell'applicazione impots:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
...........
    <servlet>
      <servlet-name>xmlsimulations</servlet-name>
    <servlet-class>xmlsimulations</servlet-class>
    <init-param>
          <param-name>xslSimulations</param-name>
        <param-value>simulations.xsl</param-value>
    </init-param>
    <init-param>
          <param-name>xslErreurs</param-name>
        <param-value>erreurs.xsl</param-value>
    </init-param>

    <init-param>
          <param-name>DSNimpots</param-name>
        <param-value>mysql-dbimpots</param-value>
    </init-param>
    <init-param>
          <param-name>admimpots</param-name>
        <param-value>admimpots</param-value>
    </init-param>
    <init-param>
          <param-name>mdpimpots</param-name>
        <param-value>mdpimpots</param-value>
    </init-param>    
  </servlet>
........
  <servlet-mapping>
      <servlet-name>xmlsimulations</servlet-name>
    <url-pattern>/xmlsimulations</url-pattern>
  </servlet-mapping>
</web-app>
  • Il servlet si chiama xmlsimulations ed è basato su xmlsimulations.class.
  • I suoi parametri sono DSNimpots, admimpots e mdpimpots, necessari per accedere al database fiscale. Inoltre, accetta altri due parametri:
    • xslSimulations, che è il nome del file del foglio di stile che deve accompagnare la risposta XML contenente le simulazioni
    • xslErrors, che è il nome del foglio di stile che deve accompagnare la risposta XML contenente eventuali errori
  • Ha un alias, xmlsimulations, che lo rende accessibile tramite l'URL http://localhost:8080/impots/xmlsimulations.

Lo scheletro del servlet xmlsimulations è simile a quello del servlet simulations già discusso. La differenza principale è che deve generare XML invece di HTML. Ciò comporterà la rimozione dei file JSP utilizzati nelle applicazioni precedenti. Il loro ruolo principale era quello di migliorare la leggibilità del codice HTML generato, evitando che fosse sepolto all'interno del codice Java del servlet. Questo ruolo non è più necessario. Il servlet ha due tipi di codice XML da generare:

  • uno per le simulazioni
  • uno per gli errori

In precedenza abbiamo presentato ed esaminato i due tipi di risposte XML da fornire in questi due casi, nonché i fogli di stile che devono accompagnarli. Il codice del servlet è il seguente:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;

public class xmlsimulations extends HttpServlet{

    // instance variables
    String msgErreur=null;
    String xslSimulations=null;
    String xslErreurs=null;
    String DSNimpots=null;
    String admimpots=null;
    String mdpimpots=null;
    impotsJDBC impots=null;

    //-------- GET
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

        // retrieve the write stream to the client
        PrintWriter out=response.getWriter();

        // specify the type of response
        response.setContentType("text/xml");

         // error list
        ArrayList erreurs=new ArrayList();

         // was the initialization successful?
        if(msgErreur!=null){
             // that's it - we send the response with errors to the server
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // it's over
            return;
        }

         // retrieve previous simulations from the session
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // retrieve the parameters of the current query
        String optMarie=request.getParameter("optMarie");              // marital status
        String txtEnfants=request.getParameter("txtEnfants");       // no. of children
        String txtSalaire=request.getParameter("txtSalaire");       // annual salary

         // do we have all the expected parameters
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // missing parameters
             // send response with errors
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // it's over
            return;
        }

         // we have all the parameters - we check them
         // marital status
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // error
            erreurs.add("Etat marital incorrect");
        }
         // number of children
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // error
            erreurs.add("Nombre d'enfants incorrect");
        }
         // salary
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // error
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // if there are errors, we report them
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // no errors
            try{
                 // you can calculate the tax payable
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // the current result is added to the previous simulations
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // we send the answer with simulations
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // we put the list of simulations back into the session
        session.setAttribute("simulations",simulations);
    }//GET

     //-------- POST
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{
        doGet(request,response);
    }//POST

     //-------- INIT
    public void init(){
         // retrieve initialization parameters
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

         // parameters ok?
        if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
            msgErreur="Configuration incorrecte";
            return;
        }

         // create an instance of impotsJDBC
        try{
            impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
        }catch(Exception ex){
            msgErreur=ex.getMessage();
        }
    }//init

     //-------- sendErreurs
    private void sendErreurs(PrintWriter out,String xslErreurs,ArrayList erreurs){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslErreurs+"\"?>\n"
              +"<erreurs>\n";
        for(int i=0;i<erreurs.size();i++){
            réponse+="<erreur>"+(String)erreurs.get(i)+"</erreur>\n";
        }//for
        réponse+="</erreurs>\n";
         // we send the answer
        out.println(réponse);
    }

     //-------- sendSimulations
    private void sendSimulations(PrintWriter out, String xslSimulations, ArrayList simulations){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslSimulations+"\"?>\n"
              + "<simulations>\n";
        String[] simulation=null;
        for(int i=0;i<simulations.size();i++){
            // simulation no. i
            simulation=(String[])simulations.get(i);
            réponse+="<simulation "
                     +"marie=\""+(String)simulation[0]+"\" "
                     +"enfants=\""+(String)simulation[1]+"\" "
                     +"salaire=\""+(String)simulation[2]+"\" "
                 +"impot=\""+(String)simulation[3]+"\" />\n";
        }//for
        réponse+="</simulations>\n";
         // we send the answer
        out.println(réponse);
    }
}

Analizziamo le principali novità di questo codice rispetto a ciò che già conoscevamo:

  • La procedura init recupera nuovi parametri dal file di configurazione web.xml: i nomi dei due fogli di stile XSL che devono accompagnare la risposta sono memorizzati nelle variabili xslSimulations e xslErrors. Questi due fogli di stile sono i file simulations.xsl ed errors.xsl discussi in precedenza. Si trovano nella directory dell'applicazione impots:
dos>dir E:\data\serge\Servlets\impots\*.xsl
27/08/2002  08:15                1 030 simulations.xsl
27/08/2002  09:23                  795 erreurs.xsl
  • La procedura GET inizia verificando se si è verificato un errore durante l'inizializzazione. In tal caso, chiama la procedura sendErrors, che genera la risposta XML appropriata per quel caso e poi termina. La risposta XML include un tag che specifica il foglio di stile da utilizzare.
  • Se non si sono verificati errori, la procedura GET analizza i parametri della richiesta del client. Se rileva un errore, lo segnala utilizzando la procedura sendErrors. Altrimenti, calcola la nuova simulazione, la aggiunge a quelle precedenti memorizzate nella sessione corrente e termina inviando la sua risposta XML tramite la procedura sendSimulations. Quest'ultima procede in modo analogo alla procedura sendErrors.
  • Si noti che il servlet dichiara la propria risposta come di tipo text/xml:
        // on précise le type de la réponse
        response.setContentType("text/xml");

Ecco alcuni esempi di esecuzione. Il modulo iniziale viene compilato come segue:

Image

Il database MySQL non è stato avviato, rendendo impossibile la creazione dell'oggetto impots nella procedura init del servlet. La risposta del servlet è quindi la seguente:

Image

Il codice ricevuto dal browser (Visualizza/Sorgente) è il seguente:

Image

Se ora eseguiamo altre due simulazioni dopo aver avviato il database MySQL, otteniamo il seguente risultato:

Image

Questa volta, il browser ha ricevuto il seguente codice:

Image

Si noti che la nostra nuova applicazione è più semplice rispetto a prima grazie alla rimozione dei file JSP. Parte del lavoro precedentemente svolto da queste pagine è stato trasferito ai fogli di stile XSL. Il vantaggio della nostra nuova divisione dei compiti è che, una volta stabilito il formato XML delle risposte del servlet, lo sviluppo dei fogli di stile è indipendente da quello del servlet.

6.3. Analisi di un documento XML in Java

Le versioni 7 e 8 della nostra applicazione impots saranno client programmati per il precedente servlet xmlsimulations. Questi client riceveranno codice XML che dovranno analizzare per estrarre le informazioni di cui hanno bisogno. Ora faremo una pausa dalle nostre varie versioni per imparare come analizzare un documento XML in Java. Lo faremo utilizzando un esempio incluso in JBuilder 7 chiamato MySaxParser. Il programma viene chiamato come segue:

dos>java MySaxParser
Usage: java MySaxParser [URI]

L'applicazione MySaxParser accetta un solo parametro: l'URI (Uniform Resource Identifier) del documento XML da analizzare. Nel nostro esempio, questo URI sarà semplicemente il nome di un file XML situato nella directory dell'applicazione MySaxParser. Consideriamo due esempi di esecuzione. Nel primo esempio, il file XML da analizzare è errors.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

L'analisi produce i seguenti risultati:

dos> java MySaxParser erreurs.xml
Début du document
Début élément <erreurs>
Début élément <erreur>
[erreur 1]
Fin élément <erreur>
Début élément <erreur>
[erreur 2]
Fin élément <erreur>
Fin élément <erreurs>
Fin du document

Non abbiamo ancora spiegato cosa fa l'applicazione MySaxParser, ma qui possiamo vedere che visualizza la struttura del documento XML analizzato. Il secondo esempio analizza il file XML simulations.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

L'analisi fornisce i seguenti risultati:

dos>java MySaxParser simulations.xml
Début du document
Début élément <simulations>
Début élément <simulation>
marie = oui
enfants = 2
salaire = 200000
impot = 22504
Fin élément <simulation>
Début élément <simulation>
marie = non
enfants = 2
salaire = 200000
impot = 33388
Fin élément <simulation>
Fin élément <simulations>
Fin du document

La classe MySaxParser contiene tutto ciò di cui abbiamo bisogno nella nostra applicazione fiscale, poiché è stata in grado di recuperare sia gli errori che le simulazioni che il server web potrebbe inviare. Esaminiamo il suo codice:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;

// the class
public class MySaxParser extends DefaultHandler {

     // value of a tree element XML
    private StringBuffer valeur=new StringBuffer();
    // a regular expression of an element's value when you want to ignore
     // the "blanks" that precede or follow it
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- hand
    public static void main(String[] argv) {
        // check number of parameters
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // retrieve the URI from the XML file to be analyzed
        String uri = argv[0];
        try {
            // creation of a XML analyzer (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // we indicate to the parser the object that will implement the methods
             // startDocument, endDocument, startElement, endElement, characters
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // initialize an element's value model
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // we indicate to the parser the XML document to be analyzed
            parser.parse(uri);
        }
        catch(Exception ex) {
             // error
            System.err.println("Erreur : " + ex);
             // trace
            ex.printStackTrace();
        }
    }//hand


     // -------- startDocument
    public void startDocument() throws SAXException {
         // procedure called when the parser encounters the start of the document
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // procedure called when the parser reaches the end of the document

        System.out.println("Fin du document");
    }//endDocument

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedure called by the parser when it encounters the start of a tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one
         // attributes: list of element attributes

         // follow-up
        System.out.println("Début élément <"+localName+">");
         // does the element have attributes?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//for
    }//startElement

    // -------- characters
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedure called repeatedly by the parser when it encounters text
         // between two <tag>text</tag> tags
         // the text is in ch from the start character on length characters

         // the text is added to the value buffer
        valeur.append(ch, start, length);
    }//characters

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedure called by the parser when it encounters an end of tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one

         // the value of the
        String strValeur=valeur.toString();
        if (ptnValeur==null) System.out.println("null");
        résultats=ptnValeur.matcher(strValeur);
        if (résultats.find() && ! résultats.group(1).equals("")){
            System.out.println("["+résultats.group(1)+"]");
        }//if
         // set element value to empty
        valeur.setLength(0);

         // follow-up
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//class

Per prima cosa, definiamo un acronimo che compare spesso nell'analisi dei documenti XML: SAX, che sta per Simple API for XML. Si tratta di un insieme di classi Java che facilitano il lavoro con i documenti XML. Esistono due versioni dell'API: SAX1 e SAX2. L'applicazione sopra riportata utilizza l'API SAX2.

L'applicazione importa una serie di pacchetti:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;

I primi due sono inclusi nel JDK 1.4, ma il terzo no. Il pacchetto xerces.jar è disponibile sul sito web di Apache Web Server. È incluso in JBuilder 7 e in Tomcat 4.x:

Image

Quindi, se si desidera compilare l'applicazione precedente al di fuori di JBuilder 7 e si dispone di JDK 1.4 e Tomcat 4.x, è possibile scrivere:

dos>javac -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser.java

Quando si esegue l'applicazione, procedere allo stesso modo:

dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml

La classe MySaxParser estende la classe DefaultHandler. Torneremo su questo punto più avanti. Esaminiamo il codice della procedura principale:

         // retrieve the URI from the XML file to be analyzed
        String uri = argv[0];
        try {
            // creation of a XML analyzer (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // we indicate to the parser the object that will implement the methods
             // startDocument, endDocument, startElement, endElement, characters
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // initialize an element's value model
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // we indicate to the parser the XML document to be analyzed
            parser.parse(uri);
        }
        catch(Exception ex) {
             // error
            System.err.println("Erreur : " + ex);
             // trace
            ex.printStackTrace();
        }

Per analizzare un documento XML, la nostra applicazione necessita di un parser di codice XML.

            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

Il parser XML utilizzato è quello fornito dal pacchetto xerces.jar. L'oggetto restituito è di tipo XMLReader. XMLReader è un'interfaccia e qui utilizziamo due dei suoi metodi:

void setContentHandler(ContentHandler handler)
indica al parser quale oggetto ContentHandler gestirà gli eventi generati durante l'analisi del documento XML
void parse(InputSource input)
avvia l'analisi del documento XML passato come parametro

Quando il parser analizza il documento XML, emette eventi quali: "Ho incontrato l'inizio del documento, l'inizio di un tag, un attributo di tag, il contenuto di un tag, la fine di un tag, la fine del documento, ...". Passa questi eventi all'oggetto ContentHandler che gli è stato fornito. ContentHandler è un'interfaccia che definisce i metodi da implementare per gestire tutti gli eventi che il parser XML può generare. DefaultHandler è una classe che fornisce un'implementazione predefinita di questi metodi. I metodi implementati in DefaultHandler non fanno nulla, ma esistono. Quando dobbiamo indicare al parser quale oggetto gestirà gli eventi che genera utilizzando l'istruzione

void setContentHandler(ContentHandler handler)

è conveniente passare un oggetto di tipo DefaultHandler come parametro. Se ci fermassimo qui, nessun evento del parser verrebbe gestito, ma il nostro programma sarebbe sintatticamente corretto. In pratica, passiamo al parser come parametro un oggetto derivato dalla classe DefaultHandler, in cui vengono ridefiniti solo i metodi che gestiscono gli eventi che ci interessano. Questo è ciò che viene fatto qui:

             // we indicate to the parser the object that will implement the methods
             // startDocument, endDocument, startElement, endElement, characters
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // we indicate to the parser the XML document to be analyzed
            parser.parse(uri);

Passiamo al parser un'istanza della classe mySaxParser, che è la nostra classe ed è stata definita in precedenza dalla dichiarazione

public class MySaxParser extends DefaultHandler {

e iniziamo ad analizzare il documento il cui URI è stato passato come parametro. Da lì, inizia l'analisi del documento XML. Il parser emette eventi e, per ciascuno di essi, chiama un metodo specifico dell'oggetto responsabile della gestione di questi eventi — in questo caso, il nostro oggetto MySaxParser. Questo oggetto gestisce cinque eventi specifici; gli altri vengono ignorati:

evento emesso dal parser
metodo di gestione
inizio del documento
void startDocument()
fine del documento
void fineDocumento()
inizio di un elemento: <tag>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: nome dell'elemento analizzato. Se l'elemento incontrato è <simulations>, localName sarà "simulations".
qName: nome qualificato con namespace dell'elemento analizzato. Un documento XML può definire un namespace, come ad esempio XX. Il nome qualificato del tag precedente sarebbe quindi XX:simulations.
attributes: elenco degli attributi del tag
valore di un elemento:
<tag>valore</tag>
public void characters(char[] ch, int start, int length)
ch: array di caratteri
start: indice del primo carattere da utilizzare nell'array ch
length: numero di caratteri da prelevare dall'array ch
Il metodo characters può essere chiamato più volte. Per costruire il valore di un elemento, usiamo un buffer che:
  • inizialmente vuoto
  • riempiamo ad ogni nuova chiamata al metodo characters
  • terminiamo alla fine dell'elemento
Fine di un elemento: </tag> o <tag .../>
void endElement(String uri, String localName, String qName)
I parametri sono gli stessi del metodo startElement.

Il metodo startElement consente di recuperare gli attributi dell'elemento utilizzando il parametro attributes di tipo Attributes:

  • Il numero di attributi è disponibile in attributes.getLength()
  • Il nome dell'attributo i è disponibile in attributes.getLocalName(i)
  • Il valore dell'attributo i è disponibile in attributes.getValue(i)
  • il valore dell'attributo localName in attributes.getValue(localName)

Con questa spiegazione, il programma precedente e i suoi esempi di esecuzione sono autoesplicativi. È stata utilizzata un'espressione regolare per recuperare i valori degli elementi in modo che un testo XML come:

<erreur>
    erreur 1
</erreur>

restituisce il testo "errore 1" come valore associato al tag <error>, privo di eventuali spazi e interruzioni di riga che potrebbero precederlo e/o seguirlo.

6.4. Applicazione per il calcolo delle imposte: Versione 7

Ora disponiamo di tutti gli elementi per scrivere i client per il nostro servizio fiscale che fornisce XML. Utilizzeremo la versione 4 della nostra applicazione per il client e manterremo la versione 6 per il server. In questa applicazione client-server:

  • Il servizio di simulazione del calcolo delle imposte è gestito dal servlet xmlsimulations. La risposta del server è quindi in formato XML, come abbiamo visto nella versione 6.
  • Il client non è più un browser, ma un client Java autonomo. La sua interfaccia grafica è quella della versione 4.

Ecco alcuni esempi dell'applicazione in azione. Innanzitutto, uno scenario di errore: il client interroga il servlet xmlsimulations anche se non è riuscito a inizializzarsi correttamente perché il DBMS MySQL non era in esecuzione:

Image

Avviamo MySQL ed eseguiamo alcune simulazioni:

Image

Il client in questa nuova versione differisce da quello della versione 4 solo per il modo in cui elabora la risposta del server. Nient’altro cambia. Nella versione 4, il client riceveva codice HTML dal quale estraeva le informazioni necessarie utilizzando espressioni regolari. Qui, il client riceve codice XML dal quale recupera le informazioni necessarie utilizzando un parser XML.

Rivediamo i passaggi principali della procedura associata al menu Calcola nella versione 4 del nostro client, poiché è lì che si verificano principalmente le modifiche:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // tax calculation
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // error is displayed
            JOptionPane.showMessageDialog(this,"L'erreur suivante s'est produite : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
        }
....
    }//mnuCalculer_actionPerformed

    public void calculerImpots(URL urlImpots,boolean marié, int nbEnfants, int salaire)
          throws Exception{
         // tAX CALCULATION
         // urlImpots : URL of the tax department
         // married: true if married, false otherwise
         // nbEnfants : number of children
         // salary: annual salary

         // remove from urlImpots the info needed to connect to the tax server
....
        try{
             // connect to the server
....

             // create customer input/output flows TCP
....

             // request URL - send HTTP headers
....

             // read the 1st line of the answer
....
             // we read the response through to the end of the headers, looking for any cookies
            while((réponse=IN.readLine())!=null){
....            }//while

            // that's it for HTTP headers - move on to HTML code
             // to retrieve simulations
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // it's over
....
    }//calculerImpots

    private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
    }

Tutto questo codice rimane valido nella nuova versione. Solo l'elaborazione della risposta HTML del server (sezione in riquadro sopra) e la sua visualizzazione devono essere sostituite dall'elaborazione della risposta XML del server e dalla sua visualizzazione:

             // that's it for HTTP headers - move on to XML code
             // to recover simulations or errors
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // close server connection
            client.close();

             // display list cleaning
            simulations.clear();
             // errors
            if(listeErreurs.size()!=0){
                 // concatenate all errors
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // error display
                throw new Exception(msgErreur);
            }//if

            // simulations
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }
            return;

Cosa fa il frammento di codice sopra riportato?

  • Crea un parser XML e gli passa lo stream IN, che contiene il codice XML inviato dal server. Questo stream conteneva anche le intestazioni HTTP, ma queste sono già state lette ed elaborate. Pertanto, rimane solo la parte XML della risposta. Il parser produce due elenchi di stringhe: l'elenco degli errori, se presenti, o l'elenco delle simulazioni. Questi due elenchi si escludono a vicenda.
  • Se l'elenco degli errori non è vuoto, i messaggi presenti nell'elenco vengono concatenati in un unico messaggio di errore e viene generata un'eccezione con quel messaggio come parametro. Questa eccezione viene visualizzata nella procedura mnuCalculer_actionPerformed che ha chiamato calculerImpots.
  • Se l'elenco delle simulazioni non è vuoto, viene visualizzato nel componente jList dell'interfaccia utente grafica.

Esploriamo ora il parser per la risposta XML del server, un parser che deriva direttamente dal nostro precedente studio su come analizzare un documento XML in Java:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

// the class
public class ImpotsSaxParser extends DefaultHandler {

    // value of a tree element XML
    private StringBuffer valeur=new StringBuffer();
    // a regular expression of an element's value when you want to ignore
     // the "blanks" that precede or follow it
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // lists of XML elements
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // elements XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- manufacturer
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // creation of a XML analyzer (parser)
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // we indicate to the parser the object that will implement the methods
         // startDocument, endDocument, startElement, endElement, characters
        parser.setContentHandler(this);
        // initialize an element's value model
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // initially no current XML element
        éléments.add("");
         // document analysis
        parser.parse(new InputSource(IN));
    }//manufacturer

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedure called by the parser when it encounters the start of a tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one
         // attributes: list of element attributes

         // note the name of the element
        élément=localName.toLowerCase();
        éléments.add(élément);
         // does the element have attributes?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // it's a simulation - we retrieve the attributes
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // add the simulation to the list of simulations
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- characters
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedure called repeatedly by the parser when it encounters text
         // between two <tag>text</tag> tags
         // the text is in ch from the start character on length characters

         // the text is added to the value buffer if it is the error element
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//characters

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedure called by the parser when it encounters an end of tag
         // uri: URI of the analyzed document?
         // localName: name of element being analyzed
         // qName: ditto, but "qualified" by a namespace if there is one

         // case of error
        if(élément.equals("erreur")){
             // retrieve the value of the error element
            String strValeur=valeur.toString();
             // we strip it of its useless "blanks" and register it in the
             // errors if non-empty
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // set element value to empty
        valeur.setLength(0);
         // reset element name
        éléments.remove(éléments.size()-1);
        élément=(String)éléments.get(éléments.size()-1);
    }//endElement

     // --------- getErreurs
    public ArrayList getErreurs(){
        return listeErreurs;
    }

     // --------- getSimulations
    public ArrayList getSimulations(){
        return listeSimulations;
    }

}//class
  • Il costruttore riceve il flusso XML da analizzare ed esegue immediatamente tale analisi. Una volta completata, l'oggetto è stato costruito e sono stati creati gli elenchi (ArrayList) degli errori (errorList) e delle simulazioni (simulationList). Tutto ciò che resta da fare alla procedura che ha costruito l'oggetto è recuperare i due elenchi utilizzando i metodi getErrors e getSimulations.
  • Solo tre eventi generati dal parser XML sono rilevanti in questo contesto:
    • l'inizio di un elemento XML, un evento che sarà gestito dalla procedura startElement. Questa procedura gestirà i tag <simulation marie=".." enfants=".." salaire=".." impot=".."> e <erreur>...</erreur>.
    • Il valore di un elemento XML, un evento che verrà gestito dalla procedura characters.
    • La fine di un elemento XML, un evento che verrà gestito dalla procedura endElement.
  • Nella procedura startElement, se abbiamo a che fare con l'elemento <simulation marie=".." enfants=".." salaire=".." impot="..">, recuperiamo i quattro attributi utilizzando attributes.getValue("nome attributo"). In tutti i casi, memorizziamo il nome dell'elemento in una variabile element e lo aggiungiamo a un elenco (ArrayList) di elementi: elem1, elem2, ..., elemN. Questo elenco è gestito come uno stack, dove l'ultimo elemento è l'elemento XML attualmente in fase di analisi. Quando si verifica l'evento "fine dell'elemento", l'ultimo elemento dell'elenco viene rimosso e viene impostato il nuovo elemento corrente. Ciò avviene nella procedura endElement.
  • La procedura characters è identica a quella studiata in un esempio precedente. Ci limitiamo semplicemente a verificare che l'elemento corrente sia effettivamente l'elemento <error>, una precauzione che normalmente qui non è necessaria. Questo tipo di precauzione è stata presa anche nella procedura startElement per verificare che si trattasse di un elemento <simulation>.

6.5. Conclusione

Grazie alla sua risposta XML, l'applicazione impots è diventata più facile da gestire sia per il suo progettista che per i progettisti delle applicazioni client.

  • La progettazione dell'applicazione server può ora essere affidata a due figure professionali: lo sviluppatore Java del servlet e il grafico che gestirà l'aspetto della risposta del server nei browser. Quest'ultimo deve semplicemente conoscere la struttura della risposta XML del server per creare i fogli di stile che la accompagneranno. Si noti che questi sono contenuti in file XSL separati, indipendenti dal servlet Java. Il progettista dell'interfaccia utente può quindi lavorare indipendentemente dallo sviluppatore Java.
  • Anche i progettisti di applicazioni client devono semplicemente conoscere la struttura della risposta XML del server. Qualsiasi modifica che il grafico possa apportare ai fogli di stile non ha alcun impatto su questa risposta XML, che rimane sempre la stessa. Questo è un enorme vantaggio.
  • Come può lo sviluppatore aggiornare il proprio servlet Java senza compromettere il funzionamento del sistema? Innanzitutto, purché la risposta XML rimanga invariata, può strutturare il servlet come preferisce. Può anche aggiornare la risposta XML, purché mantenga gli elementi <error> e <simulation> richiesti dai propri clienti. Può quindi aggiungere nuovi tag a questa risposta. Lo sviluppatore front-end ne terrà conto nei propri fogli di stile e i browser saranno in grado di ricevere le nuove versioni della risposta. I client programmatici, tuttavia, continueranno a funzionare con il vecchio modello, poiché i nuovi tag vengono semplicemente ignorati. Affinché ciò funzioni, i tag ricercati devono essere chiaramente identificati nell'analisi XML della risposta del server. Questo è ciò che è stato fatto nel nostro client XML per l'applicazione fiscale, dove le procedure specificavano che stavamo elaborando i tag <error> e <simulation>. Di conseguenza, gli altri tag vengono ignorati.