Skip to content

6. XML e Java

Neste capítulo, apresentamos a utilização de documentos XML com Java. Faremos isso no contexto da aplicação fiscal estudada no capítulo anterior.

6.1. Ficheiros XML e folhas de estilo XSL

Considere o seguinte ficheiro XML, simulations.xml, que poderia representar os resultados de simulações de cálculo de impostos:

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

Quando visualizado com o IE 6, obtém-se o seguinte resultado:

Image

O IE6 reconhece que se trata de um ficheiro XML (graças à extensão .xml do ficheiro) e formata-o à sua maneira. Com o Netscape, obtém-se uma página em branco. No entanto, se se observar o código-fonte (Ver/Fonte), é possível ver o ficheiro XML original:

Image

Por que é que o Netscape não exibe nada? Porque precisa de uma folha de estilo para lhe indicar como transformar o ficheiro XML num ficheiro HTML que possa então exibir. Acontece que o IE 6 tem uma folha de estilo predefinida quando o ficheiro XML não fornece nenhuma, o que era o caso aqui.

Existe uma linguagem chamada XSL (eXtended StyleSheet Language) que permite descrever as transformações necessárias para converter um ficheiro XML em qualquer ficheiro de texto. O XSL suporta inúmeras instruções e assemelha-se muito às linguagens de programação. Não entraremos em detalhes aqui, pois isso levaria dezenas de páginas. Vamos simplesmente descrever dois exemplos de folhas de estilo XSL. O primeiro é aquele que transformará o ficheiro XML simulations.xml em código HTML. Modificamos este último para que especifique a folha de estilo que os navegadores podem usar para o transformar num documento HTML, que podem então apresentar:

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

O comando XML

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

designa o ficheiro simulations.xsl como uma folha de estilo XML do tipo text/xsl, ou seja, um ficheiro de texto que contém código XSL. Esta folha de estilo será utilizada pelos navegadores para transformar o texto XML num documento HTML. Aqui está o resultado obtido com o Netscape 7 ao carregar o ficheiro XML simulations.xml:

Image

Quando visualizamos o código-fonte do documento (Ver/Fonte), vemos o documento XML original em vez do documento HTML apresentado:

Image

O Netscape utilizou a folha de estilo simulations.xsl para transformar o documento XML acima num documento HTML exibível. Chegou a hora de analisar o conteúdo desta folha de estilo:

<?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>
  • Uma folha de estilo XSL é um ficheiro XML e, por isso, segue as regras do XML. Entre outras coisas, deve ser «bem-formada», o que significa que todas as tags de abertura devem ser fechadas.
  • O ficheiro começa com duas diretivas XML que podem ser incluídas em qualquer folha de estilo XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

O atributo encoding="ISO-8859-1" permite que caracteres acentuados sejam utilizados na folha de estilo.

  • A tag <xsl:output method="html" indent="yes"/> indica ao interpretador XSL que pretende produzir HTML «indentado».
  • A tag <xsl:template match="element"> é utilizada para definir o elemento no documento XML ao qual serão aplicadas as instruções encontradas entre <xsl:template ...> e </xsl:template>.
    <xsl:template match="/">
................
        </xsl:template>

No exemplo acima, o elemento "/" denota a raiz do documento. Isto significa que, assim que o início do documento XML for encontrado, os comandos XSL localizados entre as duas tags serão executados.

  • Tudo o que não for uma tag XSL é incluído tal como está no fluxo de saída. As próprias tags XSL são executadas. Algumas delas produzem um resultado que é incluído no fluxo de saída. Vamos examinar o seguinte exemplo:
    <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>

Note que o documento XML que está a ser analisado é o seguinte:

<?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 partir do início do documento XML analisado (match="/"), o processador XSL irá produzir o texto

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

Note-se que no texto original tínhamos <hr/> e não <hr>. No texto original, não podíamos escrever <hr>, que, embora seja uma tag HTML válida, é uma tag XML inválida. No entanto, estamos aqui a lidar com texto XML que deve ser «bem-formado», o que significa que todas as tags devem ser fechadas. Por isso, escrevemos <hr/> e, como escrevemos <xsl:output text="html ...>>, o processador XSL transformará o texto <hr/> em <hr>. A seguir a este texto, estará o texto produzido pelo comando XSL:

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

Veremos mais tarde o que é este texto. Por fim, o interpretador irá adicionar o texto:

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

A diretiva <xsl:apply-templates select="/simulations/simulation"/> instrui o processador XSL a aplicar o «modelo» ao elemento /simulations/simulation. Será executada sempre que o interpretador XSL encontrar uma tag <simulation>..</simulations> ou <simulation/> dentro de uma tag <simulations>..</simulations> no texto XML analisado. Ao encontrar a tag <simulation>, o interpretador executará as instruções do seguinte modelo:

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

Considere as seguintes linhas XML:

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

A linha <simulation ..> corresponde ao modelo para a instrução XSL <xsl:apply-templates select="/simulations/simulation">. O interpretador XSL tentará, portanto, aplicar as instruções que correspondam a este modelo. Ele encontrará o modelo <xsl:template match="simulation"> e executá-lo-á. Recorde-se que tudo o que não seja um comando XSL é passado sem alterações pelo interpretador XSL, enquanto os comandos XSL são substituídos pelo resultado da sua execução. A instrução XSL <xsl:value-of select="@champ"/> é, assim, substituída pelo valor do atributo «champ» do nó analisado (aqui, um nó <simulation>). A análise da linha XML anterior produzirá o seguinte resultado:

XSL
saída
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
sim
</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>

No total, a linha XML

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

será convertido na seguinte linha HTML:

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

Todas estas explicações são um pouco rudimentares, mas agora deve ficar claro para o leitor que o seguinte texto 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>

acompanhado pela seguinte folha de estilo 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>

produz o seguinte texto 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>

O ficheiro XML simulations.xml, juntamente com a folha de estilo simulations.xsl, quando visualizado num navegador moderno (neste caso, o Netscape 7), é apresentado da seguinte forma:

Image

6.2. Aplicação de cálculo de impostos: versão 6

6.2.1. Os ficheiros XML e as folhas de estilo XSL da aplicação de cálculo de impostos

Voltemos à aplicação web de impostos e modifiquemo-la para que a resposta enviada aos clientes seja em formato XML, em vez de HTML. Esta resposta XML será acompanhada por uma folha de estilo XSL para que os navegadores possam exibi-la. Na secção anterior, apresentámos:

  • o ficheiro simulations.xml, que é um protótipo de uma resposta XML contendo simulações de cálculo de impostos
  • o ficheiro simulations.xsl, que será a folha de estilo XSL que acompanha esta resposta XML

Temos também de ter em conta o caso de uma resposta que contenha erros. O protótipo para a resposta XML, neste caso, será o seguinte ficheiro 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>

A folha de estilo errors.xsl utilizada para apresentar este documento XML num navegador será a seguinte:

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

Esta folha de estilo apresenta um comando XSL ainda não abordado: <xsl:value-of select="."/>. Este comando devolve o valor do nó analisado, neste caso um nó <error>text</error>. O valor deste nó é o texto entre as tags de abertura e de fecho, neste caso «text».

O código errors.xml é transformado pela folha de estilo errors.xsl no seguinte 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>

O ficheiro errors.xml, juntamente com a sua folha de estilo, é apresentado por um navegador da seguinte forma:

Image

6.2.2. O servlet xmlsimulations

Criamos um ficheiro index.html e colocamo-lo no diretório da aplicação impots. A página apresentada é a seguinte:

Image

Este documento HTML é um documento estático. O seu código é o seguinte:

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

