Introduzione
Le
cartoline elettroniche costituiscono un fenomeno di costume piuttosto interessante
all’interno del macrocosmo degli utenti internet: oramai tutti i maggiori
portali offrono questo servizio, ed alcuni siti hanno basato la propria
fortuna proprio sulle e-cards.
Uno
dei fattori di successo della rete è costituita dalla possibilità
di instaurare un rapporto personale con l’interlocutore; non a caso lo
strumento di gran lunga più utilizzato risulta essere la posta elettronica.
Sotto questo profilo i siti web “statici” assomigliano molto ai mezzi di
comunicazioni tradizionali, con un produttore di informazione e molti fruitori
passivi. Le cartoline virtuali sono uno degli strumenti che permettono
ad un sito web di essere fruito in maniera personalizzabile - e personale
- dai navigatori.
Le
cartoline virtuali costituiscono inoltre un buon mezzo pubblicitario: chi
fruisce del servizio “invita” un suo amico ad una vostra pagina web. Naturalmente
il destinatario può limitarsi a leggere la cartolina, ma può
anche decidere di spedirne una lui stesso, e magari dare uno sguardo ai
contenuti del vostro sito.
Realizzare
il servizio
Quando
andate in vacanza, vi capita di spedire una cartolina ai vostri amici.
Scegliete l’immagine che più vi piace, scrivete l’indirizzo del
destinatario, un breve messaggio, la vostra firma; attaccate il francobollo
e spedite. Su internet i francobolli non servono, ma per il resto la procedura,
agli occhi dell’utente, è la stessa: il mittente sceglie un’immagine
e compila una form dove immette il proprio nome (o nick), il proprio indirizzo
e-mail, il nome e l’indirizzo dell’amico, ed un breve messaggio.
Cosa
succede dietro le quinte? Lo script (nel nostro caso la servlet) legge
le informazioni inviate (nomi, indirizzi, messaggio, immagine scelta),
controlla che tutti i campi siano immessi. Se qualche informazione è
mancante, ripropone all’utente la form originale, evidenziando che tutti
i campi vanno compilati.
Se
l’utente compila in maniera corretta la form, la sevlet crea una pagina
html contenente l’immagine scelta, il nome del mittente, quello del destinatario,
ed il messaggio desiderato. Invia, in maniera automatica, un messaggio
di posta elettronica al destinatario, nel quale viene notificato che il
mittente ha inviato una cartolina, ed il link alla pagina generata.
Infine
la servlet invia al browser dell’utente un messaggio di conferma, con il
link alla pagina generata.
La
form iniziale
Come
abbiamo visto la servlet ha bisogno di alcune informazioni da parte dell’utente,
che vengono raccolte attraverso una pagina html contenente una form.
<html><head><title>Cartoline
del mio sito</title></head><body>
<!--
la tabella contenente i radio-button e le immagini disponibili -->
<table
border ="0" align="center" width="750">
<tr><td
width="25%" align="center">
<input
name="immagine" type="radio" value="http://www.miosito.it/cartoline/panorama.jpg">
<img
src=" http://www.miosito.it/cartoline/panorama.jpg" border="0">
</td>
<td
width="25%" align="center">
<input
name="immagine" type="radio" value="http://www.miosito.it/cartoline/fiori.jpg">
<img
src=" http://www.miosito.it/cartoline/fiori.jpg " border="0">
</td>
<!--
.... inserire in questo modo tutte le immagini desiderate -->
</tr>
</table>
<!--
i campi per raccogliere nome e indirizzo del mittente,
nome
ed indirizzo del destinatario ed il testo del messaggio -->
<table
border ="0" align="center" width="750">
<tr><td>Inserisci
il tuo nome</td>
<td><input
name="nomemitt" type="text" value=""></td></tr>
<tr><td>La
tua e-mail</td>
<td><input
name="mailmitt" type="text" value=""></td></tr>
<tr><td>Il
nome del destinatario</td>
<td><input
name="nomedest" type="text" value=""></td></tr>
<tr><td>L'e-mail
del destinatario</td>
<td><input
name="maildest" type="text" value=""></td></tr>
<tr><td>Il
testo della cartolina</td>
<td><textarea
name="textfield" rows="5" cols="50" ></textarea></td></tr>
</table><input
type="submit" value="Invia la cartolina"></form></body>
</html>
A prescindere
dall’impaginazione, la form deve inviare alcune variabili, e più
specificamente immagine, contenente il link alla picture selezionata, nomemitt,
mailmitt, nomedest, maildest e textfield.
Inizializzare
la classe Cartolina.java
Cartolina.java
dispone di una variabile contatore, utilizzata per fini statistici e per
identificare, assieme alla data di creazione, ogni cartolina inviata. Per
questo motivo questa variabile dev’essere in qualche modo persistente,
per evitare che la servlet assegni lo stesso numero progressivo a due differenti
cartoline. Se la servlet non viene utilizzata per qualche ora, la servlet
engine decide generalmente di distruggere l’istanza in corso, e crearne
una nuova alla successiva richiesta. Questa procedura azzera il valore
della variabile contatore, che dev’essere pertanto salvata su disco.
Uno
dei metodi più importanti della classe HttpServlet, che Cartolina.java
estende, è il metodo init(ServletConfig conf), utilizzato per inizializzare
la servlet. Nella versione più semplice di Cartolina.java, il metodo
init viene utilizzato esclusivamente per leggere il file contatore e reinizializzare
la variabile corrispondente:
public
void init(ServletConfig conf)
throws ServletException {
super.init(conf);
contatore = 0;
try {
BufferedReader dis =
new BufferedReader (new FileReader (countfilename));
String riga = dis.readLine ();
if (riga == null) return;
StringTokenizer tok =
new StringTokenizer (riga, "\t");
int contatoc = tok.countTokens();
if (contatoc < 2) { return; }
String countstring = tok.nextToken ();
contatore = Integer.parseInt (countstring);
}
catch (IOException e) {
System.out.println ("ioexception in init");
}
catch (NumberFormatException e) {
System.out.println ("NumberFormatException in init");
}
}
La
variabile countfilename deve rappresentare l’indirizzo del file contatore.txt,
che contiene, su una sola riga, il numero progressivo e la data dell’ultima
cartolina spedita, separate da un carattere di tabulazione (‘\t’).
Gestire il metodo
doPost
il
metodo
public
synchronized void doPost(HttpServletRequest request,
HttpServletResponse response)
costituisce
il cuore della servlet. In primo luogo è utile far notare che il
metodo è stato dicharato synchronized; ad ogni richiesta da parte
dei client, il server web attiva un nuovo processo; ogni processo utilizza
le risorse dell’unica istanza della classe servlet creata. Poiché
ad ogni richiesta la classe Cartolina.java fa uso di risorse condivise
(principalmente nella lettura e scrittura del disco) risulta necessario
evitare che due processi si sovrappongono. Teoricamente sarebbe sufficiente
isolare e sincronizzare soltanto i punti critici, ma poiché tali
punti sono distribuiti praticamente in tutto il metodo, è risultato
più semplice sincronizzare l’intero metodo. L’effetto collaterale
risulta minimo: nelle poche occasioni in cui due utenti invocano contemporaneamente
la servlet, il secondo sarà costretto ad attendere alcuni decimi,
finché il server non ha finito di rispondere al primo. Visti i tempi
di risposta della rete, tutto ciò non costituisce un problema.
Il
metodo legge le informazioni che si aspetta di ottenere dal client, attraverso
la funzione request.getParameter(nomeparametro), e delega ad una funzione
separata il controllo delle informazioni:
boolean prosegui = controlla (request, response);
if (prosegui == false) return;
la
funzione controlla (request, response) si assicura che tutti i parametri
siano presenti;
in
caso affermativo ritorna la variabile booleana true, altrimenti si occupa
di riproporre all’utente la form, pregandolo di compilare tutti i campi
richiesti.
public
boolean controlla (HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
boolean complete = true;
if ((nomemitt == null) || (nomemitt.equalsIgnoreCase (""))) complete =
false;
if ((mailmitt == null) || (mailmitt.equalsIgnoreCase (""))) complete =
false;
if ((nomedest == null) || (nomedest.equalsIgnoreCase (""))) complete =
false;
if ((maildest == null) || (maildest.equalsIgnoreCase (""))) complete =
false;
if (complete == true) return true;
//
i dati non sono completi: genera una pagina di risposta
//
riproponendo la form all’utente
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
out.println("<HTML><HEAD><TITLE>Cartolina elettronica</TITLE>");
out.println("</head><BODY BGCOLOR=\"ffffff\">");
out.println("<TABLE BORDER=\"0\" CELLPADDING=\"2\" VALIGN=\"top\" width=\"750\"
align=\"center\">");
out.println("<tr><td VALIGN=\"middle\">");
out.println("</td>");
out.println("<td VALIGN=\"middle\"><h2 STYLE=\"font-size:15pt; font-family:verdana\">");
out.println("Attenzione: parametri mancanti! tutti i campi devono essere
compilati</h2></tr></table>");
out.println("<form action=\"" + servletpath + "\" method=\"post\">");
out.println("<input name=\"immagine\" type=\"hidden\" value=\"" + immagine
+ "\">");
out.println("<table><tr><td>L'immagine della cartolina<br>the
image of your e-card</td>");
out.println("<td><img src=\"" + immagine + " alt=\"l'immagine\"
border=\"0\"></td></tr>");
if (nomemitt == null) nomemitt = "";
if (mailmitt == null) mailmitt = "";
if (nomedest == null) nomedest = "";
if (maildest == null) maildest = "";
out.println("<tr><td>Inserisci il tuo nome<br>insert your name</td>");
out.println("<td><input name=\"nomemitt\" type=\"text\" value=\""+
nomemitt + "\"></td></tr>");
out.println("<tr><td>La tua e-mail<br>your e-mail</td>");
out.println("<td><input name=\"mailmitt\" type=\"text\" value=\""+
mailmitt + "\"></td></tr>");
out.println("<tr><td>Il nome del destinatario<br>the name of the
receiver</td>");
out.println("<td><input name=\"nomedest\" type=\"text\" value=\""+
nomedest + "\"></td></tr>");
out.println("<tr><td>L'e-mail del destinatario<br>the receiver
e-mail</td>");
out.println("<td><input name=\"maildest\" type=\"text\" value=\""+
maildest + "\"></td></tr>");
out.println("<tr><td>Il testo della cartolina<br>the text of your
e-card</td>");
out.println("<td><textarea name=\"mailtext\" rows=\"5\" cols=\"50\"
></textarea></td></tr>");
out.println("</table><input type=\"submit\" value=\"Invia / Submit\"></form></body></html>");
return false;
}
Rintengo
non sia necessario entrare nei dettagli: la funzione controlla che le variabili
richieste ritornino una stringa non vuota. Se almeno uno dei campi non
è completo, la variabile complete viene settata a false; se tutte
le variabili sono presenti la funzione ritorna true, ed il controllo ritorna
alla funzione doPost. In caso di dati mancanti viene generata la risposta
all’utente; le variabili null vengono impostate come stringhe vuote, ed
i valori delle variabili vengono assegnati al parametro value del campo
corrispondente. In questo modo l’utente non dovrà riscrivere i campi
già compilati, ma soltanto quelli mancanti.
Generare la cartolina
// incremento il contatore
contatore++;
// creo la stringa giorno, che contiene la data
// nella forma aaaammgg, ad esempio 20000927
Calendar calendar = new GregorianCalendar();
String giorno = "" + calendar.get(Calendar.YEAR);
if (calendar.get(Calendar.MONTH) < 9)
giorno +="0";
giorno += "" + (calendar.get(Calendar.MONTH)+ 1);
if (calendar.get(Calendar.DATE) < 10)
giorno +="0";
giorno += "" + calendar.get(Calendar.DATE);
File countfile = new File (countfilename);
DataOutputStream countos = new DataOutputStream (new FileOutputStream (countfile));
countos.writeBytes ("" + contatore + "\t" + giorno); // set the last number
and the last date
countos.close ();
// aggiungo il contatore alla stringa giorno
// il risultato sarà del tipo 20000927_247
giorno += "_" + contatore;
// creo un file all’interno di una directory del mio web server
// ad esempio /home/httpd/html/cartoline/20000927_247.html
// ed attraverso un outputstream genero il codice html della cartolina
File dest= new File (serverdirpath + giorno + ".html");
DataOutputStream cartolina = new DataOutputStream (new FileOutputStream
(dest));
cartolina.writeBytes ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0
Transitional//EN\">");
cartolina.writeBytes ("<HTML><HEAD><TITLE>" + cardFromTitle +
" " + nomemitt + "</TITLE>");
cartolina.writeBytes ("<style>\nTD {font-size:11pt; font-family:verdana}\n</style>");
cartolina.writeBytes ("</HEAD>\n<BODY bgColor=\"#ffffff\">\n<H3>");
cartolina.writeBytes ("<CENTER><FONT face=\"verdana\">" + CustomCardTitle
+ "</FONT></CENTER></H3>\n<CENTER>");
cartolina.writeBytes ("<TABLE bgColor=\"#ffffff\" border=\"0\" cellPadding=\"5\"
cellSpacing=\"0\" width=\"750\">");
cartolina.writeBytes ("<TR><TD width=\"320\" align=\"right\" rowspan=\"2\">");
// se la variabile immagine contiene un riferimento assoluto si usa quello
// altrimenti si aggiunge la dir predefinita
if (!immagine.startsWith ("http")) immagine = imageURL + immagine;
cartolina.writeBytes ("<img src=\"" + immagine + "\" border=\"0\">\n</TD>");
cartolina.writeBytes ("<TD width=\"300\">da: " + nomemitt + "<BR>a:
" + nomedest + "</TD>");
// francobollo contiene l’url di una piccola immagine “francobollo”
cartolina.writeBytes ("<TD width=\"120\" align=\"right\">" + francobollo
+"</TD>");
cartolina.writeBytes ("</TR>\n<TR><TD vAlign=\"center\" colspan=\"2\">"
+ request.getParameter("mailtext"));
cartolina.writeBytes ("</TD></TR></TABLE>");
referer = request.getHeader("referer");
if (referer != null) {
cartolina.writeBytes ("<p><A href=\"" + referer + "\">" + cardsend
+ "</a></FONT></p>");
}
cartolina.writeBytes ("</CENTER></BODY></HTML>");
Le
parti più salienti del codice sono state commentate. Il nome della
cartolina è nel formato data_contatore. Per ricostruire le cifre
della data viene utilizzata la classe Calendar. Uno dei passaggi più
interessanti riguarda la variabile referer. referer è una delle
variabili che, attraverso http, il browser invia al server: la variabile
contiene l’url della pagina da cui l’utente è giunto alla risorsa
richiesta (nel nostro caso la servlet). Utilizzando la variabile referer
noi permettiamo a chi vede la cartolina di ritornare alla form iniziale
(per spedire a sua volta una cartolina, ad esempio); questo ci permette
di utilizzare la servlet a partire da pagine diverse, potenzialmente da
siti diversi; in questo modo la nostra servlet può essere utilizzata
anche da altri siti, a patto che creino una form contenente le variabili
richieste. Se al contrario vogliamo escludere questa possibilità,
sarà utile utilizzare proprio la variabile referer per bloccarne
l’uso, ad esempio introducendo le seguenti righe:
if
(!referer.startsWith ("http://nomemioserver"))
// invia una pagina di feedback di errore
out.println("<html>\n<head><title>Accesso negato</title>\n<body>");
out.println("h1>Questo servizio è disponibile solo all’interno del
sito nomemioserver </h1></body></html>");
Inviare la mail
di notifica
L’invio
di un messaggio di notifica viene delegato ad una classe esterna, SendMail.java.
Ci occuperemo del funzionamento di questa classe (che potete scaricare
da http://www.hyperlab.net/java/source/SendMail.java) in un successivo
articolo. Per ora ci limitiamo ad osservare che il metodo sendMail “dialoga”
con il server SMTP e gli invia i parametri necessari ad inviare una e-mail.
I
parametri che la classe utilizza sono il corpo della mail, in cui va specificato
il nome del mittente e l’url della cartolina; il mittente della mail, che
corrisponde all’indirizzo del mittente, l’indirizzo del vostro server mail,
che si occuperà di inviare fisicamente il messaggio, ed il soggetto
della mail.
String corpo = nomemitt + " ti ha spedito una cartolina elettronica\n";
corpo += "La puoi leggere all'indirizzo "+ URLpath + giorno +".html\n";
corpo += "se vuoi spedire una cartolina elettronica, vai su" + referer;
corpo += "\n--------------------------------------------\n";
//
un link al vostro sito, nel corpo della mail
corpo += "http://nomemioserver";
String subject = "Una cartolina per te";
SendMail send = new SendMail ();
Vector tracer = new Vector ();
send.sendMail(mailserver, mailserver, mailmitt, maildest, subject, corpo,
tracer);
Inviare il feedback
al mittente
Una
volta generata la cartolina ed inviato il messaggio di posta elettronica
al destinatario, è necessario notificare al mittente il buon esito
dell’operazione. Questo messaggio costituisce la risposta all’utente che
ha utilizzato la servlet, e costituisce dunque la parte più “tradizionale”
del metodo.
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>\n<body>\n<head>");
out.println("<title>Cartolina inviata</title>");
out.println("<body bgcolor=\"white\">");
out.println("la cartolina è stata creata ed un messaggio di notifica
è stato spedito a " + nomedest);
out.println("all'indirizzo <a href=\"mailto:" + maildest +"\">"+ maildest
+ "</a><br>\n");
out.println("la cartolina può essere visualizzata all'indirizzo
<a href=\"" + URLpath + giorno +".html\">");
out.println("" + URLpath + giorno +".html</a>\n</body></html>\n");
Il
codice utilizza la variabile response per stabilire il contenuto della
pagina di risposta ("text/html") e per ottenere uno stream di output: PrintWriter
out. Attraverso la variabile out la servlet scrive il codice html che verrà
visualizzato sul browser dell’utente, specificando che la mail è
stata spedita al destinatario e comunicando l’indirizzo della cartolina.
Questa parte può essere personalizzata inserendo ad esempio un link
alla home page, ad alcune sezioni del sito, oppure un banner pubblicitario.
Aggiornare
il file di log
L’ultima
operazione consiste nell’aggiungere una riga al file di log, contenente
i dati ottenuti dalla form:
RandomAccessFile
logos = new RandomAccessFile(logname, "rw");
logos.seek(logos.length());
logos.writeBytes ("" + contatore + "\t" + giorno + "\t" + immagine);
logos.writeBytes ("\t" + mailmitt + "\t" + nomemitt + "\t" + maildest +
"\t" + nomedest + "\n");
logos.close ();
Per
aggiungere una riga ad un file viene usata la classe RandomAccessFile,
che permette di scrivere su di un file a partire da un punto arbitrario.
Per accodare una riga al file (append) è sufficiente spostarsi,
con la funzione seek (int pos), al termine del file (identificato dalla
funzione length()).
Sotto
il profilo tecnico generare il file di log è piuttosto banale. Sotto
il profilo legale (e di netiquette) ricordate che, salvo espressa autorizzazione,
non potete utilizzare gli indirizzi raccolti per scopi non previsti e non
richiesti dagli utenti.
Conclusioni
La
servlet che ho descritto in questo articolo è piuttosto grezza:
nel prossimo numero introdurremo alcuni miglioramenti, quali la possibilità
di personalizzare alcuni parametri o di utilizzare la classe Locale per
realizzare una cartolina multilingue. La classe che ho descritto funziona
comunque piuttosto bene, e dimostra che utilizzando java sul lato server
è possibile aggiungere ad un sito alcuni servizi interessanti. Potete
scaricare il codice di esempio e provare il servizio dalla pagina http://www.hyperlab.net/java/ecards.html
Bibliografia
servlet
tutorial
http://java.sun.com/docs/books/tutorial/servlets/index.html
la
versione on line di Cartolina.java
http://www.hyperlab.net/java/ecards.html
Stefano
Bussolon, laureato in psicologia sperimentale. Partecipa, nella veste di
"cultore della materia", all'attività didattica dei corsi di Intelligenza
Artificiale e di Ergonomia della Facoltà di Psicologia di Padova.
Per il corso di Ergonomia ha condotto un seminario sull’usabilità
del www. Fa parte del laboratorio di realtà virtuale del dipartimento
di Psicologia Generale di Padova. E-mail: bussolon@psy.unipd.it
|