Skip to content

6. XML e JAVA

In questo capitolo introduciamo l’utilizzo dei documenti XML con Java. Lo faremo nel contesto dell’applicazione fiscale esaminata nel capitolo precedente.

6.1. File XML e fogli di stile XSL

Consideriamo il seguente file XML simulations.xml, che potrebbe rappresentare il risultato di 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 lo si visualizza con IE 6, si ottiene il seguente risultato:

Image

IE6 riconosce che si tratta di un file XML (grazie al suffisso .xml del file) e lo impagina a modo suo. Con Netscape si ottiene una pagina vuota. Tuttavia, se si osserva il codice sorgente (View/Source), si nota che è presente il file originale XML:

Image

Perché Netscape non visualizza nulla? Perché ha bisogno di un foglio di stile che gli indichi come trasformare il file XML nel file HTML, che potrà quindi visualizzare. Si dà il caso che IE 6 disponga di un foglio di stile predefinito, mentre il file XML non ne propone alcuno, come nel caso in questione.

Esiste un linguaggio chiamato XSL (eXtended StyleSheet Language) che permette di descrivere le trasformazioni da effettuare per convertire un file XML in un file di testo qualsiasi. XSL consente l’uso di numerose istruzioni e assomiglia molto ai linguaggi di programmazione. Non lo descriveremo in dettaglio qui, poiché ci vorrebbero diverse decine di pagine. Ci limiteremo semplicemente a descrivere 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 indichi il foglio di stile che i browser potranno utilizzare per trasformarlo nel documento HTML, documento che potranno 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>

L'ordine XML

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

indica il file simulations.xsl come foglio di stile (xml-stylesheet) di tipo text/xsl c.a.d. 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 quando si carica il file XML simulations.xml:

Image

Quando si esamina il codice sorgente del documento (Visualizza/Sorgente), si ritrova il documento XML iniziale e non il documento HTML visualizzato:

Image

Netscape ha utilizzato il foglio di stile simulations.xsl per trasformare il documento XML sopra riportato nel 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 ne segue quindi le regole. Deve essere, tra le altre cose, "ben formato", ovvero ogni tag aperto deve essere chiuso.
  • Il file inizia con due comandi XML che possono essere mantenuti 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 di utilizzare i caratteri accentati nel foglio di stile.

  • Il tag <xsl:output method="html" indent="yes"/> indica all'interprete XSL che si desidera generare un HTML "indentato".
  • Il tag <xsl:template match="elemento"> serve a definire l'elemento del documento XML a cui si applicheranno le istruzioni contenute 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 verrà individuato l'inizio del documento XML, verranno eseguiti i comandi XSL situati tra i due tag.

  • Tutto ciò che non è un tag XSL viene inserito così com'è nel flusso di output. I tag XSL vengono invece eseguiti. Alcuni di essi producono un risultato che viene inserito 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>

Ricordiamo 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>

Fin dall'inizio del documento XML analizzato (match="/"), l'interprete XSL produrrà in uscita 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 iniziale era presente <hr/> e non <hr>. Nel testo iniziale non era possibile scrivere <hr>, che, pur essendo un tag HTML valido, è un tag XML non valido. Ora, qui abbiamo a che fare con un testo XML che deve essere «ben formato», c.a.d il che significa che ogni tag deve essere chiuso. Si scrive quindi <hr/> e, poiché si è scritto <xsl:output text="html ...">, l’interprete XSL trasformerà il testo <hr/> in <hr>. A seguito di questo testo, seguirà il testo prodotto dal comando XSL:

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

Vedremo in seguito di quale testo si tratta. Infine, l’interprete aggiungerà il testo:

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

Il comando <xsl:apply-templates select="/simulations/simulation"/> richiede l'esecuzione del "template" (modello) dell'elemento /simulations/simulation. Verrà eseguito ogni volta che l’interprete XSL incontrerà nel testo analizzato XML un tag <simulation>..</simulations> o <simulation/> all’interno di un tag <simulations>..</simulations>. All'individuazione del tag <simulation>, l'interprete eseguirà le istruzioni del modello seguente:

    <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>