Note que os dados do formulário são enviados para o URL /impots/xmlsimulations. Esta aplicação é um servlet Java configurado da seguinte forma no ficheiro web.xml da aplicação 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>
  • O servlet chama-se xmlsimulations e baseia-se na classe xmlsimulations.class.
  • Os seus parâmetros são DSNimpots, admimpots e mdpimpots, que são necessários para aceder à base de dados fiscal. Além disso, aceita dois outros parâmetros:
    • xslSimulations, que é o nome do ficheiro de folha de estilo que deve acompanhar a resposta XML contendo as simulações
    • xslErrors, que é o nome da folha de estilo que deve acompanhar a resposta XML contendo quaisquer erros
  • Tem um alias, xmlsimulations, que o torna acessível através do URL http://localhost:8080/impots/xmlsimulations.

A estrutura do servlet xmlsimulations é semelhante à do servlet simulations já discutido. A principal diferença é que este deve gerar XML em vez de HTML. Isto resultará na remoção dos ficheiros JSP utilizados em aplicações anteriores. A sua principal função era melhorar a legibilidade do código HTML gerado, evitando que este ficasse enterrado no código Java do servlet. Esta função já não é necessária. O servlet tem dois tipos de código XML para gerar:

  • um para as simulações
  • um para os erros

Apresentámos e analisámos anteriormente os dois tipos de respostas XML a fornecer nestes dois casos, bem como as folhas de estilo que as devem acompanhar. O código do servlet é o seguinte:

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);
    }
}

Vamos analisar as principais novidades deste código em comparação com o que já conhecíamos:

  • O procedimento init recupera novos parâmetros do ficheiro de configuração web.xml: os nomes das duas folhas de estilo XSL que devem acompanhar a resposta são armazenados nas variáveis xslSimulations e xslErrors. Estas duas folhas de estilo são os ficheiros simulations.xsl e errors.xsl discutidos anteriormente. Estão localizados no diretório da aplicação 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
  • O procedimento GET começa por verificar se ocorreu algum erro durante a inicialização. Se for o caso, chama o procedimento sendErrors, que gera a resposta XML adequada para essa situação e, em seguida, termina. A resposta XML inclui uma tag que especifica a folha de estilo a ser utilizada.
  • Se não ocorreram erros, o procedimento GET analisa os parâmetros do pedido do cliente. Se encontrar algum erro, reporta-o utilizando o procedimento sendErrors. Caso contrário, calcula a nova simulação, adiciona-a às anteriores armazenadas na sessão atual e termina enviando a sua resposta XML através do procedimento sendSimulations. Este último procede de forma análoga ao procedimento sendErrors.
  • Note-se que o servlet declara a sua resposta como do tipo text/xml:
        // on précise le type de la réponse
        response.setContentType("text/xml");

Aqui estão alguns exemplos de execução. O formulário inicial é preenchido da seguinte forma:

Image

A base de dados MySQL não foi iniciada, tornando impossível construir o objeto impots no procedimento init do servlet. A resposta do servlet é, portanto, a seguinte:

Image

O código recebido pelo navegador (Ver/Fonte) é o seguinte:

Image

Se agora executarmos mais duas simulações após iniciar a base de dados MySQL, obtemos o seguinte resultado:

Image

Desta vez, o navegador recebeu o seguinte código:

Image

Note-se que a nossa nova aplicação é mais simples do que antes devido à remoção dos ficheiros JSP. Parte do trabalho anteriormente realizado por estas páginas foi transferido para as folhas de estilo XSL. A vantagem da nossa nova divisão de tarefas é que, uma vez estabelecido o formato XML das respostas do servlet, o desenvolvimento das folhas de estilo é independente do desenvolvimento do servlet.

6.3. Análise de um documento XML em Java

As versões 7 e 8 da nossa aplicação impots serão clientes programados para o servlet xmlsimulations anterior. Estes clientes receberão código XML que terão de analisar para extrair a informação de que necessitam. Vamos agora fazer uma pausa nas nossas várias versões para aprender a analisar um documento XML em Java. Faremos isto utilizando um exemplo incluído no JBuilder 7 chamado MySaxParser. O programa é chamado da seguinte forma:

dos>java MySaxParser
Usage: java MySaxParser [URI]

