private void deliver( SMTPMessage message ) {
List toAddresses = message.getToAddresses();
int numAddress = toAddresses.size();
Vector failedAddress = new Vector();
EmailAddress address = null;
// If the next scheduled delivery attempt is still in the future, skip.
if( message.getScheduledDelivery().getTime() > System.currentTimeMillis() )
{
if( log.isDebugEnabled() ) log.debug( "Skipping delivery of message " + message.getMessageLocation().getName() + " because the scheduled delivery time is still in the future: " + message.getScheduledDelivery() );
return;
}
for( int index = 0; index < numAddress; index++ ) {
try {
address = (EmailAddress) toAddresses.get( index );
if( log.isDebugEnabled()) { log.debug( "Attempting to deliver message from: " + message.getFromAddress().getAddress() + " to: " + address ); }
DeliveryService deliveryService = DeliveryService.getDeliveryService();
try {
if( deliveryService.isLocalAddress( address ) ) {
deliverLocalMessage( address, message );
}
else {
deliverRemoteMessage( address, message );
}
}
catch (NotFoundException e) {
log.info( "Delivery attempted to unknown user: " + address.getAddress() );
//The addressee does not exist. Notify the sender of the error.
bounceMessage( address, message );
}
if( log.isInfoEnabled() ) { log.info( "Delivery complete for message " + message.getMessageLocation().getName() + " to: " + address ); }
}
catch( Throwable throwable ) {
log.error( "Delivery failed for message from: " + message.getFromAddress().getAddress() + " to: " + address + " - " + throwable, throwable );
failedAddress.addElement( toAddresses.get( index ) );
}
}
// If all addresses were successful, remove the message from the spool
if( failedAddress.size() == 0 ) {
// Log an error if the delete fails. This will cause the message to get
// delivered again, but it is too late to roll back the delivery.
if( !message.getMessageLocation().delete() )
{
log.error( "Error removed SMTP message after delivery! This message may be redelivered. " + message.getMessageLocation().getName() );
}
}
// Update the message with any changes.
else {
message.setToAddresses( failedAddress );
int deliveryAttempts = message.getDeliveryAttempts();
// If the message is a bounced email, just give up and move it to the failed directory.
if(message.getFromAddress().getUsername().equalsIgnoreCase("MAILER_DAEMON"))
{
try {
log.info( "Delivery of message from MAILER_DAEMON failed, moving to failed folder." );
message.moveToFailedFolder();
}
catch (Exception e) {
log.error( "Unable to move failed message to 'failed' folder." );
}
}
// If we have not passed the maximum delivery count, calculate the
// next delivery time and save the message.
else if( deliveryAttempts < configurationManager.getDeliveryAttemptThreshold() )
{
message.setDeliveryAttempts( deliveryAttempts + 1 );
// Reschedule later, 1 min, 2 min, 4 min, 8 min, ... 2^n
// Cap delivery interval at 2^10 minutes. (about 17 hours)
if( deliveryAttempts > 10 )
{
deliveryAttempts = 10;
}
long offset = (long)Math.pow( 2, deliveryAttempts);
Date schedTime = new Date(System.currentTimeMillis() + offset*60*1000);
message.setScheduledDelivery( schedTime );
try {
message.save();
}
catch( Exception exception ) {
log.error( "Error updating spooled message for next delivery. Message may be re-delivered.", exception );
}
}
// All delivery attempts failed, bounce message.
else
{
// Send a bounce message to all failed addresses.
for( int index = 0; index < failedAddress.size(); index++ ) {
try {
EmailAddress bounce_address = (EmailAddress)( failedAddress.elementAt(index) );
bounceMessage(bounce_address, message);
}
catch(Exception e) {
log.error( "Problem bouncing message. " + message.getMessageLocation().getName() );
}