Consideriamo le seguenti righe XML:

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

La riga <simulation ..> corrisponde al modello dell'istruzione XSL <xsl:apply-templates select="/simulations/simulation>". L'interprete XSL cercherà quindi di applicarvi le istruzioni che corrispondono a questo modello. Troverà il modello <xsl:template match="simulation"> e lo eseguirà. Ricordiamo che ciò che non è un comando XSL viene ripreso tal quale 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 precedente XML produrrà in uscita il seguente risultato:

XSL
output
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
</td><td>
</td><td>
<xsl:value-of select="@enfants"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salaire"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@imposta"/>
22504
</td></tr>
</td></tr>

In totale, la riga XML

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

verrà trasformata nella 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 risultare chiaro al lettore che il testo XML seguente:

<?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, accompagnato dal foglio di stile simulations.xsl, visualizzato con un browser recente (in questo caso Netscape 7), viene quindi visualizzato come segue:

Image

6.2. Applicazione fiscale: versione 6

6.2.1. I file XML e i fogli di stile XSL dell’applicazione fiscale

Torniamo all’applicazione web per le imposte e modifichiamola in modo che la risposta inviata ai clienti sia in formato XML anziché HTML. Questa risposta XML sarà accompagnata da un foglio di stile XSL affinché i browser possano visualizzarla. Nel paragrafo precedente abbiamo presentato:

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

Dobbiamo prevedere anche il caso di una risposta contenente errori. Il prototipo della risposta XML in questo caso sarà il seguente file erreurs.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 erreurs.xsl che consente di 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 non ancora incontrato: <xsl:value-of select="."/>. Questo comando produce in uscita il valore del nodo analizzato, in questo caso un nodo <erreur>texte</erreur>. Il valore di questo nodo è il testo compreso tra i due tag di apertura e chiusura, in questo caso texte.

Il codice erreurs.xml viene trasformato dal foglio di stile erreurs.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 erreurs.xml, insieme al relativo foglio di stile, viene visualizzato dal browser nel modo seguente:

Image

6.2.2. Il servlet xmlsimulations

Creiamo un file index.html che 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(){
           // azzeramento del modulo
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//with
      }//cancella

      function calculer(){
           // verifica dei parametri prima dell'invio al server
        with(document.frmImpots){
          //numero di figli
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // il modello non è stato verificato
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//if
          //stipendio
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // il modello non è stato verificato
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//se
          // Va bene - inviamo
          submit();
        }//con
      }//calcolare        
        </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 a 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>
  • La servlet si chiama xmlsimulations e si basa sulla classe xmlsimulations.class.
  • Come parametri utilizza i valori DSNimpots, admimpots e mdpimpots, necessari per accedere al database delle imposte. Inoltre, accetta altri due parametri:
    • xslSimulations, che è il nome del file di stile che deve accompagnare la risposta XML contenente le simulazioni
    • xslErreurs, che è il nome del file di stile che deve accompagnare la risposta XML contenente eventuali errori
  • ha un alias xmlsimulations che la rende accessibile tramite URL http://localhost:8080/impots/xmlsimulations.

La struttura del servlet xmlsimulations è simile a quella del servlet simulations già esaminato. La differenza principale sta nel fatto che deve generare XML invece di HTML. Ciò comporterà l’eliminazione dei file JSP utilizzati nelle applicazioni precedenti. Il loro ruolo principale era quello di migliorare la leggibilità del codice HTML generato, evitando che questo fosse sepolto nel codice Java del servlet. Questo ruolo ora non ha più ragione di esistere. Il servlet deve generare due tipi di codice XML:

  • quello per le simulazioni
  • quello per gli errori

