[Checkins] SVN: Zope/trunk/ added support for asynchronous mail delivery using zope.sendmail

Andreas Jung andreas at andreas-jung.com
Sun Aug 19 05:05:12 EDT 2007


Log message for revision 78983:
  added support for asynchronous mail delivery using zope.sendmail
  queue mechanism
  

Changed:
  U   Zope/trunk/doc/CHANGES.txt
  U   Zope/trunk/lib/python/Products/MailHost/MailHost.py
  U   Zope/trunk/lib/python/Products/MailHost/dtml/manageMailHost.dtml

-=-
Modified: Zope/trunk/doc/CHANGES.txt
===================================================================
--- Zope/trunk/doc/CHANGES.txt	2007-08-19 07:24:24 UTC (rev 78982)
+++ Zope/trunk/doc/CHANGES.txt	2007-08-19 09:05:12 UTC (rev 78983)
@@ -66,7 +66,11 @@
 
       - MailHost: now uses zope.sendmail for delivering the mail providing
         integration with the Zope transaction system (avoids sending dupe
-        emails in case of conflict errors)
+        emails in case of conflict errors). In addition MailHost now provides 
+        support for asynchronous mail delivery. The 'Use queue' configuration 
+        option will create a mail queue on the filesystem (under 
+        'Queue directory') and start a queue thread that checks the queue every
+        three seconds. This decouples the sending of mail from its delivery.
 
       - integrated ZODB 3.8
     

Modified: Zope/trunk/lib/python/Products/MailHost/MailHost.py
===================================================================
--- Zope/trunk/lib/python/Products/MailHost/MailHost.py	2007-08-19 07:24:24 UTC (rev 78982)
+++ Zope/trunk/lib/python/Products/MailHost/MailHost.py	2007-08-19 09:05:12 UTC (rev 78983)
@@ -17,6 +17,7 @@
 
 import mimetools
 import rfc822
+import time
 from cStringIO import StringIO
 
 import Acquisition
@@ -31,10 +32,13 @@
 
 from zope.interface import implements
 from zope.sendmail.mailer import SMTPMailer
-from zope.sendmail.delivery import DirectMailDelivery
+from zope.sendmail.delivery import DirectMailDelivery, QueuedMailDelivery, \
+                            QueueProcessorThread
 
 from interfaces import IMailHost
 
+queue_threads = {}
+
 class MailHostError(Exception):
     pass
 
@@ -64,6 +68,8 @@
     security = ClassSecurityInfo()
     smtp_uid='' # Class attributes for smooth upgrades
     smtp_pwd=''
+    smtp_queue = False
+    smtp_queue_directory = '/tmp'
 
     timeout=1.0
 
@@ -79,7 +85,7 @@
 
 
     def __init__( self, id='', title='', smtp_host='localhost', smtp_port=25, 
-                  smtp_uid='', smtp_pwd=''):
+                  smtp_uid='', smtp_pwd='', smtp_queue=False, smtp_queue_directory='/tmp'):
         """Initialize a new MailHost instance """
         self.id = id
         self.title = title
@@ -87,6 +93,8 @@
         self.smtp_port = int(smtp_port)
         self.smtp_uid = smtp_uid
         self.smtp_pwd = smtp_pwd
+        self.smtp_queue = smtp_queue
+        self.smtp_queue_directory = smtp_queue_directory
 
 
     # staying for now... (backwards compatibility)
@@ -95,7 +103,9 @@
         self.smtp_port=smtp_port
 
     security.declareProtected(change_configuration, 'manage_makeChanges')
-    def manage_makeChanges(self,title,smtp_host,smtp_port,smtp_uid='',smtp_pwd='', REQUEST=None):
+    def manage_makeChanges(self,title,smtp_host,smtp_port,smtp_uid='',smtp_pwd='', 
+                           smtp_queue=False, smtp_queue_directory='/tmp',
+                           REQUEST=None):
         'make the changes'
 
         title=str(title)
