JAMES-1877 Extract DeliveryRunnable from RemoteDelivery

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/4a5a4ba6
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/4a5a4ba6
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/4a5a4ba6

Branch: refs/heads/master
Commit: 4a5a4ba660ebb8e9a6f1e2446a65527e1fa8fd37
Parents: 69f66c7
Author: Benoit Tellier <btell...@linagora.com>
Authored: Wed Nov 30 19:30:37 2016 +0700
Committer: Benoit Tellier <btell...@linagora.com>
Committed: Tue Jan 10 15:04:32 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 931 +-----------------
 .../remoteDelivery/DeliveryRunnable.java        | 963 +++++++++++++++++++
 .../remoteDelivery/VolatileIsDestroyed.java     |  36 +
 .../remoteDelivery/VolatileIsDestroyedTest.java |  41 +
 4 files changed, 1055 insertions(+), 916 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/4a5a4ba6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
index c4c18d2..e7db2c8 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
@@ -19,61 +19,34 @@
 
 package org.apache.james.transport.mailets;
 
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.ConnectException;
-import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Date;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Vector;
-import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
-import javax.mail.Address;
 import javax.mail.MessagingException;
-import javax.mail.SendFailedException;
-import javax.mail.Session;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.MimeMultipart;
-import javax.mail.internet.MimePart;
-import javax.mail.internet.ParseException;
 
 import org.apache.james.dnsservice.api.DNSService;
-import org.apache.james.dnsservice.api.TemporaryResolutionException;
-import org.apache.james.dnsservice.library.MXHostAddressIterator;
 import org.apache.james.domainlist.api.DomainList;
-import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.queue.api.MailPrioritySupport;
 import org.apache.james.queue.api.MailQueue;
 import org.apache.james.queue.api.MailQueue.MailQueueException;
-import org.apache.james.queue.api.MailQueue.MailQueueItem;
 import org.apache.james.queue.api.MailQueueFactory;
-import org.apache.james.transport.mailets.remoteDelivery.Delay;
+import org.apache.james.transport.mailets.remoteDelivery.DeliveryRunnable;
 import 
org.apache.james.transport.mailets.remoteDelivery.RemoteDeliveryConfiguration;
 import 
org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
-import org.apache.mailet.HostAddress;
+import org.apache.james.transport.mailets.remoteDelivery.VolatileIsDestroyed;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
-import org.apache.mailet.MailetContext;
 import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
 
-import com.sun.mail.smtp.SMTPTransport;
-
 /**
  * <p>The RemoteDelivery mailet delivers messages to a remote SMTP server able 
to deliver or forward messages to their final
  * destination.
@@ -144,8 +117,7 @@ import com.sun.mail.smtp.SMTPTransport;
  * <li><b>debug</b> (optional) - a Boolean (true/false) indicating whether 
debugging is on. Default is false.</li>
  * </ul>
  */