In precedenza abbiamo presentato e analizzato i due tipi di risposta XML da fornire in questi due casi, nonché i fogli di stile che devono accompagnarli. Il codice della 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{

    // variabili di istanza
    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{

        // si recupera il flusso di scrittura verso il client
        PrintWriter out=response.getWriter();

        // si specifica il tipo di risposta
        response.setContentType("text/xml");

         // l'elenco degli errori
        ArrayList erreurs=new ArrayList();

         // L'inizializzazione è andata a buon fine?
        if(msgErreur!=null){
             // Operazione completata: si invia la risposta con gli errori al server
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // È tutto
            return;
        }

         // si recuperano le simulazioni precedenti della sessione
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // si recuperano i parametri della richiesta corrente
        String optMarie=request.getParameter("optMarie");              // stato civile
        String txtEnfants=request.getParameter("txtEnfants");       // numero di figli
        String txtSalaire=request.getParameter("txtSalaire");       // stipendio annuo

         // Sono presenti tutti i parametri previsti?
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // mancano dei parametri
             // si invia la risposta con gli errori
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // è terminato
            return;
        }

         // sono presenti tutti i parametri - li verifichiamo
         // stato civile
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // errore
            erreurs.add("Etat marital incorrect");
        }
         // numero di figli
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // errore
            erreurs.add("Nombre d'enfants incorrect");
        }
         // stipendio
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // errore
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // se ci sono errori, vengono segnalati
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // nessun errore
            try{
                 // è possibile calcolare l'imposta da versare
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // si aggiunge il risultato attuale alle simulazioni precedenti
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // si invia la risposta con le simulazioni
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // si reinserisce l'elenco delle simulazioni nella sessione
        session.setAttribute("simulations",simulations);
    }//GET

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

     //-------- INIT
    public void init(){
         // si recuperano i parametri di inizializzazione
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

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

         // si crea un'istanza di 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";
         // si invia la risposta
        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++){
            // simulazione n. 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";
        }//per
        réponse+="</simulations>\n";
         // si invia la risposta
        out.println(réponse);
    }
}

Analizziamo nel dettaglio le principali novità di questo codice rispetto a quanto 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 vengono inseriti nelle variabili xslSimulations e xslErreurs. Questi due fogli di stile sono i file simulations.xsl e erreurs.xsl esaminati in precedenza. Questi sono collocati 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 caso affermativo, chiama la procedura sendErreurs che genera la risposta XML adatta a questo caso, quindi si interrompe. In questa risposta viene inserita l’istruzione XML che indica il foglio di stile da utilizzare.
  • Se non si sono verificati errori, la procedura GET analizza i parametri della richiesta del cliente. Se rileva un errore qualsiasi, lo segnala utilizzando anche in questo caso la procedura sendErreurs. In caso contrario, calcola la nuova simulazione, la aggiunge a quelle precedenti memorizzate nella sessione corrente e conclude inviando la propria risposta XML tramite la procedura sendSimulations. Quest’ultima procede in modo analogo alla procedura sendErreurs.
  • Si noti che il servlet annuncia la propria risposta come di tipo text/xml:
         // si specifica il tipo di risposta
        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 di quest’ultimo è quindi la seguente:

Image

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

Image

Se ora si eseguono nuovamente due simulazioni dopo aver avviato il database MySQL, si ottiene 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 all’eliminazione dei file JSP. Parte del lavoro svolto da queste pagine è stato trasferito ai fogli di stile XSL. Il vantaggio della nostra nuova ripartizione dei compiti è che, una volta definito 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 «impôts» saranno client programmati della precedente servlet «xmlsimulations». Questi riceveranno codice XML che dovranno analizzare per estrarne le informazioni di loro interesse. Facciamo qui una pausa nelle nostre diverse versioni per imparare come analizzare un documento XML in Java. Lo faremo partendo da un esempio fornito con JBuilder 7 chiamato MySaxParser. Il programma si chiama come segue:

dos>java MySaxParser
Usage: java MySaxParser [URI]