A aplicação MySaxParser aceita um parâmetro: o URI (Uniform Resource Identifier) do documento XML a ser analisado. No nosso exemplo, este URI será simplesmente o nome de um ficheiro XML localizado no diretório da aplicação MySaxParser. Vamos considerar dois exemplos de execução. No primeiro exemplo, o ficheiro XML a ser analisado é 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>

A análise produz os seguintes resultados:

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

Ainda não explicámos o que a aplicação MySaxParser faz, mas aqui podemos ver que ela apresenta a estrutura do documento XML analisado. O segundo exemplo analisa o ficheiro 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>

A análise produz os seguintes resultados:

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

A classe MySaxParser contém tudo o que precisamos na nossa aplicação fiscal, uma vez que conseguiu recuperar tanto os erros como as simulações que o servidor web poderia enviar. Vamos examinar o seu código:

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

Primeiro, vamos definir uma sigla que aparece frequentemente na análise de documentos XML: SAX, que significa Simple API for XML. Trata-se de um conjunto de classes Java que facilitam o trabalho com documentos XML. Existem duas versões da API: SAX1 e SAX2. A aplicação acima utiliza a API SAX2.

A aplicação importa vários pacotes:

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

Os dois primeiros vêm com o JDK 1.4, mas o terceiro não. O pacote xerces.jar está disponível no site do Apache Web Server. Vem com o JBuilder 7, bem como com o Tomcat 4.x:

Image

Portanto, se quiser compilar a aplicação anterior fora do JBuilder 7 e tiver o JDK 1.4 e o Tomcat 4.x, pode escrever:

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

Ao executar a aplicação, faça o mesmo:

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

A classe MySaxParser estende a classe DefaultHandler. Voltaremos a isso mais tarde. Vamos examinar o código do procedimento principal:

         // 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();
        }

Para analisar um documento XML, a nossa aplicação necessita de um analisador de código XML.

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

O analisador XML utilizado é o fornecido pelo pacote xerces.jar. O objeto devolvido é do tipo XMLReader. XMLReader é uma interface, e utilizamos aqui dois dos seus métodos:

void setContentHandler(ContentHandler handler)
indica ao analisador qual o objeto ContentHandler que irá tratar os eventos que este gera durante a análise do documento XML
void parse(InputSource input)
inicia a análise do documento XML passado como parâmetro

Quando o analisador analisa o documento XML, emite eventos como: «Encontrei o início do documento, o início de uma tag, um atributo de tag, o conteúdo de uma tag, o fim de uma tag, o fim do documento, ...». Passa estes eventos para o objeto ContentHandler que lhe foi fornecido. ContentHandler é uma interface que define os métodos a implementar para tratar todos os eventos que o analisador XML pode gerar. DefaultHandler é uma classe que fornece uma implementação padrão destes métodos. Os métodos implementados em DefaultHandler não fazem nada, mas existem. Quando precisamos de indicar ao analisador qual o objeto que irá tratar os eventos que este gera, utilizando a instrução

void setContentHandler(ContentHandler handler)

, é conveniente passar um objeto do tipo DefaultHandler como parâmetro. Se parássemos por aí, nenhum evento do analisador seria tratado, mas o nosso programa estaria sintaticamente correto. Na prática, passamos um objeto derivado da classe DefaultHandler para o analisador como parâmetro, no qual os métodos que tratam apenas dos eventos que nos interessam são redefinidos. É isso que é feito aqui:

             // 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);

Passamos ao analisador uma instância da classe mySaxParser, que é a nossa classe e foi definida anteriormente pela declaração

