5. L’applicazione IMPOTS
5.1. Introduzione
Riprendiamo qui l’applicazione IMPOTS, utilizzata più volte nella dispensa su Java dello stesso autore. Ricordiamo la problematica. Si tratta di scrivere un’applicazione che consenta di calcolare l’imposta di un contribuente. Consideriamo il caso semplificato di un contribuente che abbia da dichiarare solo il proprio stipendio:
- si calcola il numero di quote del dipendente nbParts=nbEnfants/2 +1 se non è sposato, nbEnfants/2+2 se è sposato, dove nbEnfants è il numero dei suoi figli.
- se ha almeno tre figli, ha diritto a una mezza quota in più
- si calcola il suo reddito imponibile R = 0,72 * S, dove S è il suo stipendio annuale
- si calcola il suo coefficiente familiare QF = R / nbParts
- si calcola la sua imposta I. Consideriamo la seguente tabella:
12620,0 | 0 | 0 |
13190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24.740 | 0,15 | 2072,5 |
31810 | 0,2 | 3309,5 |
39.970 | 0,25 | 4900 |
48360 | 0,3 | 6898,5 |
55790 | 0,35 | 9316,5 |
92970 | 0,4 | 12106 |
127.860 | 0,45 | 16754,5 |
151250 | 0,50 | 23147,5 |
172040 | 0,55 | 30710 |
195000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
Ogni riga ha 3 campi. Per calcolare l'imposta I, si cerca la prima riga in cui QF<=campo1. Ad esempio, se QF=23000, si troverà la riga
L'imposta I è quindi pari a 0,15*R - 2072,5*nbParts. Se QF è tale che la relazione QF<=campo1 non è mai soddisfatta, allora vengono utilizzati i coefficienti dell’ultima riga. In questo caso:
il che dà l’imposta I = 0,65*R - 49062*nbParts.
I dati che definiscono le diverse fasce d'imposta si trovano in un database ODBC-MySQL. MySQL è un SGBD di dominio pubblico utilizzabile su diverse piattaforme, tra cui Windows e Linux. Con questo SGBD è stato creato un database denominato dbimpots contenente un'unica tabella chiamata impots. L'accesso al database è controllato da un nome utente e una password, in questo caso admimpots/mdpimpots. La schermata seguente mostra come utilizzare il database dbimpots con MySQL:
C:\Program Files\EasyPHP\mysql\bin>mysql -u admimpots -p
Enter password: *********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 3.23.49-max-nt
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> use dbimpots;
Database changed
mysql> show tables;
+--------------------+
| Tables_in_dbimpots |
+--------------------+
| impots |
+--------------------+
1 row in set (0.00 sec)
mysql> describe impots;
+---------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+-------+
| limites | double | YES | | NULL | |
| coeffR | double | YES | | NULL | |
| coeffN | double | YES | | NULL | |
+---------+--------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> select * from impots;
+---------+--------+---------+
| limites | coeffR | coeffN |
+---------+--------+---------+
| 12620 | 0 | 0 |
| 13190 | 0.05 | 631 |
| 15640 | 0.1 | 1290.5 |
| 24740 | 0.15 | 2072.5 |
| 31810 | 0.2 | 3309.5 |
| 39970 | 0.25 | 4900 |
| 48360 | 0.3 | 6898 |
| 55790 | 0.35 | 9316.5 |
| 92970 | 0.4 | 12106 |
| 127860 | 0.45 | 16754 |
| 151250 | 0.5 | 23147.5 |
| 172040 | 0.55 | 30710 |
| 195000 | 0.6 | 39312 |
| 0 | 0.65 | 49062 |
+---------+--------+---------+
14 rows in set (0.00 sec)
mysql>quit
Il database dbimpots viene convertito nella fonte dati ODBC nel modo seguente:
- si avvia il gestore delle fonti dati ODBC a 32 bit

- si utilizza il pulsante [Add] per aggiungere una nuova fonte dati ODBC

- si seleziona il driver MySQL e si esegue [Terminer]
54321

