D:\DEV\PROJECTS\Harbor_Mailer_Lib\src\kewlstuff\harbor\mailer\MailEngine.java
/*
 * MailEngine.java
 */

package kewlstuff.harbor.mailer;
import kewlstuff.harbor.mailer.interfaces.*;
import java.io.*;
import java.lang.*;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Date; 
import java.util.LinkedList;



/**
 *
 * @author Johnny Kewl
 * This little class is able to send emails
 * It uses the JavaMail libraries which are the basis for most SuperCans out there
 * Depends on mail.jar and activation.jar
 * One can send mail as a background activity, ie your clients wont stick
 * and hold while the mail tries to SMTP.
 * Qs are not thread safe thus the synchronized stuff.
 * It has 2 threading engines...
 * The one is the server and it checks the message Q... if it finds messages it
 * starts another worker thread that sends the messages.
 * The server will start a default of 3 worker senders, ie up to three mails go
 * at the same time. The reason for this is that sometimes a SMTP server can take
 * a very long time on a particular message, the idea is that the ones that want
 * to go quick can get past the slow one. Also if a message is failing, the system
 * will retry it 3 times before sending it to the bad message Q. Thus while one
 * message is giving trouble, others can go. 3 is just a number but the
 * idea is that its a background thread of very low priority, so that other server
 * activity always comes first.... mails go quietly in the background, and not so
 * many that your network stops.
 * It will log a max default of 500 bad messages... in the same format as the original
 * ones but with an error message. Thus you can write external code that checks errors
 * fixes them, maybe it fixes the domain name of the rcpt address and re-Qs it.
 * Or simply some external code that reads the error buffer and writes it to a log file.
 * Normally in internet type work the user is long gone anyway, and what one should aim
 * for is not giving the user the chance to screw up in the first place, ie if its to
 * send the administrator an email, dont let users put any email addresses in.
 * It can send attachments, but in the context of Harbor with the client being remote,
 * you will have to write some code to get the files onto the server first, or learn a
 * little more about mime attachements in email.
 * Probably not perfect, but a nice framework for you to do your own thing.
 * Remember its POJO, it works ANYWHERE, not just in a container.
  */
public class MailEngine implements Runnable,I_MailEngine {
    private final int maxFailedMails = 500;
    private final int maxNoOfConcurrentMails = 3;
    
    private Thread runner = null;
    private Session session = null;
    private LinkedList mailToGo = new LinkedList();
    private LinkedList mailThatFailed = new LinkedList();
    private int simMails = 0;
    private boolean fSessionInited = false; 
    private String smtpHost = null;
    private boolean fRunning = false;
    private boolean fDebug = false;
  
    
    /** Creates a new instance of mailEngine */
    public MailEngine() {
           reset();
    }
    
    private void reset(){
            simMails = 0;
            fRunning = false;
            fSessionInited = false; 
            String smtpHost = null;
            mailToGo.clear();
            clearFailedMail();
    }
    
    
   public int getMailQueueSize(){
        synchronized(this){
            return mailToGo.size();
        }            
    }
    
    
    public void clearFailedMail(){
        synchronized(this){
            mailThatFailed.clear();
        }            
    }
    
    public I_MailPackage getFirstFailedMail(){
        I_MailPackage mp = null;
        try{
            synchronized(this){
               if(mailThatFailed.size() > 0) 
                    mp = (I_MailPackage)mailThatFailed.removeFirst();
            }
        }catch(Exception e){mp = null;}    
        return mp;
    }    
    
    public I_MailPackage[] getAllMailThatFailed(){
        I_MailPackage[] mps = null;
        synchronized(this){
           if(mailThatFailed.size() > 0) 
                mps = (I_MailPackage[])mailThatFailed.toArray();
        }
            return mps;
    }
    
    //--------------------------
    //In JNDI mail systems, this method is essentially all they actually do... ie give you
    //a session.
    public boolean prepareSession(String smtpHost){
        smtpHost = smtpHost.trim();
        this.smtpHost = smtpHost;
        fSessionInited = true; 
        
        try{    
            java.util.Properties properties = System.getProperties();
            properties.put("mail.smtp.host", smtpHost);
            session = Session.getInstance(properties,null);
        } catch(Exception e) {
           fSessionInited = false; 
        } 
        
        return fSessionInited;
    }
   
   //-----------------------------------
   //Called when server running, or not
   public boolean queueMail(String to, String from, String subject, String sMessage, String sAttachment, String sFilename) {  
       
       MailPackage mp = new MailPackage();
       
       //MEL MOD: Must check before trimming else NULL will raise exception
       if(from == null) return false;
       if(to == null) return false;
       if(subject == null) return false;
       if(sMessage == null) return false;       
       
       if(from.length() == 0) return false;
       if(to.length() == 0) return false;
       if(subject.length() == 0) return false;
       if(sMessage.length() == 0) return false;
       
       mp.from = from.trim();
       mp.to = to.trim();
       mp.subject = subject.trim();
       mp.sMessage = sMessage;
       
       if(sAttachment != null)  mp.sAttachment = sAttachment.trim();
       if(sFilename != null)  mp.sFilename = sFilename.trim();
       
       synchronized(this){mailToGo.add(mp);}
       
       return true;

   } 
   
    public boolean isRunning(){
        synchronized(this){return fRunning;}
    } 
   
    public void startMailer(boolean fDebug){
        if(isRunning()) return;
        this.fDebug = fDebug;
        runner = new Thread(this);
        fRunning = true;
        runner.setPriority(Thread.MIN_PRIORITY); //everything else comes first
        runner.start();
   }
    