-@SuppressWarnings("deprecation")
-public class RemoteDelivery extends GenericMailet implements Runnable {
+public class RemoteDelivery extends GenericMailet {
 
     private static final String OUTGOING_MAILS = "outgoingMails";
 
@@ -154,16 +126,12 @@ public class RemoteDelivery extends GenericMailet 
implements Runnable {
     private final MailQueueFactory queueFactory;
     private final Metric outgoingMailsMetric;
     private final Collection<Thread> workersThreads;
+    private final VolatileIsDestroyed volatileIsDestroyed;
 
     private MailQueue queue;
     private Logger logger;
     private RemoteDeliveryConfiguration configuration;
 
-    /**
-     * Flag used by 'run' method to end itself.
-     */
-    private volatile boolean destroyed = false;
-
     @Inject
     public RemoteDelivery(DNSService dnsServer, DomainList domainList, 
MailQueueFactory queueFactory, MetricFactory metricFactory) {
         this.dnsServer = dnsServer;
@@ -171,6 +139,7 @@ public class RemoteDelivery extends GenericMailet 
implements Runnable {
         this.queueFactory = queueFactory;
         this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
         this.workersThreads = new Vector<Thread>();
+        this.volatileIsDestroyed = new VolatileIsDestroyed();
     }
 
     /**
@@ -195,25 +164,20 @@ public class RemoteDelivery extends GenericMailet 
implements Runnable {
     private void initDeliveryThreads() {
         for (int a = 0; a < configuration.getWorkersThreadCount(); a++) {
             String threadName = "Remote delivery thread (" + a + ")";
-            Thread t = new Thread(this, threadName);
+            Thread t = new Thread(
+                new DeliveryRunnable(queue,
+                    configuration,
+                    dnsServer,
+                    outgoingMailsMetric,
+                    logger,
+                    getMailetContext(),
+                    volatileIsDestroyed),
+                threadName);
             t.start();
             workersThreads.add(t);
         }
     }
 
-    /**
-     * This method returns, given a retry-count, the next delay time to use.
-     *
-     * @param retry_count the current retry_count.
-     * @return the next delay time to use, given the retry count
-     */
-    private long getNextDelay(int retry_count) {
-        if (retry_count > configuration.getDelayTimes().size()) {
-            return Delay.DEFAULT_DELAY_TIME;
-        }
-        return configuration.getDelayTimes().get(retry_count - 1);
-    }
-
     @Override
     public String getMailetInfo() {
         return "RemoteDelivery Mailet";
@@ -302,9 +266,7 @@ public class RemoteDelivery extends GenericMailet 
implements Runnable {
      */
     @Override
     public synchronized void destroy() {
-        // Mark flag so threads from this Mailet stop themselves
-        destroyed = true;
-
+        volatileIsDestroyed.markAsDestroyed();
         // Wake up all threads from waiting for an accept
         for (Thread t : workersThreads) {
             t.interrupt();
@@ -312,867 +274,4 @@ public class RemoteDelivery extends GenericMailet 
implements Runnable {
         notifyAll();
     }
 
-    /**
-     * Handles checking the outgoing spool for new mail and delivering them if
-     * there are any
-     */
-    @Override
-    public void run() {
-        final Session session = 
obtainSession(configuration.createFinalJavaxProperties());
-        try {
-            while (!Thread.interrupted() && !destroyed) {
-                try {
-                    // Get the 'mail' object that is ready for deliverying. If
-                    // no
-                    // message is
-                    // ready, the 'accept' will block until message is ready.
-                    // The amount
-                    // of time to block is determined by the 'getWaitTime'
-                    // method of the
-                    // MultipleDelayFilter.
-                    MailQueueItem queueItem = queue.deQueue();
-                    Mail mail = queueItem.getMail();
-
-                    String key = mail.getName();
-
-                    try {
-                        if (configuration.isDebug()) {
-                            String message = Thread.currentThread().getName() 
+ " will process mail " + key;
-                            log(message);
-                        }
-
-                        // Deliver message
-                        if (deliver(mail, session)) {
-                            // Message was successfully delivered/fully
-                            // failed...
-                            // delete it
-                            LifecycleUtil.dispose(mail);
-                            // workRepository.remove(key);
-                        } else {
-                            // Something happened that will delay delivery.
-                            // Store it back in the retry repository.
-                            // workRepository.store(mail);
-                            int retries = 0;
-                            try {
-                                retries = 
Integer.parseInt(mail.getErrorMessage());
-                            } catch (NumberFormatException e) {
-                                // Something strange was happen with the
-                                // errorMessage..
-                            }
-
-                            long delay = getNextDelay(retries);
-
-                            if (configuration.isUsePriority()) {
-                                // Use lowest priority for retries. See 
JAMES-1311
-                                
mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, 
MailPrioritySupport.LOW_PRIORITY);
-                            }
-                            queue.enQueue(mail, delay, TimeUnit.MILLISECONDS);
-                            LifecycleUtil.dispose(mail);
-
-                            // This is an update, so we have to unlock and
-                            // notify or this mail is kept locked by this
-                            // thread.
-                            // workRepository.unlock(key);
-
-                            // Note: We do not notify because we updated an
-                            // already existing mail and we are now free to
-                            // handle
-                            // more mails.
-                            // Furthermore this mail should not be processed 
now
-                            // because we have a retry time scheduling.
-                        }
-
-                        // Clear the object handle to make sure it recycles
-                        // this object.
-                        mail = null;
-                        queueItem.done(true);
-                    } catch (Exception e) {
-                        // Prevent unexpected exceptions from causing looping 
by
-                        // removing message from outgoing.
-                        // DO NOT CHANGE THIS to catch Error! For example, if
-                        // there were an OutOfMemory condition caused because
-                        // something else in the server was abusing memory, we
-                        // would
-                        // not want to start purging the retrying spool!
-                        log("Exception caught in RemoteDelivery.run()", e);
-                        LifecycleUtil.dispose(mail);
-                        // workRepository.remove(key);
-                        queueItem.done(false);
-                        throw new MailQueueException("Unable to perform 
dequeue", e);
-                    }
-
-                } catch (Throwable e) {
-                    if (!destroyed) {
-                        log("Exception caught in RemoteDelivery.run()", e);
-                    }
-                }
-            }
-        } finally {
-            // Restore the thread state to non-interrupted.
-            Thread.interrupted();
-        }
-    }
-
-    /**
-     * We can assume that the recipients of this message are all going to the
-     * same mail server. We will now rely on the DNS server to do DNS MX record
-     * lookup and try to deliver to the multiple mail servers. If it fails, it
-     * should throw an exception.
-     *
-     * @param mail    org.apache.james.core.MailImpl
-     * @param session javax.mail.Session
-     * @return boolean Whether the delivery was successful and the message can
-     *         be deleted
-     */
-    private boolean deliver(Mail mail, Session session) {
-        try {
-            if (configuration.isDebug()) {
-                log("Attempting to deliver " + mail.getName());
-            }
-            MimeMessage message = mail.getMessage();
-
-            // Create an array of the recipients as InternetAddress objects
-            Collection<MailAddress> recipients = mail.getRecipients();
-            InternetAddress addr[] = new InternetAddress[recipients.size()];
-            int j = 0;
-            for (Iterator<MailAddress> i = recipients.iterator(); i.hasNext(); 
j++) {
-                MailAddress rcpt = i.next();
-                addr[j] = rcpt.toInternetAddress();
-            }
-
-            if (addr.length <= 0) {
-                log("No recipients specified... not sure how this could have 
happened.");
-                return true;
-            }
-
-            // Figure out which servers to try to send to. This collection
-            // will hold all the possible target servers
-            Iterator<HostAddress> targetServers;
-            if (configuration.getGatewayServer() == null) {
-                MailAddress rcpt = recipients.iterator().next();
-                String host = rcpt.getDomain();
-
-                // Lookup the possible targets
-                try {
-                    targetServers = new 
MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, 
false, logger);
-                } catch (TemporaryResolutionException e) {
-                    log("Temporary problem looking up mail server for host: " 
+ host);
-                    String exceptionBuffer = "Temporary problem looking up 
mail server for host: " + host + ".  I cannot determine where to send this 
message.";
-
-                    // temporary problems
-                    return failMessage(mail, new 
MessagingException(exceptionBuffer), false);
-                }
-                if (!targetServers.hasNext()) {
-                    log("No mail server found for: " + host);
-                    String exceptionBuffer = "There are no DNS entries for the 
hostname " + host + ".  I cannot determine where to send this message.";
-
-                    int retry = 0;
-                    try {
-                        retry = Integer.parseInt(mail.getErrorMessage());
-                    } catch (NumberFormatException e) {
-                        // Unable to parse retryCount
-                    }
-                    if (retry == 0 || retry > 
configuration.getDnsProblemRetry()) {
-                        // The domain has no dns entry.. Return a permanent
-                        // error
-                        return failMessage(mail, new 
MessagingException(exceptionBuffer), true);
-                    } else {
-                        return failMessage(mail, new 
MessagingException(exceptionBuffer), false);
-                    }
-                }
-            } else {
-                targetServers = 
getGatewaySMTPHostAddresses(configuration.getGatewayServer());
-            }
-
-            MessagingException lastError = null;
-
-            while (targetServers.hasNext()) {
-                try {
-
-                    Properties props = session.getProperties();
-                    if (mail.getSender() == null) {
-                        props.put("mail.smtp.from", "<>");
-                    } else {
-                        String sender = mail.getSender().toString();
-                        props.put("mail.smtp.from", sender);
-                    }
-
-                    HostAddress outgoingMailServer = targetServers.next();
-                    StringBuilder logMessageBuffer = new 
StringBuilder(256).append("Attempting delivery of 
").append(mail.getName()).append(" to host 
").append(outgoingMailServer.getHostName()).append(" at 
").append(outgoingMailServer.getHost()).append(" from 
").append(props.get("mail.smtp.from"))
-                            .append(" for addresses 
").append(Arrays.asList(addr));
-                    log(logMessageBuffer.toString());
-
-                    // Many of these properties are only in later JavaMail
-                    // versions
-                    // "mail.smtp.ehlo" //default true
-                    // "mail.smtp.auth" //default false
-                    // "mail.smtp.dsn.ret" //default to nothing... appended as
-                    // RET= after MAIL FROM line.
-                    // "mail.smtp.dsn.notify" //default to nothing...appended 
as
-                    // NOTIFY= after RCPT TO line.
-
-                    SMTPTransport transport = null;
-                    try {
-                        transport =  (SMTPTransport) 
session.getTransport(outgoingMailServer);
-                        transport.setLocalHost( 
props.getProperty("mail.smtp.localhost", 
configuration.getHeloNameProvider().getHeloName()) );
-                        try {
-                            if (configuration.getAuthUser() != null) {
-                                
transport.connect(outgoingMailServer.getHostName(), 
configuration.getAuthUser(), configuration.getAuthPass());
-                            } else {
-                                transport.connect();
-                            }
-                        } catch (MessagingException me) {
-                            // Any error on connect should cause the mailet to
-                            // attempt
-                            // to connect to the next SMTP server associated
-                            // with this
-                            // MX record. Just log the exception. We'll worry
-                            // about
-                            // failing the message at the end of the loop.
-
-                            // Also include the stacktrace if debug is 
enabled. See JAMES-1257
-                            if (configuration.isDebug()) {
-                                log(me.getMessage(), me.getCause());
-                            } else {
-                                log(me.getMessage());
-                            }
-                            continue;
-                        }
-                        // if the transport is a SMTPTransport (from sun) some
-                        // performance enhancement can be done.
-                        if 
(transport.getClass().getName().endsWith(".SMTPTransport")) {
-                            boolean supports8bitmime = false;
-                            try {
-                                Method supportsExtension = 
transport.getClass().getMethod("supportsExtension", new Class[]{String.class});
-                                supports8bitmime = (Boolean) 
supportsExtension.invoke(transport, "8BITMIME");
-                            } catch (NoSuchMethodException nsme) {
-                                // An SMTPAddressFailedException with no
-                                // getAddress method.
-                            } catch (IllegalAccessException iae) {
-                            } catch (IllegalArgumentException iae) {
-                            } catch (InvocationTargetException ite) {
-                                // Other issues with getAddress invokation.
-                            }
-
-                            // if the message is alredy 8bit or binary and the
-                            // server doesn't support the 8bit extension it has
-                            // to be converted to 7bit. Javamail api doesn't
-                            // perform
-                            // that conversion, but it is required to be a
-                            // rfc-compliant smtp server.
-
-                            // Temporarily disabled. See JAMES-638
-                            if (!supports8bitmime) {
-                                try {
-                                    convertTo7Bit(message);
-                                } catch (IOException e) {
-                                    // An error has occured during the 7bit
-                                    // conversion.
-                                    // The error is logged and the message is
-                                    // sent anyway.
-
-                                    log("Error during the conversion to 7 
bit.", e);
-                                }
-                            }
-                        } else {
-                            // If the transport is not the one
-                            // developed by Sun we are not sure of how it
-                            // handles the 8 bit mime stuff,
-                            // so I convert the message to 7bit.
-                            try {
-                                convertTo7Bit(message);
-                            } catch (IOException e) {
-                                log("Error during the conversion to 7 bit.", 
e);
-                            }
-                        }
-                        transport.sendMessage(message, addr);
-                    } finally {
-                        if (transport != null) {
-                            try {
-                                // James-899: transport.close() sends QUIT to
-                                // the server; if that fails
-                                // (e.g. because the server has already closed
-                                // the connection) the message
-                                // should be considered to be delivered because
-                                // the error happened outside
-                                // of the mail transaction (MAIL, RCPT, DATA).
-                                transport.close();
-                            } catch (MessagingException e) {
-                                log("Warning: could not close the SMTP 
transport after sending mail (" + mail.getName() + ") to " + 
outgoingMailServer.getHostName() + " at " + outgoingMailServer.getHost() + " 
for " + mail.getRecipients() + "; probably the server has already closed the "
-                                        + "connection. Message is considered 
to be delivered. Exception: " + e.getMessage());
-                            }
-                            transport = null;
-                        }
-                    }
-                    logMessageBuffer = new StringBuilder(256).append("Mail 
(").append(mail.getName()).append(") sent successfully to 
").append(outgoingMailServer.getHostName()).append(" at 
").append(outgoingMailServer.getHost()).append(" from 
").append(props.get("mail.smtp.from")).append(" for ")
-                            .append(mail.getRecipients());
-                    log(logMessageBuffer.toString());
-                    outgoingMailsMetric.increment();
-                    return true;
-                } catch (SendFailedException sfe) {
-                    logSendFailedException(sfe);
-
-                    if (sfe.getValidSentAddresses() != null) {
-                        Address[] validSent = sfe.getValidSentAddresses();
-                        if (validSent.length > 0) {
-                            String logMessageBuffer = "Mail (" + 
mail.getName() + ") sent successfully for " + Arrays.asList(validSent);
-                            log(logMessageBuffer);
-                        }
-                    }
-
-                    /*
-                     * SMTPSendFailedException introduced in JavaMail 1.3.2, 
and
-                     * provides detailed protocol reply code for the operation
-                     */
-                    if 
(sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
-                        try {
-                            int returnCode = (Integer) invokeGetter(sfe, 
"getReturnCode");
-                            // if 5xx, terminate this delivery attempt by
-                            // re-throwing the exception.
-                            if (returnCode >= 500 && returnCode <= 599)
-                                throw sfe;
-                        } catch (ClassCastException cce) {
-                        } catch (IllegalArgumentException iae) {
-                        }
-                    }
-
-                    if (sfe.getValidUnsentAddresses() != null && 
sfe.getValidUnsentAddresses().length > 0) {
-                        if (configuration.isDebug())
-                            log("Send failed, " + 
sfe.getValidUnsentAddresses().length + " valid addresses remain, continuing 
with any other servers");
-                        lastError = sfe;
-                    } else {
-                        // There are no valid addresses left to send, so 
rethrow
-                        throw sfe;
-                    }
-                } catch (MessagingException me) {
-                    // MessagingException are horribly difficult to figure out
-                    // what actually happened.
-                    String exceptionBuffer = "Exception delivering message (" 
+ mail.getName() + ") - " + me.getMessage();
-                    log(exceptionBuffer);
-                    if ((me.getNextException() != null) && 
(me.getNextException() instanceof java.io.IOException)) {
-                        // This is more than likely a temporary failure
-
-                        // If it's an IO exception with no nested exception,
-                        // it's probably
-                        // some socket or weird I/O related problem.
-                        lastError = me;
-                        continue;
-                    }
-                    // This was not a connection or I/O error particular to one
-                    // SMTP server of an MX set. Instead, it is almost 
certainly
-                    // a protocol level error. In this case we assume that this
-                    // is an error we'd encounter with any of the SMTP servers
-                    // associated with this MX record, and we pass the 
exception
-                    // to the code in the outer block that determines its
-                    // severity.
-                    throw me;
-                }
-            } // end while
-            // If we encountered an exception while looping through,
-            // throw the last MessagingException we caught. We only
-            // do this if we were unable to send the message to any
-            // server. If sending eventually succeeded, we exit
-            // deliver() though the return at the end of the try
-            // block.
-            if (lastError != null) {
-                throw lastError;
-            }
-        } catch (SendFailedException sfe) {
-            logSendFailedException(sfe);
-
-            // Copy the recipients as direct modification may not be possible
-            Collection<MailAddress> recipients = new 
ArrayList<MailAddress>(mail.getRecipients());
-
-            boolean deleteMessage = false;
-
-            /*
-             * If you send a message that has multiple invalid addresses, 
you'll
-             * get a top-level SendFailedException that that has the valid,
-             * valid-unsent, and invalid address lists, with all of the server
-             * response messages will be contained within the nested 
exceptions.
-             * [Note: the content of the nested exceptions is implementation
-             * dependent.]
-             * 
-             * sfe.getInvalidAddresses() should be considered permanent.
-             * sfe.getValidUnsentAddresses() should be considered temporary.
-             * 
-             * JavaMail v1.3 properly populates those collections based upon 
the
-             * 4xx and 5xx response codes to RCPT TO. Some servers, such as
-             * Yahoo! don't respond to the RCPT TO, and provide a 5xx reply
-             * after DATA. In that case, we will pick up the failure from
-             * SMTPSendFailedException.
-             */
-
-            /*
-             * SMTPSendFailedException introduced in JavaMail 1.3.2, and
-             * provides detailed protocol reply code for the operation
-             */
-            try {
-                if 
(sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
-                    int returnCode = (Integer) invokeGetter(sfe, 
"getReturnCode");
-                    // If we got an SMTPSendFailedException, use its RetCode to
-                    // determine default permanent/temporary failure
-                    deleteMessage = (returnCode >= 500 && returnCode <= 599);
-                } else {
-                    // Sometimes we'll get a normal SendFailedException with
-                    // nested SMTPAddressFailedException, so use the latter
-                    // RetCode
-                    MessagingException me = sfe;
-                    Exception ne;
-                    while ((ne = me.getNextException()) != null && ne 
instanceof MessagingException) {
-                        me = (MessagingException) ne;
-                        if 
(me.getClass().getName().endsWith(".SMTPAddressFailedException")) {
-                            int returnCode = (Integer) invokeGetter(me, 
"getReturnCode");
-                            deleteMessage = (returnCode >= 500 && returnCode 
<= 599);
-                        }
-                    }
-                }
-            } catch (IllegalStateException ise) {
-                // unexpected exception (not a compatible javamail
-                // implementation)
-            } catch (ClassCastException cce) {
-                // unexpected exception (not a compatible javamail
-                // implementation)
-            }
-
-            // log the original set of intended recipients
-            if (configuration.isDebug())
-                log("Recipients: " + recipients);
-
-            if (sfe.getInvalidAddresses() != null) {
-                Address[] address = sfe.getInvalidAddresses();
-                if (address.length > 0) {
-                    recipients.clear();
-                    for (Address addres : address) {
-                        try {
-                            recipients.add(new MailAddress(addres.toString()));
-                        } catch (ParseException pe) {
-                            // this should never happen ... we should have
-                            // caught malformed addresses long before we
-                            // got to this code.
-                            log("Can't parse invalid address: " + 
pe.getMessage());
-                        }
-                    }
-                    // Set the recipients for the mail
-                    mail.setRecipients(recipients);
-
-                    if (configuration.isDebug())
-                        log("Invalid recipients: " + recipients);
-                    deleteMessage = failMessage(mail, sfe, true);
-                }
-            }
-
-            if (sfe.getValidUnsentAddresses() != null) {
-                Address[] address = sfe.getValidUnsentAddresses();
-                if (address.length > 0) {
-                    recipients.clear();
-                    for (Address addres : address) {
-                        try {
-                            recipients.add(new MailAddress(addres.toString()));
-                        } catch (ParseException pe) {
-                            // this should never happen ... we should have
-                            // caught malformed addresses long before we
-                            // got to this code.
-                            log("Can't parse unsent address: " + 
pe.getMessage());
-                        }
-                    }
-                    // Set the recipients for the mail
-                    mail.setRecipients(recipients);
-                    if (configuration.isDebug())
-                        log("Unsent recipients: " + recipients);
-                    if 
(sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
-                        int returnCode = (Integer) invokeGetter(sfe, 
"getReturnCode");
-                        deleteMessage = failMessage(mail, sfe, returnCode >= 
500 && returnCode <= 599);
-                    } else {
-                        deleteMessage = failMessage(mail, sfe, false);
-                    }
-                }
-            }
-
-
-            return deleteMessage;
-        } catch (MessagingException ex) {
-            // We should do a better job checking this... if the failure is a
-            // general
-            // connect exception, this is less descriptive than more specific
-            // SMTP command
-            // failure... have to lookup and see what are the various Exception
-            // possibilities
-
-            // Unable to deliver message after numerous tries... fail
-            // accordingly
-
-            // We check whether this is a 5xx error message, which
-            // indicates a permanent failure (like account doesn't exist
-            // or mailbox is full or domain is setup wrong).
-            // We fail permanently if this was a 5xx error
-            return failMessage(mail, ex, ('5' == ex.getMessage().charAt(0)));
-        } catch (Exception ex) {
-            log("Generic exception = permanent failure: "+ex.getMessage(), ex);
-            // Generic exception = permanent failure
-            return failMessage(mail, ex, true);
-        }
-
-        /*
-         * If we get here, we've exhausted the loop of servers without sending
-         * the message or throwing an exception. One case where this might
-         * happen is if we get a MessagingException on each 
transport.connect(),
-         * e.g., if there is only one server and we get a connect exception.
-         */
-        return failMessage(mail, new MessagingException("No mail server(s) 
available at this time."), false);
-    }
-
-    /**
-     * Try to return a usefull logString created of the Exception which was
-     * given. Return null if nothing usefull could be done
-     *
-     * @param e The MessagingException to use
-     * @return logString
-     */
-    private String exceptionToLogString(Exception e) {
-        if (e.getClass().getName().endsWith(".SMTPSendFailedException")) {
-            return "RemoteHost said: " + e.getMessage();
-        } else if (e instanceof SendFailedException) {
-            SendFailedException exception = (SendFailedException) e;
-
-            // No error
-            if (exception.getInvalidAddresses().length == 0 && 
exception.getValidUnsentAddresses().length == 0)
-                return null;
-
-            Exception ex;
-            StringBuilder sb = new StringBuilder();
-            boolean smtpExFound = false;
-            sb.append("RemoteHost said:");
-
-            if (e instanceof MessagingException)
-                while ((ex = ((MessagingException) e).getNextException()) != 
null && ex instanceof MessagingException) {
-                    e = ex;
-                    if 
(ex.getClass().getName().endsWith(".SMTPAddressFailedException")) {
-                        try {
-                            InternetAddress ia = (InternetAddress) 
invokeGetter(ex, "getAddress");
-                            sb.append(" ( ").append(ia).append(" - 
[").append(ex.getMessage().replaceAll("\\n", "")).append("] )");
-                            smtpExFound = true;
-                        } catch (IllegalStateException ise) {
-                            // Error invoking the getAddress method
-                        } catch (ClassCastException cce) {
-                            // The getAddress method returned something
-                            // different than InternetAddress
-                        }
-                    }
-                }
-            if (!smtpExFound) {
-                boolean invalidAddr = false;
-                sb.append(" ( ");
-
-                if (exception.getInvalidAddresses().length > 0) {
-                    
sb.append(Arrays.toString(exception.getInvalidAddresses()));
-                    invalidAddr = true;
-                }
-                if (exception.getValidUnsentAddresses().length > 0) {
-                    if (invalidAddr)
-                        sb.append(" ");
-                    
sb.append(Arrays.toString(exception.getValidUnsentAddresses()));
-                }
-                sb.append(" - [");
-                sb.append(exception.getMessage().replaceAll("\\n", ""));
-                sb.append("] )");
-            }
-            return sb.toString();
-        }
-        return null;
-    }
-
-    /**
-     * Utility method used to invoke getters for javamail implementation
-     * specific classes.
-     *
-     * @param target the object whom method will be invoked
-     * @param getter the no argument method name
-     * @return the result object
-     * @throws IllegalStateException on invocation error
-     */
-    private Object invokeGetter(Object target, String getter) {
-        try {
-            Method getAddress = target.getClass().getMethod(getter);
-            return getAddress.invoke(target);
-        } catch (NoSuchMethodException nsme) {
-            // An SMTPAddressFailedException with no getAddress method.
-        } catch (IllegalAccessException iae) {
-        } catch (IllegalArgumentException iae) {
-        } catch (InvocationTargetException ite) {
-            // Other issues with getAddress invokation.
-        }
-        return new IllegalStateException("Exception invoking " + getter + " on 
a " + target.getClass() + " object");
-    }
-
-    /*
-     * private method to log the extended SendFailedException introduced in
-     * JavaMail 1.3.2.
-     */
-    private void logSendFailedException(SendFailedException sfe) {
-        if (configuration.isDebug()) {
-            MessagingException me = sfe;
-            if (me.getClass().getName().endsWith(".SMTPSendFailedException")) {
-                try {
-                    String command = (String) invokeGetter(sfe, "getCommand");
-                    Integer returnCode = (Integer) invokeGetter(sfe, 
"getReturnCode");
-                    log("SMTP SEND FAILED:");
-                    log(sfe.toString());
-                    log("  Command: " + command);
-                    log("  RetCode: " + returnCode);
-                    log("  Response: " + sfe.getMessage());
-                } catch (IllegalStateException ise) {
-                    // Error invoking the getAddress method
-                    log("Send failed: " + me.toString());
-                } catch (ClassCastException cce) {
-                    // The getAddress method returned something different than
-                    // InternetAddress
-                    log("Send failed: " + me.toString());
-                }
-            } else {
-                log("Send failed: " + me.toString());
-            }
-            Exception ne;
-            while ((ne = me.getNextException()) != null && ne instanceof 
MessagingException) {
-                me = (MessagingException) ne;
-                if 
(me.getClass().getName().endsWith(".SMTPAddressFailedException") || 
me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
-                    try {
-                        String action = 
me.getClass().getName().endsWith(".SMTPAddressFailedException") ? "FAILED" : 
"SUCCEEDED";
-                        InternetAddress address = (InternetAddress) 
invokeGetter(me, "getAddress");
-                        String command = (String) invokeGetter(me, 
"getCommand");
-                        Integer returnCode = (Integer) invokeGetter(me, 
"getReturnCode");
-                        log("ADDRESS " + action + ":");
-                        log(me.toString());
-                        log("  Address: " + address);
-                        log("  Command: " + command);
-                        log("  RetCode: " + returnCode);
-                        log("  Response: " + me.getMessage());
-                    } catch (IllegalStateException ise) {
-                        // Error invoking the getAddress method
-                    } catch (ClassCastException cce) {
-                        // A method returned something different than expected
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Converts a message to 7 bit.
-     *
-     * @param part
-     */
-    private void convertTo7Bit(MimePart part) throws MessagingException, 
IOException {
-        if (part.isMimeType("multipart/*")) {
-            MimeMultipart parts = (MimeMultipart) part.getContent();
-            int count = parts.getCount();
-            for (int i = 0; i < count; i++) {
-                convertTo7Bit((MimePart) parts.getBodyPart(i));
-            }
-        } else if ("8bit".equals(part.getEncoding())) {
-            // The content may already be in encoded the form (likely with mail
-            // created from a
-            // stream). In that case, just changing the encoding to
-            // quoted-printable will mangle
-            // the result when this is transmitted. We must first convert the
-            // content into its
-            // native format, set it back, and only THEN set the transfer
-            // encoding to force the
-            // content to be encoded appropriately.
-
-            // if the part doesn't contain text it will be base64 encoded.
-            String contentTransferEncoding = part.isMimeType("text/*") ? 
"quoted-printable" : "base64";
-            part.setContent(part.getContent(), part.getContentType());
-            part.setHeader("Content-Transfer-Encoding", 
contentTransferEncoding);
-            part.addHeader("X-MIME-Autoconverted", "from 8bit to " + 
contentTransferEncoding + " by " + getMailetContext().getServerInfo());
-        }
-    }
-
-    /**
-     * Insert the method's description here.
-     *
-     * @param mail      org.apache.james.core.MailImpl
-     * @param ex        javax.mail.MessagingException
-     * @param permanent
-     * @return boolean Whether the message failed fully and can be deleted
-     */
-    private boolean failMessage(Mail mail, Exception ex, boolean permanent) {
-        StringWriter sout = new StringWriter();
-        PrintWriter out = new PrintWriter(sout, true);
-        if (permanent) {
-            out.print("Permanent");
-        } else {
-            out.print("Temporary");
-        }
-
-        String exceptionLog = exceptionToLogString(ex);
-
-        StringBuilder logBuffer = new StringBuilder(64).append(" exception 
delivering mail (").append(mail.getName());
-
-        if (exceptionLog != null) {
-            logBuffer.append(". ");
-            logBuffer.append(exceptionLog);
-        }
-
-        logBuffer.append(": ");
-        out.print(logBuffer.toString());
-        if (configuration.isDebug())
-            ex.printStackTrace(out);
-        log(sout.toString());
-        if (!permanent) {
-            if (!mail.getState().equals(Mail.ERROR)) {
-                mail.setState(Mail.ERROR);
-                mail.setErrorMessage("0");
-                mail.setLastUpdated(new Date());
-            }
-
-            int retries = 0;
-            try {
-                retries = Integer.parseInt(mail.getErrorMessage());
-            } catch (NumberFormatException e) {
-                // Something strange was happen with the errorMessage..
-            }
-
-            if (retries < configuration.getMaxRetries()) {
-                logBuffer = new StringBuilder(128).append("Storing message 
").append(mail.getName()).append(" into outgoing after 
").append(retries).append(" retries");
-                log(logBuffer.toString());
-                ++retries;
-                mail.setErrorMessage(retries + "");
-                mail.setLastUpdated(new Date());
-                return false;
-            } else {
-                logBuffer = new StringBuilder(128).append("Bouncing message 
").append(mail.getName()).append(" after ").append(retries).append(" retries");
-                log(logBuffer.toString());
-            }
-        }
-
-        if (mail.getSender() == null) {
-            log("Null Sender: no bounce will be generated for " + 
mail.getName());
-            return true;
-        }
-
-        if (configuration.getBounceProcessor() != null) {
-            // do the new DSN bounce
-            // setting attributes for DSN mailet
-            String cause;
-            if (ex instanceof MessagingException) {
-                cause = getErrorMsg((MessagingException) ex);
-            } else {
-                cause = ex.getMessage();
-            }
-            mail.setAttribute("delivery-error", cause);
-            mail.setState(configuration.getBounceProcessor());
-            // re-insert the mail into the spool for getting it passed to the
-            // dsn-processor
-            MailetContext mc = getMailetContext();
-            try {
-                mc.sendMail(mail);
-            } catch (MessagingException e) {
-                // we shouldn't get an exception, because the mail was already
-                // processed
-                log("Exception re-inserting failed mail: ", e);
-            }
-        } else {
-            // do an old style bounce
-            bounce(mail, ex);
-        }
-        return true;
-    }
-
-    /**
-     * Utility method for getting the error message from the (nested) 
exception.
-     *
-     * @param me MessagingException
-     * @return error message
-     */
-    protected String getErrorMsg(MessagingException me) {
-        if (me.getNextException() == null) {
-            return me.getMessage().trim();
-        } else {
-            Exception ex1 = me.getNextException();
-            return ex1.getMessage().trim();
-        }
-    }
-
-    private void bounce(Mail mail, Exception ex) {
-        StringWriter sout = new StringWriter();
-        PrintWriter out = new PrintWriter(sout, true);
-        String machine;
-        try {
-            machine = configuration.getHeloNameProvider().getHeloName();
-
-        } catch (Exception e) {
-            machine = "[address unknown]";
-        }
-        String bounceBuffer = "Hi. This is the James mail server at " + 
machine + ".";
-        out.println(bounceBuffer);
-        out.println("I'm afraid I wasn't able to deliver your message to the 
following addresses.");
-        out.println("This is a permanent error; I've given up. Sorry it didn't 
work out.  Below");
-        out.println("I include the list of recipients and the reason why I was 
unable to deliver");
-        out.println("your message.");
-        out.println();
-        for (MailAddress mailAddress : mail.getRecipients()) {
-            out.println(mailAddress);
-        }
-        if (ex instanceof MessagingException) {
-            if (((MessagingException) ex).getNextException() == null) {
-                out.println(ex.getMessage().trim());
-            } else {
-                Exception ex1 = ((MessagingException) ex).getNextException();
-                if (ex1 instanceof SendFailedException) {
-                    out.println("Remote mail server told me: " + 
ex1.getMessage().trim());
-                } else if (ex1 instanceof UnknownHostException) {
-                    out.println("Unknown host: " + ex1.getMessage().trim());
-                    out.println("This could be a DNS server error, a typo, or 
a problem with the recipient's mail server.");
-                } else if (ex1 instanceof ConnectException) {
-                    // Already formatted as "Connection timed out: connect"
-                    out.println(ex1.getMessage().trim());
-                } else if (ex1 instanceof SocketException) {
-                    out.println("Socket exception: " + 
ex1.getMessage().trim());
-                } else {
-                    out.println(ex1.getMessage().trim());
-                }
-            }
-        }
-        out.println();
-
-        log("Sending failure message " + mail.getName());
-        try {
-            getMailetContext().bounce(mail, sout.toString());
-        } catch (MessagingException me) {
-            log("Encountered unexpected messaging exception while bouncing 
message: " + me.getMessage());
-        } catch (Exception e) {
-            log("Encountered unexpected exception while bouncing message: " + 
e.getMessage());
-        }
-    }
-
-    /**
-     * Returns the javamail Session object.
-     *
-     * @param props
-     * @return the java mail session
-     */
-    protected Session obtainSession(Properties props) {
-        return Session.getInstance(props);
-    }
-
-    /**
-     * Returns an Iterator over org.apache.mailet.HostAddress, a specialized
-     * subclass of javax.mail.URLName, which provides location information for
-     * servers that are specified as mail handlers for the given hostname. If 
no
-     * host is found, the Iterator returned will be empty and the first call to
-     * hasNext() will return false. The Iterator is a nested iterator: the 
outer
-     * iteration is over each gateway, and the inner iteration is over
-     * potentially multiple A records for each gateway.
-     *
-     * @param gatewayServers - Collection of host[:port] Strings
-     * @return an Iterator over HostAddress instances, sorted by priority
-     * @since v2.2.0a16-unstable
-     */
-    private Iterator<HostAddress> 
getGatewaySMTPHostAddresses(Collection<String> gatewayServers) {
-        Iterator<String> gateways = gatewayServers.iterator();
-
-        return new MXHostAddressIterator(gateways, dnsServer, false, logger);
-    }
-
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to