• Main Page
  • Related Pages
  • Classes
  • Files
  • File List

MailTransport.java

Go to the documentation of this file.
00001 
00002 import java.util.Properties;
00003 import java.util.concurrent.LinkedBlockingQueue;
00004 
00005 import javax.mail.Message;
00006 import javax.mail.Message.RecipientType;
00007 import javax.mail.MessagingException;
00008 import javax.mail.Session;
00009 import javax.mail.Transport;
00010 import javax.mail.internet.InternetAddress;
00011 import javax.mail.internet.MimeMessage;
00012 
00013 /**
00014  *  Encapsulates mail transport agent with FIFO queue and non-blocking send
00015  *  capabilities.
00016  *  
00017  *  Instances of this class may be used in two different scenarios (note 
00018  *  the position of MailTransport.startSending() method and the usage of 
00019  *  MailTransport.noMoreMessages() bellow):
00020  *  
00021  *  <div style='color:darkred;font-weight:bold'>
00022  *  Scenario 1: Sends one ore more messages at once.</div>
00023  *  
00024  *  Usage:
00025  *        - create instance using constructor MailTransport.MailTransport() 
00026  *          (with SMTP server as parameter)
00027  *        - call MailTransport.enqueueMessage()
00028  *        - [ call MailTransport.enqueueMessage() ]...
00029  *        - call MailTransport.noMoreMessages() to terminate worker thread
00030  *        - call MailTransport.startSending()
00031  *        
00032  *  <div style='color:darkred;font-weight:bold'>
00033  *  Scenario 2. Works in background as relay client to SMTP server.</div>
00034  *
00035  *  Usage:
00036  *        - create instance using constructor MailTransport.MailTransport() 
00037  *          (with SMTP server as parameters)
00038  *        - call MailTransport.startSending()
00039  *        - call MailTransport.enqueueMessage()
00040  *        - [ call MailTransport.enqueueMessage() ]...
00041  *
00042  *  @author Mikica B Kocic
00043  */  
00044 public class MailTransport implements Runnable
00045 {
00046     /**
00047      *  Provides message presentation and call-back context for the instance
00048      *  of <code>MailTransport</code> class.
00049      */
00050     public interface Context
00051     {
00052         /**
00053          *  Logs message terminated with new line
00054          *  
00055          *  @param str    message that will be logged
00056          */
00057         public abstract void println( String str );
00058         
00059         /**
00060          *  On send completed call-back
00061          *  
00062          *  @param errorMessage    error message if <> null, otherwise success
00063          */
00064         public abstract void onSendCompleted( String errorMessage );
00065     }
00066 
00067     /**
00068      *  Instance of the main thread.
00069      */
00070     private Thread workerThread = null;
00071 
00072     /**
00073      *  Presentation contexts
00074      */
00075     private Context context = null;
00076     
00077     /**
00078      *  Session to SMTP server
00079      */
00080     private Session session = null;
00081 
00082     /**
00083      *  FIFO message queue served by worker thread
00084      */
00085     private LinkedBlockingQueue<Message> queue = null;
00086 
00087     /**
00088      *  The special message used to signal the worker thread to stop
00089      *  processing of messages and terminate.
00090      */
00091     private Message quitSignal = null;
00092 
00093     /**
00094      *  Creates instance of the mail transport with the (new) session
00095      *  to SMTP server  
00096      */
00097     public MailTransport( Context context, String smtpServer, int smtpPort )
00098     {
00099         this.context    = context;
00100         this.queue      = new LinkedBlockingQueue<Message> ();
00101         this.quitSignal = new MimeMessage( this.session );
00102 
00103         Properties relayHost = new Properties();
00104         relayHost.put( "mail.smtp.host", smtpServer );
00105         relayHost.put( "mail.smtp.port", smtpPort );
00106 
00107         /* Create instance of the session.
00108          * DO NOT USE: getDefaultInstance() as it will create cached session.
00109          */
00110         this.session = Session.getInstance( relayHost, null ); 
00111     }
00112 
00113     /**
00114      *  Gets number of messages in the queue
00115      */
00116     public int getQueueSize ()
00117     {
00118         return this.queue.size ();
00119     }
00120 
00121     /**
00122      *  Starts the worker thread (if not already started)
00123      */
00124     public void startSending ()
00125     {
00126         synchronized( this )
00127         {
00128             if ( workerThread != null ) {
00129                 return; // already started (there can be only one worker thread)
00130             }
00131 
00132             this.workerThread = new Thread( this );
00133             this.workerThread.start ();
00134         }
00135     }
00136 
00137     /**
00138      *  Sends QUIT message to the worker thread
00139      */
00140     public void noMoreMessages ()
00141     {
00142         synchronized( this )
00143         {
00144             /* put the 'QUIT' message into queue
00145              */
00146             try {
00147                 this.queue.put( this.quitSignal );
00148             } catch( InterruptedException e ) {
00149                 /* ignore */
00150             }
00151         }
00152     }
00153 
00154     /**
00155      *  Creates and puts new MIME text message into outgoing queue
00156      */
00157     public void enqueueMessage( String from, 
00158             String[] to, String subject, String textMessage 
00159             ) throws MessagingException
00160     {
00161         InternetAddress[] toAddr = new InternetAddress[ to.length ];
00162         for( int i = 0; i < to.length; i++ ) {
00163             toAddr[i] = new InternetAddress( to[i] );
00164         }
00165 
00166         MimeMessage mimeMsg = new MimeMessage( session );
00167         mimeMsg.setFrom( new InternetAddress( from ) );
00168         mimeMsg.setRecipients( Message.RecipientType.TO, toAddr );
00169         mimeMsg.setHeader( "X-Mailer", "IP1-MailSender" );
00170         mimeMsg.setSubject( subject );
00171         mimeMsg.setContent( textMessage, "text/plain" );
00172 
00173         /* Places the message into outgoing queue...
00174          */
00175         try {
00176             this.queue.put( mimeMsg );
00177         } catch( InterruptedException e ) {
00178             /* ignore */
00179         }
00180     }
00181 
00182     /**
00183      *  Waits for messages to become available in the queue, then takes
00184      *  messages (first-in first-out) from the queue and relays them through the
00185      *  session to remote SMTP server. After receiving special 'QUIT' message, 
00186      *  the worker thread terminates. In case of error, thread also quits and 
00187      *  reports the error using Context.onSendCompleted() from the MailTransport.Context 
00188      *  call-back interface.
00189      */
00190     @Override
00191     public void run ()
00192     {
00193         context.println( "MailTransport(" + workerThread.getId () 
00194                 + "): Worker thread started..." );
00195         
00196         context.println( "MailTransport(" + workerThread.getId () 
00197                 + "): Found " + queue.size() + " messages in queue." );
00198         
00199         String errorMessage = null; // error to be reported to the owner
00200         int curMessageNo = 0; // keeps track of the current message id
00201 
00202         while( true )
00203         {
00204             Message msgToSend = null;
00205 
00206             /* Retrieves and removes the head message from the queue,
00207              * waiting if necessary until an element becomes available).
00208              * Terminates the thread in case of interrupt.
00209              */
00210             try {
00211                 context.println( "MailTransport(" + workerThread.getId () 
00212                         + "): Waiting for messsages..." );
00213                 msgToSend = this.queue.take ();
00214             } catch( InterruptedException e ) {
00215                 break;
00216             }
00217 
00218             /* Terminates the thread if the last retrieved message is
00219              * the 'QUIT thread' message.
00220              */
00221             if ( msgToSend == quitSignal || msgToSend == null ) {
00222                 context.println( "MailTransport(" + workerThread.getId () 
00223                         + "): Got signal to 'QUIT'..." );
00224                 break;
00225             }
00226 
00227             /* Sends message via transport protocol
00228              */
00229             try
00230             {
00231                 ++curMessageNo;
00232                 
00233                 context.println( "MailTransport(" + workerThread.getId () 
00234                         + "): Sending message #" + curMessageNo + "..." );
00235                 
00236                 for( javax.mail.Address a : msgToSend.getFrom() ) {
00237                     context.println( "    From:    " + a.toString () );
00238                 }
00239                 for( javax.mail.Address a : msgToSend.getRecipients( RecipientType.TO ) ) {
00240                     context.println( "    To:      " + a.toString () );
00241                 }
00242                 context.println( "    Subject: " + msgToSend.getSubject () );
00243 
00244                 Transport.send( msgToSend );
00245                 
00246                 context.println( "MailTransport(" + workerThread.getId () 
00247                         + "): MESSAGE SENT!" );
00248             }
00249             catch( MessagingException e )
00250             {
00251                 context.println( "MailTransport(" + workerThread.getId () 
00252                         + "): Error\n\n" + e.toString () + "\n" );
00253                 
00254                 errorMessage = e.toString ();
00255 
00256                 break; // there is no point to continue in case of error...
00257             }
00258         }
00259         
00260         context.println( "MailTransport(" + workerThread.getId () 
00261                 + "): Thread done." );
00262         
00263         context.onSendCompleted( errorMessage );
00264     }
00265 }
00266 

Generated on Thu Dec 16 2010 12:56:05 for Send Mail Client by  doxygen 1.7.2