- il driver MySQL richiede alcune informazioni:
1 | il nome DSN da assegnare alla fonte di dati ODBC - può essere qualsiasi nome |
2 | il computer su cui viene eseguito il processo SGBD MySQL - in questo caso localhost. È interessante notare che il database potrebbe essere un database remoto. Le applicazioni locali che utilizzano la fonte dati ODBC non se ne accorgerebbero. Questo varrebbe in particolare per la nostra applicazione Java. |
3 | il database MySQL da utilizzare. MySQL è un SGBD che gestisce database relazionali, ovvero insiemi di tabelle collegate tra loro da relazioni. In questo caso, si specifica il nome del database gestito. |
4 | il nome di un utente con diritti di accesso a questo database |
5 | la sua password |
Sono state definite due classi per il calcolo delle imposte: impots e impotsJDBC. Viene creata un'istanza della classe impots con i dati delle fasce di imposta passati come parametri in array:
// creazione di una classe "imposte"
public class impots{
// i dati necessari per il calcolo dell'imposta
// provengono da una fonte esterna
protected double[] limites=null;
protected double[] coeffR=null;
protected double[] coeffN=null;
// costruttore vuoto
protected impots(){}
// costruttore
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
// si verifica che le 3 tabelle abbiano le stesse dimensioni
boolean OK=LIMITES.length==COEFFR.length && LIMITES.length==COEFFN.length;
if (! OK) throw new Exception ("Les 3 tableaux fournis n'ont pas la même taille("+
LIMITES.length+","+COEFFR.length+","+COEFFN.length+")");
// va bene
this.limites=LIMITES;
this.coeffR=COEFFR;
this.coeffN=COEFFN;
}//produttore
// calcolo dell'imposta
public long calculer(boolean marié, int nbEnfants, int salaire){
// calcolo del numero di quote
double nbParts;
if (marié) nbParts=(double)nbEnfants/2+2;
else nbParts=(double)nbEnfants/2+1;
if (nbEnfants>=3) nbParts+=0.5;
// calcolo del reddito imponibile e del quoziente familiare
double revenu=0.72*salaire;
double QF=revenu/nbParts;
// calcolo dell'imposta
limites[limites.length-1]=QF+1;
int i=0;
while(QF>limites[i]) i++;
// restituzione del risultato
return (long)(revenu*coeffR[i]-nbParts*coeffN[i]);
}//calcolare
}//classe
La classe impotsJDBC deriva dalla precedente classe impots. Viene creata un'istanza della classe impotsJDBC utilizzando i dati relativi alle fasce di imposta memorizzati in un database. Le informazioni necessarie per accedere a tale database vengono passate come parametri al costruttore:
// pacchetti importati
import java.sql.*;
import java.util.*;
public class impotsJDBC extends impots{
// aggiunta di un costruttore che consente di creare
// le tabelle limiti, coeffr, coeffn a partire dalla tabella
// imposte di un database
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: nome DSN del database
// userIMPOTS, mdpIMPOTS: login/password di accesso al database
// le tabelle dei dati
ArrayList aLimites=new ArrayList();
ArrayList aCoeffR=new ArrayList();
ArrayList aCoeffN=new ArrayList();
// connessione al database
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection connect=DriverManager.getConnection("jdbc:odbc:"+dsnIMPOTS,userIMPOTS,mdpIMPOTS);
// creazione di un oggetto Statement
Statement S=connect.createStatement();
// query SELECT
String select="select limites, coeffr, coeffn from impots";
// esecuzione della query
ResultSet RS=S.executeQuery(select);
while(RS.next()){
// elaborazione della riga corrente
aLimites.add(RS.getString("limites"));
aCoeffR.add(RS.getString("coeffr"));
aCoeffN.add(RS.getString("coeffn"));
}// riga successiva
// chiusura delle risorse
RS.close();
S.close();
connect.close();
// trasferimento dei dati in tabelle delimitate
int n=aLimites.size();
limites=new double[n];
coeffR=new double[n];
coeffN=new double[n];
for(int i=0;i<n;i++){
limites[i]=Double.parseDouble((String)aLimites.get(i));
coeffR[i]=Double.parseDouble((String)aCoeffR.get(i));
coeffN[i]=Double.parseDouble((String)aCoeffN.get(i));
}//for
}//costruttore
}//classe
Una volta creata un'istanza della classe impotsJDBC, è possibile richiamare ripetutamente il suo metodo calculer per calcolare l'imposta:
L'acquisizione dei tre dati necessari può avvenire in diversi modi. Il vantaggio della classe impotsJDBC è che ci si deve occupare esclusivamente di tale acquisizione. Una volta ottenute le tre informazioni (stato civile, numero di figli, stipendio annuo), la chiamata al metodo calculer della classe impotsJDBC ci fornisce l’imposta da pagare.
5.2. Versione 1
Ci troviamo nel contesto di un’applicazione web che presenterebbe un’interfaccia HTML a un utente al fine di ottenere i tre parametri necessari al calcolo dell’imposta:
- lo stato civile (coniugato o no)
- il numero di figli
- lo stipendio annuale