L’applicazione MySaxParser accetta un parametro: l’URI (Uniform Resource Identifier) del documento XML da analizzare. Nel nostro esempio, questo URI sarà semplicemente il nome di un file XML presente nella directory dell'applicazione MySaxParser. Consideriamo due esempi di esecuzione. Nel primo esempio, il file XML analizzato è il file erreurs.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 fornisce 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 avevamo ancora spiegato a cosa servisse l'applicazione MySaxParser, ma qui si vede 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 impots, poiché è stata in grado di individuare 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.*;

// la classe
public class MySaxParser extends DefaultHandler {

     // valore di un elemento dell'albero XML
    private StringBuffer valeur=new StringBuffer();
    // un'espressione regolare del valore di un elemento quando si desidera ignorare
     // gli "spazi" che lo precedono o lo seguono
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- main
    public static void main(String[] argv) {
        // verifica del numero di parametri
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // si recupera il URI dal file XML da analizzare
        String uri = argv[0];
        try {
            // creazione di un analizzatore XML (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // si indica al parser l'oggetto che implementerà i metodi
             // startDocument, endDocument, startElement, endElement, caratteri
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // si inizializza il modello di valore di un elemento
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // si indica al parser il documento XML da analizzare
            parser.parse(uri);
        }
        catch(Exception ex) {
             // errore
            System.err.println("Erreur : " + ex);
             // traccia
            ex.printStackTrace();
        }
    }//main


     // -------- startDocument
    public void startDocument() throws SAXException {
         // procedura chiamata quando il parser incontra l'inizio del documento
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // procedura chiamata quando il parser raggiunge la fine del documento

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

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedura chiamata dal parser quando incontra l'inizio di un tag
         // URI: URI del documento analizzato?
         // localName: nome dell'elemento attualmente in fase di analisi
         // qName: idem, ma “qualificato” da uno spazio dei nomi, se presente
         // attributes: elenco degli attributi dell'elemento

         // monitoraggio
        System.out.println("Début élément <"+localName+">");
         // l'elemento ha degli attributi?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//per
    }//startElement

    // -------- caratteri
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedura richiamata ripetutamente dal parser quando incontra del testo
         // tra due tag <tag>testo</tag>
         // il testo si trova in ch a partire dal carattere start per length caratteri

         // il testo viene aggiunto al buffer valore
        valeur.append(ch, start, length);
    }//caratteri

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedura chiamata dal parser quando incontra la fine di un tag
         // URI: URI del documento analizzato?
         // localName: nome dell'elemento attualmente in fase di analisi
         // qName: idem, ma “qualificato” da uno spazio dei nomi, se presente

         // viene visualizzato il valore dell'elemento
        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
         // si imposta il valore dell'elemento su vuoto
        valeur.setLength(0);

         // seguito da
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//classe

Per prima cosa definiamo un acronimo che ricorre frequentemente nell'analisi dei documenti XML: SAX, che significa Simple API for XML. Si tratta di un insieme di classi Java che facilitano il lavoro con i documenti XML. Esistono due versioni di API: SAX1 e SAX2. L’applicazione sopra citata utilizza API e 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 nella versione 1.4 di JDK, mentre il terzo no. Il pacchetto xerces.jar è disponibile sul sito del server Web Apache. È fornito con JBuilder 7 ma anche con Tomcat 4.x:

Image

Se quindi si desidera compilare l’applicazione precedente al di fuori di JBuilder 7 e si dispone di JDK 1.4 e di Tomcat 4.x, si potrà scrivere:

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

All’esecuzione, si procederà allo stesso modo:

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

La classe MySaxParser deriva dalla classe DefaultHandler. Ci torneremo più avanti. Esaminiamo il codice della procedura main:

         // si recupera il URI dal file XML da analizzare
        String uri = argv[0];
        try {
            // creazione di un analizzatore XML (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // si indica al parser l'oggetto che implementerà i metodi
             // startDocument, endDocument, startElement, endElement, caratteri
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // si inizializza il modello di valore di un elemento
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // si indica al parser il documento XML da analizzare
            parser.parse(uri);
        }
        catch(Exception ex) {
             // errore
            System.err.println("Erreur : " + ex);
             // traccia
            ex.printStackTrace();
        }

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

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