    //Will not stop instantly
    public void stopMailer(){
        fRunning = false;
    }    
    
    //--------------------------------
    //For testing outside of thread
    //ie client will wait for mail to go
    public boolean mailTo() {
          MailPackage mp = null;
          synchronized(this){
              mp = (MailPackage)mailToGo.removeFirst();
          }   
          return  mailTo(mp);
    }    
    
    //--------------------------------
    //Actual engine    
    private boolean mailTo(MailPackage mp) {
        
        if(!fSessionInited) return false;
        
       
        if(mp.to == null) return false;
        
        try{          
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(mp.from));
        msg.addRecipient(Message.RecipientType.TO, new InternetAddress(mp.to));
        msg.setSubject(mp.subject);
        msg.setSentDate(new Date());
        
            // create and fill the first message part
            MimeBodyPart mbp1 = new MimeBodyPart();
            mbp1.setText(mp.sMessage);
            
            MimeBodyPart mbp2 = new MimeBodyPart();
            if ((mp.sFilename != null) && (mp.sAttachment != null))
                if (mp.sFilename.length() > 0){
                    mbp2.setFileName(mp.sFilename);
                    mbp2.setText(mp.sAttachment, "us-ascii");
                }

            // create the Multipart, add its parts to it
            Multipart multiPart = new MimeMultipart();
            multiPart.addBodyPart(mbp1);
            if ((mp.sFilename != null) && (mp.sAttachment != null))
                if (mp.sFilename.length() > 0) multiPart.addBodyPart(mbp2);
            // add the Multipart to the message
            msg.setContent(multiPart);        
        

        Transport.send(msg);
        
        } catch(Exception e) {
             mp.errorMessage = e.getMessage();
             if(fDebug) System.out.println("MailTo Error " + e.getMessage() + "\n");
             if(fDebug) e.printStackTrace();
           return false; 
        }          
        
        return true;
    }
    
    //----------------------------------------------------------
    // Semisphore stuff
            private synchronized void waitAWhile(){
                try{
                    wait();
                }catch(InterruptedException e){}
            }     
    
            private synchronized void okLetsGo(){
                       notifyAll();
            } 
            
            private synchronized void cntMails(boolean fInc){
                       if(fInc) simMails++;
                       else simMails--;
                       if(simMails < 0) simMails = 0; //should never happen      
            }    

    //------------------------------------------------
    // Background thread
    public void run() {
            Thread worker = null;
                    
            MailPackage mp = null;
                   
            while(isRunning()){
              try{
                if(mp == null)  
                    Thread.sleep(1000 * 1); //1 second give the system a break 
              }catch(InterruptedException e){fRunning = false;}               
                
               try{
                    synchronized(this){mp = (MailPackage)mailToGo.removeFirst();}
               }catch(Exception e){mp = null;}  
              
               //if(mp == null) fRunning = false; //exit if no more messages for testing 
              
               if(mp != null){ 
                  
                            if(fDebug) System.out.println("Starting Worker " + mp.subject);
                            MailWorker mailWorker = new MailWorker(mp); 
                            worker = new Thread(mailWorker);
                            worker.setPriority(Thread.MIN_PRIORITY); //everything else comes first
                            worker.start();
                            cntMails(true); //inc
                            if(simMails > maxNoOfConcurrentMails) waitAWhile(); // dont hit the system too hard

               }//processed a mail package
            }//while server running
            
         if(fDebug) System.out.println("EXITING");    
    }
    
        //======================================================================
        // Sub worker Class
        private class MailWorker implements Runnable {
            private MailPackage mp = null;
            
           
            private MailWorker(MailPackage mp) {
                this.mp = mp;
            }            
            
            public void run() {
                
               
                     if(fDebug) System.out.println("Sending Mail " + mp.subject);
                     boolean fWaitForMailSuccess = mailTo(mp); 
                     if(fDebug) System.out.println("Mail Sent " + mp.subject);
                     
                    //Try send it... if doesnt go assume bad comms and retry 3 times
                     if(!fWaitForMailSuccess){//retry
                     if(fDebug) System.out.println("Entering Retry " + mp.subject);    
                         
                        int cntr = 0;
                        while(!fWaitForMailSuccess){
                            cntr += 1;
                            
                            if(fDebug) System.out.println("RETRY " + mp.subject + " " + cntr);
                            
                            fWaitForMailSuccess = mailTo(mp);
                            
                            if(!fWaitForMailSuccess){// give mail server time to recover
                                try{
                                    Thread.sleep(1000 * 10); //10 seconds
                                }catch(InterruptedException e){fWaitForMailSuccess = true;}
                            }
                            
                            if(cntr >= 3){ // 30 sec approx
                                if(fDebug) System.out.println("FAILED TO SEND " + mp.subject);
                                fWaitForMailSuccess = true; //Something wrong get out
                                 synchronized(this){ //add bad mail to q, dont let it get too big
                                    mailThatFailed.add(mp);
                                    while(mailThatFailed.size() > maxFailedMails){
                                        mailThatFailed.removeFirst();
                                    }
                                }                                 
                                //Report to CD Unit
                            }
                        }// end while retries
                      }//end it failed retry   
                
                 cntMails(false); //dec
                 okLetsGo(); //wakes up holding thread

            } // end run
        }// end worker class    
}





Models, Models, Models! Free Modeling Portfolio! ModelCoast. Photographers! Free Photographic Portfolios. Imagegods. Visit The Best Free Image Hosting.