public class MySaxParser extends DefaultHandler {

e começamos a analisar o documento cujo URI foi passado como parâmetro. A partir daí, inicia-se a análise do documento XML. O analisador emite eventos e, para cada um deles, chama um método específico do objeto responsável por lidar com esses eventos — neste caso, o nosso objeto MySaxParser. Este objeto lida com cinco eventos específicos; os restantes são ignorados:

evento emitido pelo analisador
método de tratamento
início do documento
void startDocument()
fim do documento
void endDocument()
início de um elemento: <tag>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: nome do elemento analisado. Se o elemento encontrado for <simulations>, localName será "simulations".
qName: nome qualificado pelo namespace do elemento analisado. Um documento XML pode definir um namespace, como XX. O nome qualificado da tag anterior seria, então, XX:simulations.
attributes: lista dos atributos da tag
valor de um elemento:
<tag>valor</tag>
public void characters(char[] ch, int start, int length)
ch: matriz de caracteres
start: índice do primeiro caractere a utilizar na matriz ch
length: número de caracteres a retirar da matriz ch
O método characters pode ser chamado repetidamente. Para construir o valor de um elemento, usamos um buffer que:
  • inicialmente está vazio
  • preenchemos a cada nova chamada ao método characters
  • terminamos no final do elemento
Fim de um elemento: </tag> ou <tag .../>
void endElement(String uri, String localName, String qName)
Os parâmetros são os mesmos do método startElement.

O método startElement permite-lhe recuperar os atributos do elemento utilizando o parâmetro attributes do tipo Attributes:

  • O número de atributos está disponível em attributes.getLength()
  • O nome do atributo i está disponível em attributes.getLocalName(i)
  • O valor do atributo i está disponível em attributes.getValue(i)
  • o valor do atributo localName em attributes.getValue(localName)

Com isto explicado, o programa anterior e os seus exemplos de execução são autoexplicativos. Foi utilizada uma expressão regular para recuperar os valores dos elementos, de modo a que um texto XML como:

<erreur>
    erreur 1
</erreur>

retorna o texto "erro 1" como o valor associado à tag <error>, sem quaisquer espaços e quebras de linha que possam precedê-lo e/ou segui-lo.

6.4. Aplicação de cálculo de impostos: Versão 7

Temos agora todos os elementos para escrever clientes para o nosso serviço fiscal que fornece XML. Utilizaremos a versão 4 da nossa aplicação para o cliente e manteremos a versão 6 para o servidor. Nesta aplicação cliente-servidor:

  • O serviço de simulação do cálculo de impostos é gerido pelo servlet xmlsimulations. A resposta do servidor é, portanto, em formato XML, tal como vimos na versão 6.
  • O cliente já não é um navegador, mas sim um cliente Java autónomo. A sua interface gráfica é a da versão 4.

Aqui estão alguns exemplos da aplicação em ação. Primeiro, um cenário de erro: o cliente consulta o servlet xmlsimulations mesmo que não tenha conseguido inicializar corretamente porque o SGBD MySQL não estava a funcionar:

Image

Iniciamos o MySQL e executamos algumas simulações:

Image

O cliente nesta nova versão difere do cliente da versão 4 apenas na forma como processa a resposta do servidor. Nada mais muda. Na versão 4, o cliente recebia código HTML, do qual extraía as informações necessárias utilizando expressões regulares. Aqui, o cliente recebe código XML, do qual recupera as informações necessárias utilizando um analisador XML.

Vamos rever os principais passos do procedimento associado ao menu «Calcular» na versão 4 do nosso cliente, uma vez que é aí que as alterações ocorrem principalmente:

  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{
....
    }

Todo este código permanece válido na nova versão. Apenas o processamento da resposta HTML do servidor (secção em caixa acima) e a sua apresentação precisam de ser substituídos pelo processamento da resposta XML do servidor e a sua apresentação:

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

O que faz o trecho de código acima?