Il parser XML utilizzato è quello fornito dal pacchetto xerces.jar. L’oggetto recuperato è di tipo XMLReader. XMLReader è un’interfaccia di cui qui utilizziamo due metodi:

void setContentHandler(ContentHandler handler)
indica al parser l’oggetto di tipo ContentHandler che gestirà gli eventi che genererà durante l’analisi del documento XML
void parse(InputSource input)
avvia l’analisi del documento XML passato come parametro

Quando il parser analizzerà il documento XML, emetterà eventi quali: «ho individuato 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, ...». Trasmette 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 eseguono alcuna operazione, ma esistono comunque. Quando è necessario indicare al parser quale oggetto gestirà gli eventi che genererà tramite l’istruzione

void setContentHandler(ContentHandler handler)

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

             // si indica al parser l'oggetto che implementerà i metodi
             // startDocument, endDocument, startElement, endElement, caratteri
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // si indica al parser il documento XML da analizzare
            parser.parse(uri);

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

public class MySaxParser extends DefaultHandler {

e avviamo l'analisi del documento di cui abbiamo passato l'oggetto URI come parametro. A questo punto, ha inizio l'analisi del documento XML. Il parser genera degli eventi e, per ciascuno di essi, chiama un metodo specifico dell’oggetto incaricato di gestirli, in questo caso il nostro oggetto MySaxParser. Quest’ultimo gestisce cinque eventi specifici, mentre gli altri vengono ignorati:

evento generato dal parser
metodo di elaborazione
début du document    
void startDocument()
fin du document
void endDocument()
début d'un élément :<balise>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: nome dell'elemento analizzato. Se l'elemento individuato è <simulations>, si avrà localName="simulations".
qName: nome qualificato da uno spazio dei nomi dell'elemento analizzato. Un documento XML può definire uno spazio dei nomi, ad esempio XX. Il nome qualificato del tag precedente sarebbe quindi XX:simulations.
attributes: elenco degli attributi del tag
valeur d'un élément :
<balise>valeur</balise>
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, si utilizza quindi un buffer che:
  • svuota all'inizio dell'elemento
  • si riempie ad ogni nuova chiamata del metodo characters
  • si conclude alla fine dell’elemento
fin d'un élément : </balise> ou <balise .../>
void endElement(String uri, String localName, String qName)
i parametri sono quelli del metodo startElement.

Il metodo startElement consente di recuperare gli attributi dell'elemento tramite 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 denominato localName in attributes.getValue(localName)

Una volta chiarito questo, il programma precedente, corredato dagli esempi di esecuzione, risulta di per sé comprensibile. È stata utilizzata un'espressione regolare per recuperare i valori degli elementi in modo che un testo XML come:

<erreur>
    erreur 1
</erreur>

fornisca come valore associato al tag <erreur> il testo «errore 1», privo di spazi e interruzioni di riga che potrebbero precederlo e/o seguirlo.

6.4. Applicazione fiscale: versione 7

Ora disponiamo di tutti gli elementi necessari per scrivere i client programmati per il nostro servizio fiscale che genera XML. Riprendiamo la versione 4 della nostra applicazione per realizzare il client e manteniamo la versione 6 per quanto riguarda il server. In questa applicazione client-server:

  • il servizio di simulazione del calcolo delle imposte è gestito dal servlet xmlsimulations. La risposta del server è quindi nel 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 di esecuzione. Innanzitutto un caso di errore: il client interroga la servlet xmlsimulations mentre questa non è riuscita a inizializzarsi correttamente poiché il processo SGBD MySQL non era stato avviato:

Image

Si avvia MySQL e si eseguono alcune simulazioni:

Image

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

Ricordiamo le linee generali della procedura relativa al menu «Calcola» della versione 4 del nostro client, poiché è principalmente lì che vengono apportate le modifiche:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // si calcola l'imposta
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // viene visualizzato l'errore
            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{
         // calcolo dell'imposta
         // urlImpots: URL dell'ufficio delle imposte
         // sposato: true se sposato, false altrimenti
         // nbEnfants: numero di figli
         // stipendio: stipendio annuale

         // da urlImpots si estraggono le informazioni necessarie per la connessione al server dell'agenzia delle entrate
....
        try{
             // si effettua la connessione al server
....

             // si creano i flussi di entrata e uscita del cliente TCP
....

             // si richiede l'URL - invio delle intestazioni HTTP
....

             // si legge la prima riga della risposta
....
             // si legge la risposta fino alla fine delle intestazioni cercando l'eventuale cookie
            while((réponse=IN.readLine())!=null){
....            }//while

            // le intestazioni sono terminate HTTP - si passa al codice HTML
             // per recuperare le simulazioni
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // è tutto
....
    }//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 (parte evidenziata sopra) e la sua visualizzazione devono essere sostituite dall'elaborazione della risposta XML del server e dalla sua visualizzazione:

             // è tutto per le intestazioni HTTP - si passa al codice XML
             // per recuperare le simulazioni o gli errori
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // chiusura della connessione al server
            client.close();

             // pulizia dell'elenco di visualizzazione
            simulations.clear();
             // errori
            if(listeErreurs.size()!=0){
                 // si concatenano tutti gli errori
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // visualizzazione errori
                throw new Exception(msgErreur);
            }//if

            // simulazioni
            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 il flusso IN che contiene il codice XML inviato dal server. Questo flusso conteneva anche le intestazioni HTTP, ma queste sono già state lette ed elaborate. Rimane quindi solo la parte XML della risposta. Il parser genera due elenchi di stringhe: l’elenco degli errori, se presenti, oppure quello delle simulazioni. Questi due elenchi si escludono a vicenda.
  • Se l’elenco degli errori non è vuoto, i messaggi in esso contenuti vengono concatenati in un unico messaggio di errore e viene generata un’eccezione con tale messaggio come parametro. Questa eccezione viene visualizzata nella procedura mnuCalculer_actionPeformed che ha chiamato calculerImpots.
  • Se l’elenco delle simulazioni non è vuoto, viene visualizzato nel componente jList dell’interfaccia grafica.

Esaminiamo ora il parser della risposta XML del server, parser che deriva direttamente dallo studio che abbiamo condotto in precedenza 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.*;

// la classe
public class ImpotsSaxParser extends DefaultHandler {

    // valore di un elemento dell'albero XML
    private StringBuffer valeur=new StringBuffer();
    // un'espressione regolare del valore di un elemento quando si desidera ignorare
     // gli «spazi» che lo precedono o lo seguono
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // gli elenchi di elementi XML
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // elementi XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- produttore
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // creazione di un analizzatore XML (parser)
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // si indica al parser l'oggetto che implementerà i metodi
         // startDocument, endDocument, startElement, endElement, caratteri
        parser.setContentHandler(this);
        // si inizializza il modello di valore di un elemento
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // inizialmente non è presente alcun elemento XML corrente
        éléments.add("");
         // si analizza il documento
        parser.parse(new InputSource(IN));
    }//generatore

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedura chiamata dal parser quando incontra linizio di un tag
         // URI: URI del documento analizzato?
         // localName: nome dell'elemento attualmente in fase di analisi
         // qName: idem, ma qualificato da uno spazio dei nomi, se presente
         // attributes: elenco degli attributi dell'elemento

         // si indica il nome dell'elemento
        élément=localName.toLowerCase();
        éléments.add(élément);
         // l'elemento ha degli attributi?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // si tratta di una simulazione: si recuperano gli attributi
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // si aggiunge la simulazione all'elenco delle simulazioni
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- caratteri
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedura richiamata ripetutamente dal parser quando incontra del testo
         // tra due tag <tag>testo</tag>
         // il testo si trova in ch a partire dal carattere start per length caratteri

         // il testo viene aggiunto al buffer valore se si tratta dell'elemento errore
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//caratteri

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedura chiamata dal parser quando incontra la chiusura di un tag
         // URI: URI del documento analizzato?
         // localName: nome dell'elemento attualmente in fase di analisi
         // qName: idem, ma qualificato da uno spazio dei nomi, se presente

         // caso di errore
        if(élément.equals("erreur")){
             // si recupera il valore dell'elemento di errore
            String strValeur=valeur.toString();
             // si eliminano gli "spazi" superflui e lo si registra nell'elenco degli
             // errori, se non è vuota
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // si imposta il valore dell'elemento su vuoto
        valeur.setLength(0);
         // si reimposta il nome dell'elemento
        é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;
    }

}//classe
  • il produttore riceve il flusso XML IN da analizzare e procede immediatamente all'analisi. Una volta completata l’analisi, l’oggetto è stato creato e sono stati generati gli elenchi (ArrayList) degli errori (listeErreurs) e delle simulazioni (listeSimulations). Alla procedura che ha creato l’oggetto non resta che recuperare i due elenchi utilizzando i metodi getErreurs e getSimulations.
  • In questo contesto ci interessano solo tre eventi generati dal parser XML:
    • l’inizio di un elemento XML, evento che verrà gestito dalla procedura startElement. Quest’ultima dovrà gestire i tag <simulation marie=".." enfants=".." salaire=".." impot=".."> e <erreur>...</erreur>.
    • valore di un elemento XML, evento che verrà gestito dalla procedura characters.
    • fine di un elemento XML, evento che verrà gestito dalla procedura endElement.
  • nella procedura startElement, se si ha a che fare con l'elemento <simulation marie=".." enfants=".." salaire=".." impot="..">, si recuperano i quattro attributi tramite attributes.getValue("nome dell'attributo"). In ogni caso, si memorizza il nome dell’elemento in una variabile «elemento» e lo si aggiunge a un elenco (ArrayList) di elementi: elem1, elem2, …, elemn. Questo elenco viene gestito come uno stack il cui ultimo elemento è l’elemento XML attualmente in fase di analisi. Quando si verifica l’evento “fine elemento”, l’ultimo elemento dell’elenco viene rimosso e il nuovo elemento corrente viene aggiornato. Ciò avviene nella procedura endElement.
  • La procedura `characters` è identica a quella esaminata in un esempio precedente. Ci si limita semplicemente a verificare che l'elemento corrente sia effettivamente l'elemento <erreur>, precauzione che normalmente qui non sarebbe necessaria. Questo tipo di precauzione era stata adottata 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. A quest’ultimo basta conoscere la struttura della risposta XML del server per creare i fogli di stile che la accompagneranno. Ricordiamo che questi ultimi sono oggetto di file XSL separati e indipendenti dal servlet Java. Il grafico può quindi lavorare indipendentemente dallo sviluppatore Java.
  • Anche i progettisti delle applicazioni client devono semplicemente conoscere la struttura della risposta XML del server. Le modifiche che il grafico potrebbe apportare ai fogli di stile non hanno alcuna ripercussione su questa risposta XML, che rimane sempre la stessa. Si tratta di un enorme vantaggio.
  • Come può lo sviluppatore far evolvere il proprio servlet Java senza compromettere il funzionamento del sistema? Innanzitutto, fintanto che la sua risposta XML non cambia, può organizzare il proprio servlet come preferisce. Può anche aggiornare la risposta XML purché mantenga gli elementi <errore> e <simulazione> previsti dai propri clienti. Può quindi aggiungere nuovi tag a questa risposta. Il grafico ne terrà conto nei propri fogli di stile e i browser potranno visualizzare le nuove versioni della risposta. I client programmati, invece, continueranno a funzionare con il vecchio modello, ignorando semplicemente i nuovi tag. Affinché ciò sia possibile, è necessario che nell’analisi XML della risposta del server i tag ricercati siano correttamente identificati. È quanto è stato fatto nel nostro client XML dell’applicazione fiscale, dove nelle procedure si specificava espressamente che venivano trattati i tag <erreur> e <simulation>. Di conseguenza, gli altri tag vengono ignorati.