@@ -107,6 +117,17 @@
         self.smtp_port=smtp_port
         self.smtp_uid = smtp_uid
         self.smtp_pwd = smtp_pwd
+        self.smtp_queue = smtp_queue
+        self.smtp_queue_directory = smtp_queue_directory
+
+        # restart queue processor thread 
+        if self.smtp_queue:
+            self._stopQueueProcessorThread() 
+            self._startQueueProcessorThread() 
+        else:
+            self._stopQueueProcessorThread() 
+
+
         if REQUEST is not None:
             msg = 'MailHost %s updated' % self.id
             return self.manage_main( self
@@ -153,18 +174,56 @@
 
         self._send( mfrom, mto, body )
 
+
+    def _makeMailer(self):
+        """ Create a SMTPMailer """
+        return SMTPMailer(self.smtp_host,
+                          int(self.smtp_port),
+                          self.smtp_uid or None,
+                          self.smtp_pwd or None
+                          )
+
+    def _stopQueueProcessorThread(self):
+        """ Stop thread for processing the mail queue """
+
+        path = self.absolute_url(1)
+        if queue_threads.has_key(path):
+            thread = queue_threads[path]
+            thread.stop()
+            while thread.isAlive():
+                # wait until thread is really dead
+                time.sleep(0.1)
+
+
+    def _startQueueProcessorThread(self):
+        """ Start thread for processing the mail queue """
+        
+        path = self.absolute_url(1)
+
+        if not queue_threads.has_key(path):
+
+            thread = QueueProcessorThread()
+            thread.setMailer(self._makeMailer())
+            thread.setQueuePath(self.smtp_queue_directory)
+            thread.start()
+            queue_threads[path] = thread     
+
+
     security.declarePrivate('_send')
     def _send(self, mfrom, mto, messageText):
         """ Send the message """
 
-        mailer = SMTPMailer(self.smtp_host,
-                            int(self.smtp_port),
-                            self.smtp_uid or None,
-                            self.smtp_pwd or None
-                            )
-        delivery = DirectMailDelivery(mailer)
-        delivery.send(mfrom, mto, messageText)
+        if self.smtp_queue:
+            
+            # Start queue processor thread, if necessary
+            self._startQueueProcessorThread()
 
+            delivery = QueuedMailDelivery(self.smtp_queue_directory)
+        else:
+            delivery = DirectMailDelivery(self._makeMailer())
+
+        delivery.send(mfrom, mto, messageText)                
+
 InitializeClass(MailBase)
 
 

Modified: Zope/trunk/lib/python/Products/MailHost/dtml/manageMailHost.dtml
===================================================================
--- Zope/trunk/lib/python/Products/MailHost/dtml/manageMailHost.dtml	2007-08-19 07:24:24 UTC (rev 78982)
+++ Zope/trunk/lib/python/Products/MailHost/dtml/manageMailHost.dtml	2007-08-19 09:05:12 UTC (rev 78983)
@@ -51,7 +51,7 @@
   <tr>
     <td align="left" valign="top">
     <div class="form-label">
-    Authentication ID:
+    Username:
     </div>
     </td>
     <td align="left" valign="top">
@@ -72,8 +72,31 @@
   </tr>
   <tr>
     <td align="left" valign="top">
+    <div class="form-label">
+    Use queue
+    </div>
     </td>
     <td align="left" valign="top">
+    <input type="checkbox" name="smtp_queue:boolean" value="1"
+     <dtml-if "smtp_queue">checked</dtml-if>
+
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Queue directory:
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="smtp_queue_directory" size="30"
+     value="&dtml-smtp_queue_directory;"/>
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    </td>
+    <td align="left" valign="top">
     <div class="form-element">
     <input class="form-element" type="submit" name="submit" 
      value="Save Changes" /> 



More information about the Checkins mailing list