MokaByte Numero 27  -  Febbraio 1999
Gestire applicazioni multistrato 
con la posta elettronica
di 
Giovanni Puliti
Un utilizzo non convenzionale della posta elettronica permette  di risolvere situazioni apparentemente complesse.


 

Oltre ad essere un ottimo strumento di comunicazione la posta elettronica può essere utilizzata anche come sistema alternativo di gestione in architetture multistrato. 

 

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.


 
 
 
 
 

MokaByte Web  1999 - www.mokabyte.it

MokaByte ricerca nuovi collaboratori. 
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it