Le
API JavaMail implementano anche alcuni eventi; le operazioni
sui Folder possono essere infatti controllate da altri
oggetti tramite una serie di eventi. Ad esempio, se
una parte dell'applicazione rimuove un messaggio, i
listener registrati sul folder stesso (e sullo Store
da cui il folder deriva), ne vengono notificati. In
questo modo è possibile eseguire porzioni di
codice arbitrario a fronte di determinati eventi, quali
ad esempio la cancellazione dei messaggi. Nella tabella
3 sono riassunti i metodi per l'aggiunta dei listener
nelle classi Folder, nella tabella 4 quelli della classe
Store.
Tabella 3 - Listener di Folder
Tabella 4 - Listener di Store
Quando avviene una modifica al contenuto del folder,
viene generato un evento FolderEvent appropriato. Ad
esempio, quando viene invocato il metodo delete() su
un Folder, viene generato un evento con type = DELETED.
La classe FolderEvent dispone infatti dei metodi:
- getFolder().
Ritorna il folder coinvolto nell'operazione;
- getNewFolder().
Se l'operazione eseguita è stata una rinominazione,
questo metodo ritorna un Folder che rappresenta il
nuovo nome;
- getType().
Ritorna il tipo di operazione, che può essere
FolderEvent.CREATED, FolderEvent.DELETED, FolderEvent.RENAMED;
Nel
listato 3 è presente un esempio di utilizzo degli
eventi; in questo caso viene utilizzato l'evento MessageCountEvent,
per individuare l'arrivo di un nuovo messaggio e realizzare
così un programma monitor che segnali all'utente
che ha nuova posta.
Come si nota osservando il codice, viene fatto uso del
metodo addMessageCountListener() presente nella classe
Folder, a cui viene passato un nuovo oggetto anonimo
derivato da MessageCountAdapter. Questo implementa il
metodo messagesAdded(), metodo che indica l'aggiunta
di messaggi al Folder; quando questo metodo viene richiamato,
viene stampata a console la lunghezza dell'array di
messaggi contenuti nell'oggetto evento, di classe MessageCountEvent.
Questo array identifica appunto i messaggi coinvolti
dall'operazione, che può essere di aggiunta o
rimozione, come indicato dal metodo getType(), che può
ritornare ADDED o REMOVED.
Listato
3 - Monitor.java
package com.mokabyte.mokabook2.javamail;
import
java.util.*;
import
javax.mail.*;
import javax.mail.event.*;
public
class Monitor {
static final int millisecAttesa = 4500;
public static void main(String args[]) {
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props,
null);
Store store = session.getStore("imap");
store.connect(args[0], args[1],
args[2]);
Folder folder = store.getDefaultFolder();
if (folder != null) {
folder = folder.getFolder("INBOX");
if (folder
!= null) {
folder.open(Folder.READ_WRITE);
folder.addMessageCountListener(new
MessageCountAdapter() {
public
void messagesAdded(MessageCountEvent ev) {
Message[]
msgs = ev.getMessages();
System.out.println("Sono
arrivati " + msgs.length +
"nuovi messaggi");
}
});
System.out.println(
"Pronto a ricevere messaggi" );
for(;;)
{
Thread.sleep(
millisecAttesa );
folder.getMessageCount();
System.out.println(
"ping..." );
}
} else
{
System.out.println(
"Folder non trovato" );
}
} else {
System.out.println(
"Folder di default non trovato" );
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Per
eseguire il codice digitare:
ant
monitor
la
porzione di script di Ant che esegue il comando è
la seguente:
<target
name="testMonitor">
<java classname="com.mokabyte.mokabook2.javamail.Monitor"
fork="yes">
<classpath refid="project-classpath"/>
<arg value="pop3.server.it" />
<arg value="max/bigatti.it" />
<arg value="password" />
</java>
</target>
Messaggi
multipart
In precedenza è stato utilizzato il metodo setText()
per impostare il contenuto di un messaggio, ma in realtà
questo è un metodo di "convenienza",
che effettua alcune operazioni per impostare il tipo
di contenuto (MIME) a text/plain. JavaMail supporta
infatti messaggi di tipo multipart, composti cioè
da contenuti multipli di tipi eterogenei. Un messaggio
di posta potrebbe essere composto infatti da un contenuto
in testo semplice e da un contenuto HTML; spesso questi
messaggi di tipo ricco possono essere visualizzati in
un modo o nell'altro in funzione delle capacità
dei singoli programmi di client email. Un altro uso
dei messaggi multipart è quello legato agli allegati;
quando si allega un file di Word o Excel, un file compresso
od un'immagine ad un messaggio di posta, il client crea
un messaggio multipart creando una parte per ciascun
allegato associando alla parte il tipo MIME dell'archivio
(p.e. image/gif) ed il contenuto binario del file.
E' in questo momento che entra in gioco il JavaBeans
Activation Framework, che offre appunto il supporto
richiesto a queste operazioni con dati binari, permettendo
addirittura l'invio di oggetti Java serializzati!
Invio
di allegati
Per inviare un messaggio multipart in JavaMail le operazioni
di connessione al provider ed inizializzazione del messaggio
sono le medesime di quelle viste nel listato 1; le novità
intervengono infatti solo nella composizione del corpo
da inviare. E' necessario infatti utilizzare un oggetto
Multipart, che consente di memorizzare diverse parti
di corpo (oggetti BodyPart) e che fornisce metodi per
impostarle e ritornarle; anche in questo caso Multipart
è una classe astratta che demanda alle sottoclassi
l'effettiva realizzazione di parte dei suoi servizi;
per la posta Internet è disponibile la classe
MimeMultipart che si basa appunto sulle convenzioni
MIME.
La classe MimeMultipart definisce diversi sottotipi,
conformi alle specifiche MIME, che possono essere "mixed",
"alternative", "related" e così
via; il tipo principale è invece "multipart".
Questo approccio consente una più agevole corrispondenza
nell'Activation Framework, disaccoppiando il processo
di fornitura dei gestori delle diverse parti dalle API
JavaMail.
Nel listato 4 è presente il codice completo di
un programma di prova che esegue un invio di un messaggio
con allegato, sviluppato a partire dalla classe Invio
presentata nel listato 1. Come si nota osservando il
codice, viene per prima cosa istanziato un oggetto MimeMultipart,
che ospiterà il corpo del messaggio e subito
dopo un oggetto MimeBodyPart (sottoclasse di BodyPart)
che fornisce una implementazione MIME di una singola
parte. Nell'esempio vengono utilizzati due parti, una
per il testo del messaggio ed un'altra per l'allegato
Word. Se nel primo caso è sufficiente chiamare
il metodo setText() per impostare il contenuto della
parte, che è solo testuale, nel secondo caso
è necessario utilizzare le classi di Activation
Framework per ottenere un blocco dati binario che contenga
il documento Word.
In particolare, viene creato un nuovo DataSource a partire
dal file Documento.doc che viene letto tramite una istanza
di DataHandler:
DataSource
source = new FileDataSource( "Documento.doc"
);
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler( new DataHandler(source)
);
messageBodyPart.setFileName( "Documento.doc"
);
I
due oggetti MimeBodyPart vengono poi aggiunti all'oggetto
MimeMultipart tramite il metodo addBodyPart().
Per eseguire l'esempio digitare:
ant
invioMultipart
la
porzione di script Ant invocata è la seguente:
<target
name="testInvioMultipart">
<java classname="com.mokabyte.mokabook2.javamail.InvioMultipart"
fork="yes">
<classpath refid="project-classpath"/>
<arg value="mail.tin.it" />
</java>
</target>
Si
dovrebbe ottenere un risultato simile a quello presente
in figura 3.
Figura 3 - Il messaggio multipart è stato
ricevuto correttamente
Listato
4 - InvioMultipart.java
package com.mokabyte.mokabook2.javamail;
import
java.util.*;
import
javax.activation.*;
import javax.mail.*;
import javax.mail.internet.*;
public
class InvioMultipart {
public static void main( String[] args ) {
try {
Properties props = System.getProperties();
props.put( "mail.smtp.host", args[0] );
Session session = Session.getDefaultInstance( props
);
Message message = new MimeMessage( session );
InternetAddress from = new InternetAddress( "max@bigatti.it"
);
InternetAddress to[] = InternetAddress.parse( "max@bigatti.it"
);
message.setFrom( from );
message.setRecipients( Message.RecipientType.TO, to
);
message.setSubject( "[Mokabyte] - Messaggio multipart
di prova" );
message.setSentDate( new Date() );
Multipart multipart = new MimeMultipart();
//crea la parte testuale
BodyPart messageBodyPart1 = new MimeBodyPart();
messageBodyPart1.setText("Invio in allegato un
documento Word");
//crea l'allegato Word
DataSource source = new FileDataSource( "Documento.doc"
);
BodyPart messageBodyPart2 = new MimeBodyPart();
messageBodyPart2.setDataHandler( new DataHandler(source)
);
messageBodyPart2.setFileName( "Documento.doc"
);
//aggiunge le parti all'oggetto multipart
multipart.addBodyPart( messageBodyPart1 );
multipart.addBodyPart( messageBodyPart2 );
//imposta come contenuto del messaggio l'oggetto multipart
message.setContent(multipart);
Transport.send(message);
} catch(MessagingException e) {
e.printStackTrace();
}
}
}
Ricezione
di allegati
La ricezione di messaggi con allegati passa, in modo
similare all'invio, dalle classi Multipart e Part. Nell'esempio
riportato nel listato 5, una volta ottenuto il messaggio,
viene stampato tramite il metodo stampaMessaggio() che
limita la sua elaborazione ai soli messaggi multipart.
Tramite il metodo getCount() sull'oggetto Multipart
ottenuto con la chiamata Message.getContent() si ottiene
il numero di parti di cui è composto l'oggetto;
queste vengono estratte una ad una tramite il metodo
getBodyPart(). A questo punto viene chiamato il metodo
stampaParte() che elabora ciascuna parte; per prima
cosa viene ottenuto il tipo MIME, tramite il metodo
getContentType(). Se questo è di tipo text/plain,
il contenuto è semplice testo, che può
essere stampato direttamente a console riga per riga
tramite la classe BufferedReader; in caso di allegati
binari, viene creato un file su disco tramite la classe
FileOutputStream.
Listato
5 - RicezioneMultipart.java
package com.mokabyte.mokabook2.javamail;
import
java.io.*;
import java.util.*;
import
javax.mail.*;
import javax.mail.internet.*;
public
class RicezioneMultipart {
public static void main(String args[]) {
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props,
null);
Store store = session.getStore("pop3");
store.connect(args[0], args[1], args[2]);
Folder folder = store.getDefaultFolder();
if (folder != null) {
folder = folder.getFolder("INBOX");
if (folder != null) {
folder.open(Folder.READ_ONLY);
Message[] elencoMessaggi = folder.getMessages();
for (int indice = 0; indice < elencoMessaggi.length;
indice++) {
Message messaggio = elencoMessaggi[ indice ];
InternetAddress fromAddress = (InternetAddress)messaggio.getFrom()[0];
String from = fromAddress.getPersonal();
if( from == null ) {
from = fromAddress.toString();
}
System.out.println( "----------------------------------------"
);
System.out.println(
"DA:" + from +
" OGGETTO: " + messaggio.getSubject() +
" DATA: " + messaggio.getSentDate() +
"\n"
);
stampaMessaggio( messaggio );
}
folder.close(false);
} else {
System.out.println( "Folder non trovato" );
}
} else {
System.out.println( "Folder di default non trovato"
);
}
store.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
static void stampaMessaggio( Message msg ) throws MessagingException,
IOException {
Part msgPart = msg;
Object contenuto = msgPart.getContent();
if( contenuto instanceof Multipart ) {
Multipart mp = (Multipart)contenuto;
for( int i=0; i<mp.getCount(); i++ ) {
stampaParte( mp.getBodyPart(i), i );
}
}
}
static void stampaParte( Part parte, int count ) throws
MessagingException, IOException {
String contentType = parte.getContentType();
System.out.println( "Disposizione: " + parte.getDisposition()
);
System.out.println( "Testo: " );
if( contentType.startsWith("text/plain") )
{
InputStream in = parte.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader( in )
);
do {
String linea = reader.readLine();
if( linea == null ) {
break;
}
System.out.println( linea );
} while( true );
reader.close();
in.close();
} else {
String filename = parte.getFileName();
if( filename == null ) {
filename = "allegato" + count + ".bin";
}
FileOutputStream writer = new FileOutputStream( filename
);
byte[] buffer = new byte[ 4096 ];
InputStream in = parte.getInputStream();
while( true ) {
int readed = in.read( buffer );
if( readed == -1 ) {
break;
}
writer.write( buffer, 0, readed );
}
writer.close();
in.close();
System.out.println("Salvato il file " + filename
);
}
}
}
Per provare questo esempio digitare:
ant ricezioneMultipart
la
porzione di script di Ant relativo è la seguente:
<target
name="testRicezione">
<java classname="com.mokabyte.mokabook2.javamail.RicezioneMultipart"
fork="yes">
<classpath refid="project-classpath"/>
<arg value="pop3.server.it" />
<arg value="max/bigatti.it" />
<arg value="password" />
</java>
</target>
Un
esempio di output è il seguente (riformattato):
----------------------------------------
DA:Apple iCards
OGGETTO: Massimiliano Bigatti has sent you an Apple
iCard DATA: Thu Oct 30 10:17:28 CET 2003
Disposizione:
null
Testo:
Salvato il file allegato0.bin
Disposizione: inline
Testo:
Salvato il file iCard.jpg
Disposizione: null
Testo:
Salvato il file allegato2.bin
----------------------------------------
DA:max@bigatti.it
OGGETTO: [Mokabyte] - Messaggio multipart di prova DATA:
Fri Oct 31 21:50:02 CET 2003
Disposizione:
null
Testo:
Invio in allegato un documento Word
Disposizione: attachment
Testo:
Salvato il file Documento.doc
Conclusioni
Le API JavaMail supportano un completo insieme di funzionalità
per la gestione della posta elettronica; oltre all'invio
e ricezione di messaggi semplici e multipart, sono presenti
funzionalità di gestione della quota dell'utente,
per controllare l'occupazione di spazio delle caselle
di posta, per la ricerca di messaggi all'interno delle
caselle postali e per l'autenticazione dell'utente.
Argomenti che verranno trattati nei prossimi numeri.
Bibliografia
[1] JavaMail QuickStart - http://www.javaworld.com/javaworld/jw-10-2001/jw-1026-javamail.html
[2] Managing ezines with JavaMail and XSLT, Part 2 -
http://www-106.ibm.com/developerworks/xml/library/x-xmlist2/?open&l=842%2Ct=gr%2Cp=JavaMail2
[3] Fundamentals of the JavaMail API short course -
http://developer.java.sun.com/developer/onlineTraining/JavaMail/contents.html
[4] JavaMail Homepage - http://java.sun.com/products/javamail/
[5] JavaMail API documentation - http://java.sun.com/products/javamail/javadocs/index.html
[6] JavaBeans Activation Framework - http://java.sun.com/products/javabeans/glasgow/jaf.html
|