  • Cria um analisador XML e passa-lhe o fluxo IN, que contém o código XML enviado pelo servidor. Este fluxo também continha os cabeçalhos HTTP, mas estes já foram lidos e processados. Por conseguinte, apenas permanece a parte XML da resposta. O analisador produz duas listas de cadeias de caracteres: a lista de erros, caso existam, ou a lista de simulações. Estas duas listas são mutuamente exclusivas.
  • Se a lista de erros não estiver vazia, as mensagens na lista são concatenadas numa única mensagem de erro, e é lançada uma exceção com essa mensagem como parâmetro. Esta exceção é exibida no procedimento mnuCalculer_actionPerformed que chamou calculerImpots.
  • Se a lista de simulações não estiver vazia, é apresentada no componente jList da interface gráfica do utilizador.

Vamos agora explorar o analisador da resposta XML do servidor, um analisador que decorre diretamente do nosso estudo anterior sobre como analisar um documento XML em 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
  • O construtor recebe o fluxo IN XML a ser analisado e executa imediatamente essa análise. Assim que esta estiver concluída, o objeto foi construído e as listas (ArrayList) de erros (errorList) e simulações (simulationList) foram criadas. Tudo o que resta para o procedimento que construiu o objeto é recuperar as duas listas utilizando os métodos getErrors e getSimulations.
  • Apenas três eventos gerados pelo analisador XML são relevantes aqui:
    • o início de um elemento XML, um evento que será tratado pelo procedimento startElement. Este procedimento tratará as tags <simulation marie=".." enfants=".." salaire=".." impot=".."> e <erreur>...</erreur>.
    • O valor de um elemento XML, um evento que será tratado pelo procedimento characters.
    • O fim de um elemento XML, um evento que será tratado pelo procedimento endElement.
  • No procedimento startElement, se estivermos a lidar com o elemento <simulation marie=".." enfants=".." salaire=".." impot="..">, recuperamos os quatro atributos utilizando attributes.getValue("nome do atributo"). Em todos os casos, armazenamos o nome do elemento numa variável element e adicionamo-lo a uma lista (ArrayList) de elementos: elem1, elem2, ..., elemN. Esta lista é gerida como uma pilha, onde o último elemento é o elemento XML atualmente a ser analisado. Quando ocorre o evento «fim do elemento», o último elemento da lista é removido e o novo elemento atual é definido. Isto é feito no procedimento endElement.
  • O procedimento characters é idêntico ao estudado num exemplo anterior. Limitamo-nos a verificar se o elemento atual é de facto o elemento <error>, uma precaução que normalmente é desnecessária aqui. Este tipo de precaução também foi tomada no procedimento startElement para verificar se estávamos a lidar com um elemento <simulation>.

6.5. Conclusão

Graças à sua resposta XML, a aplicação impots tornou-se mais fácil de gerir, tanto para o seu criador como para os criadores de aplicações cliente.

  • O design da aplicação de servidor pode agora ser confiado a dois tipos de pessoas: o programador Java do servlet e o designer gráfico que irá gerir a aparência da resposta do servidor nos navegadores. Este último precisa apenas de conhecer a estrutura da resposta XML do servidor para criar as folhas de estilo que a acompanharão. Note-se que estas estão contidas em ficheiros XSL separados, independentes do servlet Java. O designer da interface do utilizador pode, portanto, trabalhar independentemente do programador Java.
  • Os designers de aplicações cliente também precisam apenas de conhecer a estrutura da resposta XML do servidor. Quaisquer alterações que o designer gráfico possa fazer nas folhas de estilo não têm impacto nesta resposta XML, que permanece sempre a mesma. Esta é uma enorme vantagem.
  • Como é que o programador pode atualizar o seu servlet Java sem causar erros? Em primeiro lugar, desde que a resposta XML permaneça inalterada, pode organizar o servlet como quiser. Também pode atualizar a resposta XML, desde que mantenha os elementos <error> e <simulation> esperados pelos seus clientes. Assim, pode adicionar novas tags a esta resposta. O programador front-end irá ter em conta essas tags nas suas folhas de estilo, e os navegadores poderão receber as novas versões da resposta. Os clientes programáticos, no entanto, continuarão a funcionar com o modelo antigo, uma vez que as novas tags são simplesmente ignoradas. Para que isto funcione, as tags que estão a ser procuradas devem ser claramente identificadas na análise XML da resposta do servidor. Foi isto que foi feito no nosso cliente XML para a aplicação fiscal, onde os procedimentos indicavam especificamente que estávamos a processar as tags <error> e <simulation>. Como resultado, as outras tags são ignoradas.