La visualizzazione del modulo avviene tramite la seguente pagina JSP:
<%@ page import="java.util.*" %>
<%
// si recuperano gli attributi passati dal servlet principale
String chkoui=(String)request.getAttribute("chkoui");
String chknon=(String)request.getAttribute("chknon");
String txtEnfants=(String)request.getAttribute("txtEnfants");
String txtSalaire=(String)request.getAttribute("txtSalaire");
String txtImpots=(String)request.getAttribute("txtImpots");
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
%>
<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="";
}//con
}//cancella
</script>
</head>
<body background="/impots/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" action="/impots/main" method="POST">
<table>
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="optMarie" value="oui" <%= chkoui %>>oui
<input type="radio" name="optMarie" value="non" <%= chknon %>>non
</td>
</tr>
<tr>
<td>Nombre d'enfants</td>
<td><input type="text" size="5" name="txtEnfants" value="<%= txtEnfants %>"></td>
</tr>
<tr>
<td>Salaire annuel</td>
<td><input type="text" size="10" name="txtSalaire" value="<%= txtSalaire %>"></td>
</tr>
<tr>
<td><font color="green">Impôt</font></td>
<td><input type="text" size="10" name="txtImpots" value="<%= txtImpots %>" readonly></td>
</tr>
<tr></tr>
<tr>
<td><input type="submit" value="Calculer"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
</tr>
</table>
</form>
</center>
<%
// ci sono errori
if(erreurs!=null){
// visualizzazione degli errori
out.println("<hr>");
out.println("<font color=\"red\">");
out.println("Les erreurs suivantes se sont produites<br>");
out.println("<ul>");
for(int i=0;i<erreurs.size();i++){
out.println("<li>"+(String)erreurs.get(i));
}
out.println("</ul>");
out.println("</font>");
}
%>
</body>
</html>
La pagina JSP si limita a visualizzare le informazioni che le vengono trasmesse dal servlet principale dell'applicazione:
// si recuperano gli attributi passati dal servlet principale
String chkoui=(String)request.getAttribute("chkoui");
String chknon=(String)request.getAttribute("chknon");
String txtEnfants=(String)request.getAttribute("txtEnfants");
String txtSalaire=(String)request.getAttribute("txtSalaire");
String txtImpots=(String)request.getAttribute("txtImpots");
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
gli attributi dei pulsanti di opzione «sì» e «no» possono assumere i valori "checked" o "" per attivare o disattivare il pulsante di opzione corrispondente | |
il numero di figli del contribuente | |
il suo stipendio annuale | |
l'importo dell'imposta da pagare | |
un eventuale elenco di errori se errori!=null |
La pagina inviata al cliente contiene uno script JavaScript con una funzione effacer associata al pulsante "Effacer", il cui scopo è riportare il modulo allo stato iniziale: pulsante deselezionato, campi di immissione vuoti. Si noti che questo risultato non poteva essere ottenuto con un pulsante HTML di tipo "reset". Infatti, quando si utilizza questo tipo di pulsante, il browser riporta il modulo nello stato in cui lo ha ricevuto. Tuttavia, nella nostra applicazione, il browser riceve moduli che possono non essere vuoti.
Il servlet principale dell’applicazione si chiama main.java e il suo codice è il seguente:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;
public class main extends HttpServlet{
// variabili di istanza
String msgErreur=null;
String urlAffichageImpots=null;
String urlErreur=null;
String DSNimpots=null;
String admimpots=null;
String mdpimpots=null;
impotsJDBC impots=null;
//-------- GET
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
// L'inizializzazione è andata a buon fine?
if(msgErreur!=null){
// si passa il controllo alla pagina di errore
request.setAttribute("msgErreur",msgErreur);
getServletContext().getRequestDispatcher(urlErreur).forward(request,response);
}
// attributi della richiesta
String chkoui=null;
String chknon=null;
String txtImpots=null;
// si recuperano i parametri della richiesta
String optMarie=request.getParameter("optMarie"); // stato civile
String txtEnfants=request.getParameter("txtEnfants"); // numero di figli
if(txtEnfants==null) txtEnfants="";
String txtSalaire=request.getParameter("txtSalaire"); // stipendio annuo
if(txtSalaire==null) txtSalaire="";
// Sono presenti tutti i parametri previsti?
if(optMarie==null || txtEnfants==null || txtSalaire==null){
// mancano dei parametri
request.setAttribute("chkoui","");
request.setAttribute("chknon","checked");
request.setAttribute("txtEnfants","");
request.setAttribute("txtSalaire","");
request.setAttribute("txtImpots","");
// si passa all'URL di visualizzazione dell'imposta
getServletContext().getRequestDispatcher(urlAffichageImpots).forward(request,response);
}
// sono presenti tutti i parametri - li verifichiamo
ArrayList erreurs=new ArrayList();
// stato civile
if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
// errore
erreurs.add("Etat marital incorrect");
optMarie="non";
}
// 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");
}
// se ci sono errori, li si passa come attributi della query
if(erreurs.size()!=0){
request.setAttribute("erreurs",erreurs);
txtImpots="";
}else{
// è possibile calcolare l'imposta da pagare
try{
int nbEnfants=Integer.parseInt(txtEnfants);
int salaire=Integer.parseInt(txtSalaire);
txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
}catch(Exception ex){}
}
// gli altri attributi della richiesta
if(optMarie.equals("oui")){
request.setAttribute("chkoui","checked");
request.setAttribute("chknon","");
}else{
request.setAttribute("chknon","checked");
request.setAttribute("chkoui","");
}
request.setAttribute("txtEnfants",txtEnfants);
request.setAttribute("txtSalaire",txtSalaire);
request.setAttribute("txtImpots",txtImpots);
// si passa il controllo all'URL di visualizzazione dell'imposta
getServletContext().getRequestDispatcher(urlAffichageImpots).forward(request,response);
}
//-------- POST
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
doGet(request,response);
}
//-------- INIT
public void init(){
// si recuperano i parametri di inizializzazione
ServletConfig config=getServletConfig();
urlAffichageImpots=config.getInitParameter("urlAffichageImpots");
urlErreur=config.getInitParameter("urlErreur");
DSNimpots=config.getInitParameter("DSNimpots");
admimpots=config.getInitParameter("admimpots");
mdpimpots=config.getInitParameter("mdpimpots");
// parametri ok?
if(urlAffichageImpots==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();
}
}
}
- Il metodo init del servlet svolge due operazioni:
- recupera i propri parametri di inizializzazione. Questi le consentono di connettersi al database ODBC contenente i dati delle diverse fasce fiscali (DSNimpots, admimpots, mdpimpots) e i URL delle pagine associate all'applicazione: urlAffichageImpots per il modulo, urlErreur per la pagina di errore.
- crea un'istanza della classe impotsJDBC
In entrambi i casi, gli eventuali errori vengono gestiti e il messaggio di errore viene inserito nella variabile msgErreur.
- Il metodo doGET
- verifica innanzitutto che il servlet sia stato inizializzato correttamente. In caso contrario, viene visualizzata la pagina di errore
- recupera i parametri previsti dal modulo fiscale: optMarie, txtEnfants, txtSalaire. Se uno di essi manca (==null), viene inviato un modulo fiscale vuoto. Si potrebbe pensare che la verifica della validità del parametro optMarie sia superflua. Si tratta infatti del valore di un pulsante di opzione e in questo contesto può assumere solo uno dei valori «oui» e «non». Ma ciò significa trascurare il fatto che nulla impedisce a un programma di interrogare direttamente il servlet inviandogli i parametri che desidera. Non si può mai essere certi di essere realmente connessi a un browser. Trascurare questo aspetto può portare a falle di sicurezza nell’applicazione ed è del resto frequente trovarne anche nelle applicazioni commerciali.
- Viene verificata la validità di ciascuno dei tre parametri recuperati. Ogni errore rilevato viene aggiunto a un elenco di errori (ArrayList errori). Se non ci sono errori, l’importo dell’imposta viene calcolato; in caso contrario, non viene calcolato.
- Le informazioni necessarie per la visualizzazione della pagina vengono inserite negli attributi della richiesta e il modulo fiscale viene quindi visualizzato
La pagina di errore JSP è la seguente:
<%
// jspService
// si è verificato un errore
String msgErreur= (String)request.getAttribute("msgErreur");
if(msgErreur==null) msgErreur="Erreur non identifiée";
%>
<!-- inizio della pagina HTML -->
<html>
<head>
<title>impots</title>
</head>
<body>
<h3>calcul d'impots</h3>
<hr>
Application indisponible(<%= msgErreur %>)
</body>
</html>
L'applicazione web si chiama «impots» ed è configurata nel file server.xml di Tomcat nel modo seguente:
La cartella dell’applicazione contiene le seguenti cartelle e file:




Il file di configurazione dell'applicazione impots, denominato web.xml, è il seguente:
<?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>main</servlet-name>
<servlet-class>main</servlet-class>
<init-param>
<param-name>urlAffichageImpots</param-name>
<param-value>/impots.jsp</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>
<init-param>
<param-name>urlErreur</param-name>
<param-value>/erreur.jsp</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/main</url-pattern>
</servlet-mapping>
</web-app>
Il servlet principale si chiama main e ha l'alias /main. È quindi accessibile tramite URL e http://localhost:8080/impots/main.
Ecco alcuni esempi di applicazione:
Per inizializzarsi correttamente, la servlet deve avere accesso al database mysql-dbimpots. Ecco la pagina che si ottiene se, ad esempio, il server MySQL non è in esecuzione e il database mysql-dbimpots risulta quindi inaccessibile:

La pagina visualizzata in caso di immissione errata è la seguente:

Se i dati inseriti sono corretti, l’imposta viene calcolata:

5.3. Versione 2
Nell'esempio precedente, la validità dei parametri txtEnfants e txtSalaire del modulo viene verificata dal server. Qui ci proponiamo di verificarla tramite uno script JavaScript incluso nella pagina del modulo. È quindi il browser a effettuare la verifica dei parametri. Il server viene interpellato solo se questi sono validi. In questo modo si risparmia "larghezza di banda". La pagina di visualizzazione JSP diventa la seguente:
<%@ page import="java.util.*" %>
............
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
.......
}//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 il modulo al server
submit();
}//con
}//calcolare
</script>
</head>
<body background="/impots/images/standard.jpg">
........
<td><input type="button" value="Calculer" onclick="calculer()"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
.....
</body>
</html>
Si notino le seguenti modifiche:
- il pulsante Calculer non è più di tipo submit, bensì di tipo button, associato a una funzione denominata calculer. È questa funzione che effettuerà l'analisi dei campi txtEnfants e txTsalaire. Se sono corretti, i valori del modulo verranno inviati al server (submit); in caso contrario, verrà visualizzato un messaggio di errore.
Ecco un esempio di visualizzazione in caso di errore:

5.4. Versione 3
Modifichiamo leggermente l’applicazione per introdurvi il concetto di sessione. Consideriamo ora che l’applicazione sia un programma di simulazione del calcolo delle imposte. Un utente può quindi simulare diverse «configurazioni» di contribuente e vedere quale sarebbe, per ciascuna di esse, l’imposta da pagare. La pagina WEb riportata di seguito fornisce un esempio di ciò che si potrebbe ottenere:

