Commit 03524809 authored by Andreas Jung's avatar Andreas Jung

added support for asynchronous mail delivery using zope.sendmail

queue mechanism
parent 0e38c34a
...@@ -66,7 +66,11 @@ Zope Changes ...@@ -66,7 +66,11 @@ Zope Changes
- MailHost: now uses zope.sendmail for delivering the mail providing - MailHost: now uses zope.sendmail for delivering the mail providing
integration with the Zope transaction system (avoids sending dupe 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 - integrated ZODB 3.8
......
...@@ -17,6 +17,7 @@ $Id$ ...@@ -17,6 +17,7 @@ $Id$
import mimetools import mimetools
import rfc822 import rfc822
import time
from cStringIO import StringIO from cStringIO import StringIO
import Acquisition import Acquisition
...@@ -31,10 +32,13 @@ from DateTime import DateTime ...@@ -31,10 +32,13 @@ from DateTime import DateTime
from zope.interface import implements from zope.interface import implements
from zope.sendmail.mailer import SMTPMailer from zope.sendmail.mailer import SMTPMailer
from zope.sendmail.delivery import DirectMailDelivery from zope.sendmail.delivery import DirectMailDelivery, QueuedMailDelivery, \
QueueProcessorThread
from interfaces import IMailHost from interfaces import IMailHost
queue_threads = {}
class MailHostError(Exception): class MailHostError(Exception):
pass pass
...@@ -64,6 +68,8 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager): ...@@ -64,6 +68,8 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
security = ClassSecurityInfo() security = ClassSecurityInfo()
smtp_uid='' # Class attributes for smooth upgrades smtp_uid='' # Class attributes for smooth upgrades
smtp_pwd='' smtp_pwd=''
smtp_queue = False
smtp_queue_directory = '/tmp'
timeout=1.0 timeout=1.0
...@@ -79,7 +85,7 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager): ...@@ -79,7 +85,7 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
def __init__( self, id='', title='', smtp_host='localhost', smtp_port=25, 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 """ """Initialize a new MailHost instance """
self.id = id self.id = id
self.title = title self.title = title
...@@ -87,6 +93,8 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager): ...@@ -87,6 +93,8 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self.smtp_port = int(smtp_port) self.smtp_port = int(smtp_port)
self.smtp_uid = smtp_uid self.smtp_uid = smtp_uid
self.smtp_pwd = smtp_pwd self.smtp_pwd = smtp_pwd
self.smtp_queue = smtp_queue
self.smtp_queue_directory = smtp_queue_directory
# staying for now... (backwards compatibility) # staying for now... (backwards compatibility)
...@@ -95,7 +103,9 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager): ...@@ -95,7 +103,9 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self.smtp_port=smtp_port self.smtp_port=smtp_port
security.declareProtected(change_configuration, 'manage_makeChanges') 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' 'make the changes'
title=str(title) title=str(title)
...@@ -107,6 +117,17 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager): ...@@ -107,6 +117,17 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self.smtp_port=smtp_port self.smtp_port=smtp_port
self.smtp_uid = smtp_uid self.smtp_uid = smtp_uid
self.smtp_pwd = smtp_pwd 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: if REQUEST is not None:
msg = 'MailHost %s updated' % self.id msg = 'MailHost %s updated' % self.id
return self.manage_main( self return self.manage_main( self
...@@ -153,17 +174,55 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager): ...@@ -153,17 +174,55 @@ class MailBase(Acquisition.Implicit, OFS.SimpleItem.Item, RoleManager):
self._send( mfrom, mto, body ) 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') security.declarePrivate('_send')
def _send(self, mfrom, mto, messageText): def _send(self, mfrom, mto, messageText):
""" Send the message """ """ Send the message """
mailer = SMTPMailer(self.smtp_host, if self.smtp_queue:
int(self.smtp_port),
self.smtp_uid or None, # Start queue processor thread, if necessary
self.smtp_pwd or None self._startQueueProcessorThread()
)
delivery = DirectMailDelivery(mailer) delivery = QueuedMailDelivery(self.smtp_queue_directory)
delivery.send(mfrom, mto, messageText) else:
delivery = DirectMailDelivery(self._makeMailer())
delivery.send(mfrom, mto, messageText)
InitializeClass(MailBase) InitializeClass(MailBase)
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
Authentication ID: Username:
</div> </div>
</td> </td>
<td align="left" valign="top"> <td align="left" valign="top">
...@@ -70,6 +70,29 @@ ...@@ -70,6 +70,29 @@
value="&dtml-smtp_pwd;"/> value="&dtml-smtp_pwd;"/>
</td> </td>
</tr> </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> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
</td> </td>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment