Uno strumento
ormai nell'uso quotidiano
Tra tutti i
servizi che Internet mette a disposizione, la posta elettronica è
forse quello che ha avuto il maggior successo e diffusione.
Questo strumento
infatti ha raggiunto i livelli di diffusione planetari che sappiamo, proprio
grazie alla potenza che mette a disposizione in rapporto alla facilità
di gestione ed utilizzo. Leggere la posta è ormai diventata una
abitudine, (o una schiavitù, dipende dai punti di vista), a cui
difficilmente possiamo o vogliamo sottrarci.
Normalmente
si considera questo media come uno dei tanti strumenti di comunicazione
di cui l’uomo dispone, in grado di mettere in contatto due o più
persone fra loro (si pensi alle mailing list).
La posta elettronica
però non sempre viene utilizzata come strumento strettamente orientato
allo scambio di informazioni fra persone, ma come "canale" per agire su
particolari sistemi.
Già adesso,
sempre più spesso, la posta elettronica ci viene in aiuto per svolgere
alcuni compiti del tutto automatici inviando messaggi ad entità
astratte che eseguono i compiti più disparati. Un esempio di un
utilizzo, per così dire, alternativo della posta è riportato
in [1] in cui si mostra come eseguire interrogazioni ad un database remoto
semplicemente inviando messaggi ad una applicazione remota appositamente
progettata.
Un altro esempio
cui ci troviamo di fronte molto frequentemente è quello di Majordomo,
l’ormai famoso software di gestione delle iscrizioni a mailing list.
Tornando alle
questioni tecniche, consideriamo per un momento una situazione, ormai sempre
più frequente, in cui una applicazione residente su un server di
qualche tipo (tipicamente in ambito Internet), debba svolgere un certo
task ininterrottamente. Tipicamente il lavoro di questa applicazione prevede
il verificarsi di una serie di eventi che hanno una certa rilevanza: tali
eventi possono essere considerati negativi, un errore interno o esterno
alla applicazione, oppure semplicemente fatti di cui tenere memoria.
La soluzione
più diffusa per gestire situazioni di questo tipo è quella
di memorizzare le informazioni rilevanti dell’evento verificatosi in un
file, il cosiddetto file di log, file che viene periodicamente visionato
dal sistemista o dal responsabile dell’applicazione in esame. In alcuni
casi questi eventi, per la loro importanza, devono essere presi in esame
nel più breve tempo possibile; specie se gli eventi sono non periodici,
può non essere efficiente aspettare che il responsabile, per sua
iniziativa consulti il file di log. Ecco quindi che un sistema di allarme
basato su posta elettronica può risultare molto utile; la possibilità
inoltre di forwardare la posta su sistemi di comunicazione differenti (esempio
SMS) permette una infinita serie di possibilità e potenzialità.
Quello che andremo
quindi a vedere in questo articolo, è come si possa gestire la posta
elettronica in Java, e vedremo come poi sia possibile utilizzare tale tecnica,
riferendoci a quanto appena analizzato.
Come inviare
posta in Java…
Vediamo per
prima cosa come sia possibile inviare un messaggio di posta per mezzo del
JDK 1.1, mentre più avanti parleremo delle JavaMail API, che troveremo
come estensione nella prossima versione del Java Development Kit.
Sarà
più chiaro in seguito il motivo, ma definiamo questo sistema "manuale",
proprio per contrapposizione con quello di JavaMail. Il metodo manuale
è molto semplice, e permette con poche righe di codice di fare tutto
il necessario per inviare un messaggio: alla base di tutto c’è la
semplicità con cui Java permette di gestire i socket, e di manipolare
gli stream ad essi associati. L’applicazione che invia la posta deve infatti
aprire una connessione verso una macchina remota (dove è installato
un server di posta), e comunicare per mezzo del protocolloTCP/IP su una
particolare porta, genericamente la numero (in valore decimale) 25. Disporre
di una connessione verso un host x, sulla porta 25, equivale a poter comunicare
direttamente con il server di posta. Nella Figura 1 è riportato
lo schema di questo meccanismo.
Figura 1:
Il
sistema di gestione e controllo della posta denominato "manuale",
prevede l’utilizzo
di socket e stream per l’invio bidirezionale di stringhe
corrispondenti
a comandi POP3/SMTP |
|
Il tutto, che
a parole può sembrare una cosa difficile, si traduce semplicemente
nella riga di codice
Socket mailSocket =
new Socket ("www.mokabyte.it", 25);
A questo
punto, utilizzando i comandi dello standard SMTP (Simple Mail Transfer
Protocol), è possibile inviare posta, ma anche leggere quella memorizzata
nella casella postale (se ovviamente il server è lo stesso). Indipendentemente
da "chi" è in ascolto dall’altra parte del socket, è ben
noto che per poter inviare anche un solo bit nel socket, dobbiamo prima
"attaccarvici" uno stream, cosa che può essere fatta per mezzo del
metodo getOutputStream()
PrintStream
os =
new PrintStream (mailSocket.getOutputStream());
A questo
punto possiamo comunicare col server inviando i comandi secondo lo standard
SMTP.
Ad esempio per
aprire la comunicazione postale:
os.println("HELO "+Host);
dove host è
una variabile contenente il nome dell’host di ricezione. Per specificare
il mittente (importante mettere un indirizzo valido per non perdere eventuali
messaggi di ritorno in caso di errore):
os.println("MAIL FROM:
puliti@mokabyte.it");
ed il destinatario
sarà quindi:
os.println ("RCPT TO:
redazioneCP@infomedia.it");
A questo punto possiamo
iniziare a spedire il corpo della mail
os.println("DATA");
os.println("SUBJECT: una mail
di prova");
os.println("Ecco il body del
messaggio");
È fondamentale
per chiudere il messaggio inviare un "."
os.println(".");
e poi chiudere la
comunicazione ed il socket:
os.println("QUIT");
os.close();
mailSocket.close();
Come si può
notare è tutto molto semplice, anche se si deve fare attenzione
a due cose: la prima, forse ovvia, è che si deve tener conto del
fatto che si lavora con socket e con stream, per cui si dovrà tener
conto delle eventuali eccezioni.
La seconda invece
è meno immediata: nel caso in cui si volesse utilizzare il codice
mostrato poco sopra per implementare una applet che invii posta, allora
si tenga presente che per i motivi legati alla sicurezza, una applet può
collegarsi solamente con il server dal quale è stata scaricata.
Per questo se
installate l’applet sul file system di un host differente da quello su
cui è in esecuzione il mail server, esso non potrà funzionare.
Per quanto riguarda l’attach, con il metodo manuale le cose sono un po’
meno semplici: infatti, per prima cosa, si deve convertire l’attach in
BASE64 e poi inserirlo nella e-mail. Mettendo insieme i vari pezzi di codice
riportati fin qui, non è difficile costruire una applet/applicazione
che invii messaggi di posta.
Come leggere
la posta
Vediamo adesso
come sia possibile invece leggere i messaggi in giacenza su un server di
posta.
In questo caso
il protocollo utilizzato è il POP3 (acronimo di Post Office Protocol
versione 3): analogamente all’SMTP, esso è composto da una serie
di comandi che permettono di interrogare il server remoto e di ottenere
sia informazioni sui messaggi, che i messaggi veri e propri. Anche in questo
caso la connessione avviene tramite un collegamento via socket, ma questa
volta utilizzando la porta 110.
Ecco una breve
lista dei comandi principali che POP3 mette a disposizione per consentire
all’utente collegarsi (in questo caso un vero e proprio login) e richiedere
i messaggi:
-
USER: per inviare
il nome utente;
-
PASS: password utente;
-
LIST: lista dei
messaggi;
-
TOP [msg] [n]: richiedere
le prime n righe del messaggio msg;
-
RETR [msg]: richiede
tutto un messaggio;
-
QUIT: chiude il
collegamento.
Nella maggior parte
dei casi, questi comandi restituiscono un messaggio indicante l’esito del
comando: +OK se non ci sono stati problemi, -ERR in caso di errore. Dopo
l’invio di un comando via stream, è possibile leggere, sempre via
stream, il risultato che l’esecuzione del comando ha prodotto.
Vediamo alcuni
dei passi da eseguire per collegarsi ad un server POP3 e per leggere i
messaggi: per prima cosa la connessione (la variabile host rappresenta
il server su cui è in esecuzione il pop3-server):
Socket mailSocket=new Socket(Host,110);
os=new PrintStream(mailSocket.getOutputStream());
is=new DataInputStream (mailSocket.getInputStream());
Come nel caso precedente
si utilizza un socket, aperto sulla porta 110, per eseguire la connessione.
I due stream
servono per la comunicazione (sincrona): da una parte si inviano i comandi,
dall’altra si leggono i risultati ed i messaggi.
La breve sequenza
che segue mostra ad esempio come eseguire il login:
os.println("USER "+User);
// ricava le prime 3 lettere
della risposta
String uid_res=is.readLine().substring(0,3);
os.println("PASS "+Passwd);
// ricava le prime 3 lettere
della risposta
String pass_res =is.readLine().substring(0,3);
if(uid_res.compareTo("+OK")==0
&& uid_res.compareTo("+OK")!=0)
System.out.println ("Login eseguito
correttamente");
In maniera analoga,
è possibile ricavare la lista dei messaggi e scaricarli direttamente
in locale per mezzo di comandi inviati con gli stream. Ad esempio:
os.println("LIST");
String
n_messaggi=is.readLine().substring(4);
System.out.println("Ci
sono "+ n_messaggi +" messaggi");
ci permette di
sapere quanti messaggi sono nella casella di posta alla quale ci siamo
loggati. Come si può notare, analogamente al caso dell’invio di
messaggi, è tutto molto semplice. Nel file POP3Reader.java è
riportato il sorgente completo per realizzare tale sistema.
La posta nel JDK 1.2
Dal punto di
vista della programmazione Java, la tecnica manuale, non è in nessun
modo vincolata al fatto che si inviano dati ad un server di posta: ripercorrendo
infatti i vari punti, si potrebbe sintetizzare che si tratta di una comunicazione
via socket e stream con una applicazione remota (Figura 1).
Il fatto che
si inviino determinate parole (comandi SMTP) dentro lo stream, esula dall’ottica
Java: dal punto di vista della programmazione avremmo potuto inviare comandi
del tipo "per favore mi invii questa mail", anche se non avremmo ottenuto
lo stesso effetto. Questo è uno dei grossi vantaggi di Java, quello
di appoggiarsi a standard già presenti e non introdurne di nuovi
proprietari ([2]).
Anche se molto
semplice, il metodo manuale presenta delle limitazioni (ne parliamo più
approfonditamente più avanti), ed è per questo che con il
JDK 1.2 sono state introdotte le JavaMail API: esse offrono una serie di
funzionalità più potenti rispetto al metodo precedente. Questo
set di API si basa sul Java Activation Framework e sul modello dei JavaBeans.
Senza entrare troppo nei dettagli, è però interessante notare
alcune caratteristiche delle nuove API.
Per prima cosa
si deve dire che esse non fanno parte ufficialmente del JDK 1.2 ma ne sono
una estensione: ricordo che le estensioni sono API considerate non fondamentali,
che non devono essere per forza implementate e che possono essere distribuite
a parte. Di queste estensioni fanno parte tra le altre: Java3D, l’API delle
servlet ed infine le JavaMail. Una Extension API è contraddistinta
dal fatto di essere inserita in un package denominato javax invece del
classico java.
Le JavaMail
API permettono di costruire solamente la parte client del programma, lasciando
ad altri ambiti la realizzazione di server di posta, settore coperto da
programmi che svolgono il loro compito in maniera più che onorevole.
Vediamo quindi
cosa è necessario fare per inviare un messaggio utilizzando queste
nuove API. La classe fondamentale è Message, che mette a disposizione
alcune interessanti funzionalità per automatizzare il processo di
creazione ed invio del messaggio stesso. Ad esempio con le poche righe
che seguono possiamo creare un messaggio e specificarne le varie componenti:
Message msg = new Message(session);
msg.setFrom(new InternetAddress
("puliti@mokabyte.it"));
msg.setRecipient(Message.RecipientType.TO,new
IternetAddress("RedazioneCP@infomedia.it"));
msg.setSubject("Articolo per
la rubrica Java");
msg.setContent("Domani vi invio
l’articolo","text/plain");
Per la spedizione
vera e propria del messaggio invece si utilizza la classe Transport, la
quale effettua fisicamente la spedizione del messaggio ad esempio per mezzo
del metodo
public static void send(Message
msg) throws MessagingException
In caso di errore
(ad esempio l’indirizzo del destinatario è errato) viene lanciata
una eccezione di tipo SendFailedException. Come si può notare, rispetto
alla modalità precedente, le JavaMail implementano maggiormente
una filosofia ad oggetti, in linea con il modo di lavorare con Java (Figura
2).
Figura 2:
In
alternativa le JavaMail prevedono un sistema molto più potente e
flessibile, che in maniera automatica permettono di spedire messaggi direttamente
utilizzando la classe Message |
|
Esse possono
essere pensate come una interfaccia per un sistema sottostante del tutto
equivalente a quello basato sugli stream: il fatto però di utilizzare
un sistema wrapper ad oggetti, permette da un lato di nascondere le problematiche
relative all’implementazione (incapsulamento) e dall’altro di offrire un
maggior numero di funzionalità, grazie all’utilizzo trasparente
di tecniche differenti (Figura 3).
Figura 3:
Le
JavaMail, fra le altre cose, permettono di nascondere le problematiche
relative all’implementazione TCP/IP e di offrire più funzionalità,
in maniera trasparente allo sviluppatore |
|
Infine da ricordare
che oltre al framework astratto, Sun fornisce una implementazione dei protocolli
SMTP e IMAP4 (quest’ultimo è un protocollo per ricevere la posta,
più potente e flessibile del POP3). Una delle differenze che sussistono
fra i vari protocolli appena citati, è relativa alla modalità
di manipolazione della posta; in teoria si possono avere tre tipi di funzionamento:
online, offline e disconnesso.
La prima (online)
lascia la posta sul server, utilizzando direttamente i messaggi qui memorizzati
e manipolandoli da remoto. Questo modo di lavorare è una prerogativa
dell’IMAP4, dato che invece il POP3 utilizza quasi esclusivamente la modalità
"offline", (la posta viene scaricata dal client, ed utilizzata localmente).
Infine la modalità
disconnessa permette di mantenere due copie della posta, una locale e una
remota, effettuando una sincronizzazione a ogni connessione: tale metodo,
caratteristica dell‘IMAP4 non è ancora implementato dalle JavaMail,
anche se presto tale lacuna sarà colmata.
Un esempio:
un gateway di posta verso il web
Si è
già parlato precedentemente di come la posta elettronica possa essere
utilizzata per scopi differenti dal semplice scambio di messaggi: il meccanismo
di registrazione sulle mailing list, o attivazione di allarmi. Ovviamente
niente vieta di implementare un programma client di posta, anche se, vista
la presenza sul mercato di prodotti commerciali, tale possibilità
ha una utilità più didattica che altro.
Vediamo adesso
un esempio ulteriore: alcuni provider mettono a disposizione dei loro utenti
un servizio di consultazione della posta per mezzo di pagine Html: questo
sistema, utilizzando CGI, a volte risulta essere molto comodo: ad esempio
quando non si dispone di una connessione diretta verso il proprio mail
server e/o non si può utilizzare il proprio programma di posta preferito.
A volte, io
stesso, trovandomi fuori sede, ho utilizzato questo sistema per dare una
sbirciatina veloce alla posta, magari utilizzando un chiosco elettronico
nella sala d’aspetto di un aeroporto.
Quanto andiamo
a vedere adesso, liberamente ispirato ad un esempio contenuto nelle JavaMail
API di Sun, implementa proprio un meccanismo di questo tipo. Si tratta
di un servlet che collegandosi con un mail server X, effettua un gateway
verso http permettendo così ad un utente abilitato al log su X,
di visionare la lista delle mail, leggerle una per una, e/o inviarne di
nuove.
In particolare
il JavaMailServlet mette a disposizione le seguenti funzionalità:
login
verso un server IMAP server;
list
di tutti i messaggi nella casella INBOX:
view
di un messaggio selezionato;
composizione
e spedizione di un messaggio.
Nella Figura
4 è riportato il funzionamento del servlet: verso il client, per
mezzo dei metodi doGet e doPost, corrispondenti alle richieste http GET
e POST, viene instaurata una comunicazione tramite CGI.
Figura 4:
lo schema funzionale del servlet di gestione e controllo della posta. Come
si vede l’utilizzo tipico è quello in una struttura multistrato.
In questo caso la coppia servlet+WebServer eseguono un cosiddetto
gateway dei messaggi verso il mondo http |
|
In particolare
il metodo doPost() processa le richieste del client, sia per la login,
che per la visualizzazione vera e propria delle mail. Al momento del log,
il metodo doPost ricava le informazioni relative all’utente dal form Html
e le memorizza nelle variabili "hostname", "username" and "password". Successivamente
utilizza tali valori per collegarsi con il server e per leggere i messaggi
presenti nella Inbox. Per il mantenimento dello stato fra una richiesta
ed un’altra, il servlet memorizza le informazioni utilizzando la tecnica
delle Sessioni messa a disposizione dal JavaWeb Server [3].
Cliccando su
link "Inbox" della pagina Html generata, il metodo doGet(), dopo essersi
collegato con il server, per mezzo del displayMessage() visualizza una
tabella con gli header dei messaggi.
Per quanto riguarda
la composizione e spedizione dei messaggi essa è molto simile alla
lettura: al click sul link "Componi una nuova mail". Il servlet invia il
codice Html contenente il form per la creazione di una nuova mail.
Dopo che l’utente
ha riempito tutti i campi, viene eseguita una richiesta POST verso il servlet,
la quale viene evasa dal solito doPost() verso il metodo send(). Quest’ultimo
crea un nuovo messaggio con la classe MimeMessage che viene inviata per
mezzo del metodo statico Transport.send().
Conclusioni
Come si è
visto la gestione della posta in Java è una cosa piuttosto semplice,
e nella versione 1.2 del JDK, con l’introduzione delle JavaMail API, sicuramente
le potenzialità a disposizione sono sicuramente maggiori.
Pur essendo
la tecnologia Java vista in questo articoli perfettamente funzionante e
matura per essere utilizzata anche in applicazioni professionali, bisogna
fare attenzione a quello che si trova in rete: firewall, proxy ed altre
cose del genere possono a volte bloccare il normale traffico dei messaggi.
Questo ovviamente
è un fattore molto importante di cui tener conto, anche se estraneo
alla programmazione Java.
Bibliografia
[1] "MailQuery:
interrogare un database per posta", Computer Programming 56 e successivi;
[2] "Intervista
a Miko Mastumura", in "JavaOne - 3ª edizione", in Computer Programming
70;
[3] "Novità
dal lato server: JavaWebServer", Dev 55, Settembre 1998;
[4] Per scaricare
le specifiche e l’implementazione di sun di javamail: http://java.sun.com/products/javamail/index.html
[5] Javabeans
activation framework: http://java.sun.com/beans/glasgow/jaf.html;
[6] Implementazioni
del POP3: http://www.linkable.com/products/pop3/
e http://www.dog.net.uk/knife/
(anche nntp);
[7] Client di
posta scritto con javamail: http://www.ice.com/java/icemail/index.shtml. |