Il servlet principale è stato modificato e ora si chiama simulations. È configurato come segue all’interno 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>
<servlet-name>simulations</servlet-name>
<servlet-class>simulations</servlet-class>
<init-param>
<param-name>urlSimulationImpots</param-name>
<param-value>/simulationsImpots.jsp</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>
<init-param>
<param-name>urlErreur</param-name>
<param-value>/erreur.jsp</param-value>
</init-param>
</servlet>
......
<servlet-mapping>
<servlet-name>simulations</servlet-name>
<url-pattern>/simulations</url-pattern>
</servlet-mapping>
</web-app>
Il servlet principale si chiama «simulations» e si basa sul file di classe simulations.class. Ha l’alias /simulations che la rende accessibile tramite URL e http://localhost:8080/impots/simulations. Presenta gli stessi parametri di inizializzazione del servlet principale esaminato in precedenza per quanto riguarda l’accesso al database. Compare un nuovo parametro, urlSimulationsImpots, che corrisponde al URL della pagina di simulazione JSP (quella appena presentata poco sopra).
La servlet simulations.java è simile alla servlet main.java. Da essa si differenzia per i seguenti punti principali:
- la servlet principale calcola un valore txtImpots a partire dai parametri optmarie, txtEnfants e txtSalaire e passa questo valore alla pagina di visualizzazione JSP
- il servlet «simulazioni» calcola allo stesso modo il valore txtImpots, registra i parametri (optMarie, txtEnfants, txtsalaire, txtImpots) in un elenco denominato «simulazioni». È proprio questo elenco che viene passato come parametro alla pagina di visualizzazione JSP. Affinché tale elenco contenga tutte le simulazioni effettuate dall’utente, viene salvato come attributo della sessione corrente.
Il servlet «simulazioni» è il seguente (sono state mantenute solo le righe di codice che differiscono da quelle dell’applicazione precedente):
import java.io.*;
.......
public class simulations extends HttpServlet{
// variabili di istanza
String msgErreur=null;
String urlSimulationImpots=null;
String urlErreur=null;
...........
//-------- GET
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
...........
// si recuperano le simulazioni precedenti della sessione
HttpSession session=request.getSession();
ArrayList simulations=(ArrayList)session.getAttribute("simulations");
if(simulations==null) simulations=new ArrayList();
// si inseriscono le simulazioni nella query corrente
request.setAttribute("simulations",simulations);
// altri attributi della query
...........
// sono presenti tutti i parametri previsti
if(optMarie==null || txtEnfants==null || txtSalaire==null){
........
// si passa all'URL di visualizzazione delle simulazioni dei calcoli fiscali
getServletContext().getRequestDispatcher(urlSimulationImpots).forward(request,response);
}
// si dispone di tutti i parametri - li si verifica
...........
// se ci sono errori, li si inserisce negli attributi della richiesta
if(erreurs.size()!=0){
request.setAttribute("erreurs",erreurs);
}else{
try{
// è possibile calcolare l'imposta da pagare
int nbEnfants=Integer.parseInt(txtEnfants);
int salaire=Integer.parseInt(txtSalaire);
txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
// si aggiunge il risultato corrente alle simulazioni precedenti
String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
simulations.add(simulation);
// il nuovo valore delle simulazioni viene reinserito nella sessione
session.setAttribute("simulations",simulations);
}catch(Exception ex){}
}
// altri attributi della query
..........
// si passa all'URL di visualizzazione delle simulazioni
getServletContext().getRequestDispatcher(urlSimulationImpots).forward(request,response);
}
//-------- POST
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
doGet(request,response);
}
//-------- INIT
public void init(){
// si recuperano i parametri di inizializzazione
ServletConfig config=getServletConfig();
urlSimulationImpots=config.getInitParameter("urlSimulationImpots");
urlErreur=config.getInitParameter("urlErreur");
DSNimpots=config.getInitParameter("DSNimpots");
admimpots=config.getInitParameter("admimpots");
mdpimpots=config.getInitParameter("mdpimpots");
// parametri ok?
.........................
}
}
La pagina di visualizzazione JSP, derivata dalla pagina simulationsImpots.jsp, è diventata la seguente (è stato mantenuto solo il codice che differisce dalla pagina di visualizzazione JSP dell'applicazione precedente).
<%@ page import="java.util.*" %>
<%
// si recuperano gli attributi passati dal servlet principale
...........
ArrayList simulations=(ArrayList)request.getAttribute("simulations");
%>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
........
</script>
</head>
<body background="/impots/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" action="/impots/simulations" method="POST">
.......................
</form>
</center>
<hr>
<%
// Ci sono errori?
if(erreurs!=null){
..................
}else if(simulations.size()!=0){
// risultati delle simulazioni
out.println("<h3>Résultats des simulations<h3>");
out.println("<table \"border=\"1\">");
out.println("<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>");
for(int i=0;i<simulations.size();i++){
String[] simulation=(String[])simulations.get(i);
out.println("<tr><td>"+simulation[0]+"</td><td>"+simulation[1]+"</td><td>"+simulation[2]+"</td><td>"+simulation[3]+"</td></tr>");
}
out.println("</table>");
}
%>
</body>
</html>
5.5. Versione 4
Ora creeremo un'applicazione autonoma che fungerà da client web dell'applicazione precedente /impots/simulations. Questa applicazione avrà la seguente interfaccia grafica:
![]() |
n. | tipo | nome | ruolo |
1 | JTextField | txtUrlServiceImpots | URL del servizio di simulazione del calcolo delle imposte |
2 | JRadioButton | rdOui | spuntare se sposato |
3 | JRadioButton | rdNon | da spuntare se non sposato |
4 | JSpinner | spinEnfants | numero di figli del contribuente (minimo=0, massimo=20, incremento=1) |
5 | JTextField | txtSalaire | stipendio annuo in F del contribuente |
6 | JList in JScrollPane | lstSimulations | elenco delle simulazioni |
Il menu Imposte è composto dalle seguenti opzioni:
Opzione principale | Opzione secondaria | nome | ruolo |
Imposte | |||
Calcola | mnuCalculer | calcola l'imposta da pagare quando tutti i dati necessari al calcolo sono presenti e corretti | |
Cancella | mnuEffacer | riporta il modulo allo stato iniziale | |
Esci | mnuQuitter | chiude l'applicazione |
Regole di funzionamento
- l'opzione di menu Calculer rimane disattivata se uno dei campi 1 o 5 è vuoto
- viene rilevata una URL sintatticamente errata nel campo 1

- viene rilevato uno stipendio errato

- viene segnalato qualsiasi errore di connessione al server (nell'esempio 1 qui sotto, la porta è errata; nell'esempio 2, il URL richiesto non esiste; nell'esempio 3 il database MySQL non era in esecuzione)



- Se tutto è corretto, vengono visualizzate le simulazioni

Quando si sviluppa un client web, è necessario sapere esattamente cosa invia il server in risposta alle diverse richieste possibili da parte di un client. Il server invia una serie di righe HTML che comprendono informazioni utili e altre presenti solo per motivi di impaginazione HTML. Le espressioni regolari di Java possono aiutarci a individuare le informazioni utili nel flusso delle righe inviate dal server. A tal fine, dobbiamo conoscere il formato esatto delle diverse risposte del server. In questa sede utilizzeremo il client web già visto in precedenza, che consente di visualizzare sullo schermo la risposta di un server alla richiesta di un URL. Il URL richiesto sarà quello del nostro servizio di simulazione del calcolo delle imposte http://localhost:8080/impots/simulations, al quale potremo passare dei parametri nella forma http://localhost:8080/impots/simulations?param1=vam1¶m2=val2&...
Richiediamo l’URL mentre la base MySQL che consente di creare un oggetto di tipo impots non è stata avviata:
Dos>java clientweb http://localhost:8080/impots/simulations GET
HTTP/1.1 200 OK
Content-Type: text/html;charset=ISO-8859-1
Date: Fri, 16 Aug 2002 16:31:04 GMT
Connection: close
Server: Apache Tomcat/4.0.3 (HTTP/1.1 Connector)
Set-Cookie: JSESSIONID=9DEC8B27966A1FBE3D4968A7B9DF3331;Path=/impots
<!-- dÚbut della pagina HTML -->
<html>
<head>
<title>impots</title>
</head>
<body>
<h3>calcul d'impôts</h3>
<hr>
Application indisponible([TCX][MyODBC]Can't connect to MySQL server on 'localhost' (10061))
</body>
</html>
Per individuare l'errore, il client web dovrà cercare nella risposta del server web la riga contenente il testo "Applicazione non disponibile". Ora avviamo il database MySQL e richiediamo lo stesso URL passando i valori che si aspetta (li accetta indifferentemente sotto forma di GET o di POST – cfr. codice Java del server):
Dos>java clientweb "http://localhost:8080/impots/simulations?ptMarie=oui&txtEnfants=2&txtSalaire=200000" GET
HTTP/1.1 200 OK
Content-Type: text/html;charset=ISO-8859-1
Date: Fri, 16 Aug 2002 16:42:36 GMT
Connection: close
Server: Apache Tomcat/4.0.3 (HTTP/1.1 Connector)
Set-Cookie: JSESSIONID=C2A707600E98A37A343611D80DD5C8A2;Path=/impots
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
...................................................
}//cancella
function calculer(){
...................................................
}//calcola
</script>
</head>
<body background="/impots/images/standard.jpg">
<center>
Calcul d'imp¶ts
<hr>
<form name="frmImpots" action="/impots/simulations" method="POST">
...................................................
</form>
</center>
<hr>
<h3>Résultats des simulations<h3>
<table "border="1">
<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>
<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>
</table>
</body>
</html>
Qui è riportato il documento completo HTML inviato dal server. I risultati delle diverse simulazioni sono riportati nell'unica tabella presente in questo documento. L'espressione regolare che ci permette di recuperare le informazioni rilevanti dal documento potrebbe essere la seguente:
dove le quattro espressioni tra parentesi rappresentano le quattro informazioni da recuperare.
Ora disponiamo delle linee guida su cosa fare quando l’utente richiede il calcolo dell’imposta dall’interfaccia grafica precedente:
- verificare che tutti i dati dell’interfaccia siano validi ed eventualmente segnalare gli errori;
- connettersi all’URL indicato nel campo 1. A tal fine si seguirà il modello del client web generico già presentato e studiato
- nel flusso della risposta del server, utilizzare espressioni regolari per:
- individuare il messaggio di errore, se presente
- individuare i risultati delle simulazioni se non vi sono errori
Il codice relativo al menu calculer è il seguente:
void mnuCalculer_actionPerformed(ActionEvent e) {
// calcolo dell’imposta
// verifica URL servizio
URL urlImpots=null;
try{
urlImpots=new URL(txtURLServiceImpots.getText().trim());
String query=urlImpots.getQuery();
if(query!=null) throw new Exception();
}catch (Exception ex){
// messaggio di errore
JOptionPane.showMessageDialog(this,"URL incorrecte. Recommencez","Erreur",JOptionPane.ERROR_MESSAGE);
// focus sul campo errato
txtURLServiceImpots.requestFocus();
// ritorno all'interfaccia
return;
}
// verifica stipendio
int salaire=0;
try{
salaire=Integer.parseInt(txtSalaire.getText().trim());
if(salaire<0) throw new Exception();
}catch (Exception ex){
// messaggio di errore
JOptionPane.showMessageDialog(this,"Salaire incorrect. Recommencez","Erreur",JOptionPane.ERROR_MESSAGE);
// focus sul campo errato
txtSalaire.requestFocus();
// torna all'interfaccia
return;
}
// numero di figli
Integer nbEnfants=(Integer)spinEnfants.getValue();
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
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
String path=urlImpots.getPath();
if(path.equals("")) path="/";
String query="?"+"optMarie="+(marié ? "oui":"non")+"&txtEnfants="+nbEnfants+"&txtSalaire="+salaire;
String host=urlImpots.getHost();
int port=urlImpots.getPort();
if(port==-1) port=urlImpots.getDefaultPort();
// dati locali
Socket client=null; // il cliente
BufferedReader IN=null; // il flusso di lettura del cliente
PrintWriter OUT=null; // il flusso di scrittura del client
String réponse=null; // risposta del server
// il modello ricercato nelle intestazioni HTTP
Pattern modèleCookie=Pattern.compile("^Set-Cookie: JSESSIONID=(.*?);");
// il modello di una risposta corretta
Pattern réponseOK=Pattern.compile("^.*? 200 OK");
// il risultato del confronto con il modello
Matcher résultat=null;
try{
// si effettua la connessione al server
client=new Socket(host,port);
// si creano i flussi di ingresso-uscita del cliente TCP
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// si richiede l'URL - invio delle intestazioni HTTP
OUT.println("GET " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
if(! JSESSIONID.equals("")){
OUT.println("Cookie: JSESSIONID="+JSESSIONID);
}
OUT.println("Connection: close");
OUT.println("");
// si legge la prima riga della risposta
réponse=IN.readLine();
// si confronta la riga HTTP con il modello della risposta corretta
résultat=réponseOK.matcher(réponse);
if(! résultat.find()){
// si riscontra un problema con URL
throw new Exception("Le serveur a répondu : URL ["+ txtURLServiceImpots.getText().trim() + "] inconnue");
}//if(risultato)
// si legge la risposta fino alla fine delle intestazioni cercando l'eventuale cookie
while((réponse=IN.readLine())!=null){
// riga vuota?
if(réponse.equals("")) break;
// riga HTTP non vuota
// se non si dispone del token di sessione, lo si cerca
if (JSESSIONID.equals("")){
// si confronta la riga HTTP con il modello del cookie
résultat=modèleCookie.matcher(réponse);
if(résultat.find()){
// è stato trovato il cookie del token
JSESSIONID=résultat.group(1);
}//if(risultato)
}//if(JSESSIONID)
}//while
// fine delle intestazioni 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
client.close();
}catch (Exception ex){
throw new Exception(ex.getMessage());
}
}//calculerImpots
private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
// il modello di una riga della tabella delle simulazioni
Pattern ptnSimulation=Pattern.compile("<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>");
// il modello di una riga dell'elenco degli errori
Pattern ptnErreur=Pattern.compile("(Application indisponible.*?)\\s*$");
// il risultato del confronto con il modello
Matcher résultat=null;
// le simulazioni
ArrayList listeSimulations=new ArrayList();
// vengono lette tutte le righe fino alla fine
String ligne=null;
boolean simulationRéussie=false;
while((ligne=IN.readLine())!=null){
// monitoraggio
// si confronta la riga con il modello di errore se la sezione delle simulazioni non è stata ancora raggiunta
if(! simulationRéussie){
résultat=ptnErreur.matcher(ligne);
if(résultat.find()){
// messaggio di errore
JOptionPane.showMessageDialog(this,résultat.group(1),"Erreur",JOptionPane.ERROR_MESSAGE);
// operazione completata
return listeSimulations;
}//se
}//se
// si confronta la riga con il modello di simulazione
résultat=ptnSimulation.matcher(ligne);
if(résultat.find()){
// è stata trovata una riga della tabella
listeSimulations.add(résultat.group(1)+":"+résultat.group(2)+":"+résultat.group(3)+
":"+résultat.group(4));
// la simulazione è andata a buon fine
simulationRéussie=true;
}//if
}//mentre
// fine
return listeSimulations;
}
Spieghiamo brevemente questo codice:
- la procedura mnuCalculer_actionPerformed verifica che i dati dell'interfaccia siano validi. Se non lo sono, viene generato un messaggio di errore e la procedura viene interrotta. Se invece sono validi, viene eseguita la procedura calculerImpots.
- La procedura calculerImpots inizia creando la procedura URL che deve richiamare
// si estraggono da urlImpots le informazioni necessarie per la connessione al server fiscale
String path=urlImpots.getPath();
if(path.equals("")) path="/";
String query="?"+"optMarie="+(marié ? "oui":"non")+"&txtEnfants="+nbEnfants+"&txtSalaire="+salaire;
String host=urlImpots.getHost();
int port=urlImpots.getPort();
if(port==-1) port=urlImpots.getDefaultPort();
........................
- quindi si connette a questo URL inviando le intestazioni HTTP appropriate:
// ci si connette al server
client=new Socket(host,port);
// si creano i flussi di entrata e uscita del cliente TCP
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// si richiede l'URL - invio delle intestazioni HTTP
OUT.println("GET " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
if(! JSESSIONID.equals("")){
OUT.println("Cookie: JSESSIONID="+JSESSIONID);
}
OUT.println("Connection: close");
OUT.println("");
- Una volta effettuata la richiesta, il nostro client web attende la risposta. Innanzitutto riceve le intestazioni HTTP della risposta del server. Esamina tali intestazioni per individuare il token di sessione. Infatti, dovrà rinviare quest'ultimo al server affinché quest'ultimo possa tenere traccia delle diverse simulazioni effettuate. Si noti che sarebbe stato più semplice se il client avesse memorizzato autonomamente le diverse simulazioni effettuate dall’utente. Tuttavia, manteniamo l’idea di rinviare il token per fornire un nuovo esempio di gestione della sessione. La prima riga della risposta viene trattata a parte. Infatti, deve avere la forma HTTP/versione 200 OK per indicare che il URL richiesto esiste effettivamente. Se quindi non ha questa forma, si deduce che l’utente abbia richiesto un URL errato e glielo si comunica.
// il modello ricercato nelle intestazioni HTTP
Pattern modèleCookie=Pattern.compile("^Set-Cookie: JSESSIONID=(.*?);");
// il modello di una risposta corretta
Pattern réponseOK=Pattern.compile("^.*? 200 OK");
..........
// si legge la prima riga della risposta
réponse=IN.readLine();
// si confronta la riga HTTP con il modello della risposta corretta
résultat=réponseOK.matcher(réponse);
if(! résultat.find()){
// si riscontra un problema con URL
throw new Exception("Le serveur a répondu : URL ["+ txtURLServiceImpots.getText().trim() + "] inconnue");
}//if(risultato)
// si legge la risposta fino alla fine delle intestazioni cercando l'eventuale cookie
while((réponse=IN.readLine())!=null){
// riga vuota?
if(réponse.equals("")) break;
// riga HTTP non vuota
// se non si dispone del token di sessione, lo si cerca
if (JSESSIONID.equals("")){
// si confronta la riga HTTP con il modello del cookie
résultat=modèleCookie.matcher(réponse);
if(résultat.find()){
// è stato trovato il cookie del token
JSESSIONID=résultat.group(1);
}//if(risultato)
}//if(JSESSIONID)
}//while
- una volta elaborate le intestazioni HTTP, si passa alla parte HTML della risposta
// fine delle intestazioni 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));
}
- La procedura getSimulations restituisce l'elenco delle simulazioni, se presenti; tale elenco risulterà vuoto se il server restituisce un messaggio di errore. In tal caso, il messaggio viene visualizzato in una finestra di messaggio. Se l'elenco non è vuoto, viene visualizzato nell'elenco a discesa dell'interfaccia grafica.
- La procedura getSimulations confronta ogni riga della risposta HTML con l'espressione regolare che rappresenta il messaggio di errore (Applicazione non disponibile...) e con l'espressione regolare che rappresenta una simulazione. Se viene rilevato il messaggio di errore, questo viene visualizzato e la procedura termina. Se viene individuato il risultato di una simulazione, questo viene aggiunto all’elenco delle simulazioni. Al termine della procedura, tale elenco viene restituito come risultato.
private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
// il modello di una riga della tabella delle simulazioni
Pattern ptnSimulation=Pattern.compile("<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>");
// il modello di una riga dell’elenco degli errori
Pattern ptnErreur=Pattern.compile("(Application indisponible.*?)\\s*$");
5.6. Versione 5
Qui trasformiamo la precedente applicazione grafica autonoma in un'applet Java. L'interfaccia grafica è leggermente diversa. Con l'applicazione autonoma, l'utente forniva autonomamente il URL del servizio di simulazione del calcolo delle imposte e successivamente l'applicazione si collegava a tale URL. In questo caso, l’applicazione client è un browser e l’utente richiederà l’URL del documento HTML contenente l’applet. È importante ricordare che un’applet Java può stabilire una connessione di rete solo con il server da cui è stata scaricata. L’URL del servizio di simulazione si troverà quindi sullo stesso server del documento HTML contenente l’applet. Nel nostro esempio, nel campo txtUrlServiceImpots verrà inserito un parametro di inizializzazione dell’applet; tale campo non sarà modificabile dall’utente. Si otterrà così il seguente client:

Il documento HTML contenente l’applet si chiama simulations.htm ed è il seguente:
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body background="/impots/images/standard.jpg">
<center>
<h3>Simulations de calculs d'impôts</h3>
<hr>
<applet code="appletImpots.class" width="400" height="360">
<param name="urlServiceImpots" value="simulations">
</applet>
</center>
</body>
</html>
L'applet ha un parametro urlServiceImpots che corrisponde al URL del servizio di simulazione del calcolo delle imposte. Questo URL è relativo e misurato rispetto all'URL del documento HTML simulations.htm. Pertanto, se un browser recupera questo documento con l'http://localhost:8080/impots/simulations.htm URL, il URL del servizio di simulazione sarà http://localhost:8080/impots/simulations.. Se questo URL fosse stato http://stahe:8080/impots/simulations.htm, il codice URL del servizio di simulazione sarebbe stato http://stahe:8080/impots/simulations.
L'applet appletImpots.java riprende integralmente il codice della precedente applicazione grafica autonoma, rispettando le regole di trasformazione di un'applicazione grafica in applet.
public class appletImpots extends JApplet {
// i componenti della finestra
JPanel contentPane;
JMenuBar jMenuBar1 = new JMenuBar();
JMenu jMenu1 = new JMenu();
JMenuItem mnuCalculer = new JMenuItem();
.............
//Costruire il frame
public void init() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
// altre inizializzazioni
moreInit();
}
// inizializzazione del modulo
private void moreInit(){
// si recupera il parametro urlServiceImpots
String urlServiceImpots=getParameter("urlServiceImpots");
if(urlServiceImpots==null){
// parametro mancante
JOptionPane.showMessageDialog(this,"Le paramètre urlServiceImpots de l'applet n'a pas été défini","Erreur",JOptionPane.ERROR_MESSAGE);
// fine
return;
}
// si inserisce URL nel relativo campo
String codeBase=""+getCodeBase();
if(codeBase.endsWith("/"))
txtURLServiceImpots.setText(codeBase+urlServiceImpots);
else txtURLServiceImpots.setText(codeBase+"/"+urlServiceImpots);
// menu «Calcola» disabilitato
mnuCalculer.setEnabled(false);
// selettore «Bambini» – da 0 a 20 bambini
spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
spinEnfants.setBounds(new Rectangle(130,140,50,27));
contentPane.add(spinEnfants);
}//moreInit
//Inizializza il componente
private void jbInit() throws Exception {
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(null);
...............
}
Quando un browser carica un'applet, esegue innanzitutto la sua procedura init. In essa, abbiamo recuperato il valore del parametro urlServiceImpots dell’applet, quindi abbiamo calcolato il nome completo del servizio di simulazione URL e inserito tale valore nel campo txtURLServiceImpots come se fosse stato digitato dall’utente. Fatto ciò, non vi è più alcuna differenza tra le due applicazioni. In particolare, il codice associato al menu Calculer è identico. Ecco un esempio di esecuzione:

5.7. Conclusione
Abbiamo illustrato diverse versioni della nostra applicazione client-server per il calcolo delle imposte:
- versione 1: il servizio è fornito da un insieme di servlet e pagine JSP, il client è un browser. Esegue una sola simulazione e non conserva le precedenti.
- versione 2: si aggiungono alcune funzionalità lato browser inserendo script JavaScript nel documento HTML caricato dal browser stesso. Il browser verifica la validità dei parametri del modulo.
- versione 3: si consente al servizio di ricordare le diverse simulazioni effettuate da un client gestendo una sessione. L’interfaccia HTML viene modificata di conseguenza per visualizzarle.
- versione 4: il client è ora un’applicazione grafica autonoma. Ciò ci consente di tornare alla scrittura di client web programmati.
- versione 5: il client si trasforma in un’applet Java. Si ottiene così un’applicazione client-server interamente programmata in Java, sia sul lato server che sul lato client.
A questo punto, è possibile fare alcune osservazioni:
- le versioni da 1 a 3 supportano browser che non dispongono di altre funzionalità se non quella di eseguire script JavaScript. Si noti che un utente ha sempre la possibilità di disabilitare l’esecuzione di questi script. L’applicazione funzionerà quindi solo parzialmente nella versione 1 (l’opzione «Cancella» non funzionerà) e per nulla nelle versioni 2 e 3 (le opzioni «Cancella» e «Calcola» non funzioneranno). Potrebbe essere interessante prevedere una versione del servizio che non utilizzi script JavaScript.
- La versione 4 richiede che il computer client disponga di una macchina virtuale Java 2.
- La versione 5 richiede che il computer client disponga di un browser dotato di una macchina virtuale Java 2.
Quando si scrive un servizio Web, è necessario chiedersi a quali tipi di client ci si rivolge. Se si vuole raggiungere il maggior numero possibile di client, si scriverà un'applicazione che invii ai browser solo HTML (senza JavaScript né applet). Se si lavora all’interno di una intranet e si ha il controllo sulla configurazione dei computer che la compongono, è possibile permettersi di essere più esigenti a livello di client e la precedente versione 5 può quindi essere accettabile.
Le versioni 4 e 5 sono client web che recuperano le informazioni di cui hanno bisogno all’interno del flusso HTML inviato dal server. Molto spesso non si ha il controllo su questo flusso. È il caso in cui si sia scritto un client per un servizio web esistente sulla rete e gestito da qualcun altro. Facciamo un esempio. Supponiamo che il nostro servizio di simulazioni per il calcolo delle imposte sia stato sviluppato da una società X. Attualmente il servizio invia le simulazioni in una tabella HTML e il nostro client sfrutta questa caratteristica per recuperarle. Confronta quindi ogni riga della risposta del server con l’espressione regolare:
// il modello di una riga della tabella delle simulazioni
Pattern ptnSimulation=Pattern.compile("<tr>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*<td>(.*?)</td>\\s*</tr>");
Supponiamo ora che lo sviluppatore dell’applicazione modifichi l’aspetto visivo della risposta inserendo le simulazioni non in una tabella, ma in un elenco nella forma:
In questo caso, il nostro client web dovrà essere riscritto. Questa è la minaccia costante che grava sui client web delle applicazioni di cui non si ha il controllo diretto. XML può fornire una soluzione a questo problema:
- invece di generare HTML, il servizio di simulazione genererà XML. Nel nostro esempio, potrebbe essere
<simulations>
<entetes marie="marié" enfants="enfants" salaire="salaire" impot="impôt"/>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504" />
<simulation marie="non" enfants="2" salaire="200000" impot="33388" />
</simulations>
- a questa risposta potrebbe essere associato un foglio di stile che indichi ai browser l'aspetto visivo da attribuire a tale risposta XML
- i client web programmati ignorerebbero questo foglio di stile e recupererebbero le informazioni direttamente dal flusso XML della risposta
Se il progettista del servizio desidera modificare la presentazione visiva dei risultati forniti, modificherà il foglio di stile e non il XML. Grazie al foglio di stile, i browser visualizzeranno la nuova forma visiva e i client web programmati non dovranno essere modificati. Potrebbero quindi essere scritte nuove versioni del nostro servizio di simulazioni:
- versione 6: il servizio fornisce una risposta XML accompagnata da un foglio di stile destinato ai browser
- versione 7: il client è un'applicazione grafica autonoma che utilizza la risposta XML del server
- versione 8: il client è un’applet Java che utilizza la risposta XML del server
