You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2017/01/10 14:18:16 UTC

[01/50] [abbrv] james-project git commit: JAMES-1877 RemoteDelivery should rely on constructor injection

Repository: james-project
Updated Branches:
  refs/heads/master 3b6c5e00d -> 131c01148


JAMES-1877 RemoteDelivery should rely on constructor injection


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

Branch: refs/heads/master
Commit: 788b3931e55d34f422176f6795a2ed707cbd8e87
Parents: 1cb969b
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Nov 29 10:50:38 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 11:37:10 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 39 ++++++--------------
 1 file changed, 11 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/788b3931/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 c4113b5..89a4987 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
@@ -164,8 +164,12 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     private static final Pattern PATTERN = Patterns.compilePatternUncheckedException(PATTERN_STRING);
     private static final String OUTGOING_MAILS = "outgoingMails";
 
-    @Inject
-    private DNSService dnsServer;
+    private final DNSService dnsServer;
+    private final DomainList domainList;
+    private final MailQueueFactory queueFactory;
+    private final Metric outgoingMailsMetric;
+    private final Properties defprops; // Default properties for the JavaMail Session
+    private final Collection<Thread> workersThreads;
 
     /**
      * Flag to define verbose logging messages.
@@ -225,11 +229,6 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     private boolean isBindUsed = false;
 
     /**
-     * Collection that stores all worker threads.
-     */
-    private final Collection<Thread> workersThreads = new Vector<Thread>();
-
-    /**
      * Flag used by 'run' method to end itself.
      */
     private volatile boolean destroyed = false;
@@ -239,47 +238,32 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      */
     private String bounceProcessor = null;
 
-    /**
-     * Default properties for the JavaMail Session
-     */
-    private final Properties defprops = new Properties();
 
     /**
      * The retry count dnsProblemErrors
      */
     private int dnsProblemRetry = 0;
 
-    private MailQueueFactory queueFactory;
-
     private MailQueue queue;
 
     private Logger logger;
 
     private boolean usePriority;
 
-    private DomainList domainList;
-
     private boolean startTLS = false;
 
     private boolean isSSLEnable = false;
 
-    private MetricFactory metricFactory;
-    private Metric outgoingMailsMetric;
     private HeloNameProvider heloNameProvider;
 
     @Inject
-    public void setDomainList(DomainList domainList) {
+    public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory) {
+        this.dnsServer = dnsServer;
         this.domainList = domainList;
-    }
-
-    @Inject
-    public void setMailQueueFactory(MailQueueFactory queueFactory) {
         this.queueFactory = queueFactory;
-    }
-
-    @Inject
-    public void setMetricFactory(MetricFactory metricFactory) {
-        this.metricFactory = metricFactory;
+        this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
+        this.defprops = new Properties();
+        this.workersThreads = new Vector<Thread>();
     }
 
     /**
@@ -289,7 +273,6 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      * @throws MessagingException on failure to initialize attributes.
      */
     public void init() throws MessagingException {
-        outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
         // Set isDebug flag.
         isDebug = (getInitParameter("debug") == null) ? false : Boolean.valueOf(getInitParameter("debug"));
 


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


[14/50] [abbrv] james-project git commit: JAMES-1877 Try to reduce DeliveryRunnable complexity

Posted by ro...@apache.org.
JAMES-1877 Try to reduce DeliveryRunnable complexity

 - Extract log message and bounce message generation
 - Do some IDE extraction refactoring
 - Simplify remaining debug log computation
 - Simplify comments


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

Branch: refs/heads/master
Commit: b839f8b086941c613eb9a23a9acf5594327e947a
Parents: 6774b87
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 14:09:34 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:48 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 979 ++++++++-----------
 1 file changed, 383 insertions(+), 596 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/b839f8b0/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 5a0bdf1..cb189d0 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -20,13 +20,8 @@
 package org.apache.james.transport.mailets.remoteDelivery;
 
 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;
@@ -63,6 +58,7 @@ import com.sun.mail.smtp.SMTPTransport;
 @SuppressWarnings("deprecation")
 public class DeliveryRunnable implements Runnable {
 
+    public static final boolean PERMANENT_FAILURE = true;
     private final MailQueue queue;
     private final RemoteDeliveryConfiguration configuration;
     private final DNSService dnsServer;
@@ -70,6 +66,7 @@ public class DeliveryRunnable implements Runnable {
     private final Logger logger;
     private final MailetContext mailetContext;
     private final VolatileIsDestroyed volatileIsDestroyed;
+    private final MessageComposer messageComposer;
 
     public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric, Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
         this.queue = queue;
@@ -79,6 +76,7 @@ public class DeliveryRunnable implements Runnable {
         this.logger = logger;
         this.mailetContext = mailetContext;
         this.volatileIsDestroyed = volatileIsDestroyed;
+        this.messageComposer = new MessageComposer(configuration);
     }
 
     /**
@@ -91,81 +89,29 @@ public class DeliveryRunnable implements Runnable {
         try {
             while (!Thread.interrupted() && !volatileIsDestroyed.isDestroyed()) {
                 try {
-                    // Get the 'mail' object that is ready for deliverying. If
-                    // no
-                    // message is
+                    // 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.
+                    // The amount of time to block is determined by the 'getWaitTime' method of the MultipleDelayFilter.
                     MailQueue.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;
-                            logger.debug(message);
+                            logger.debug(Thread.currentThread().getName() + " will process mail " + mail.getName());
                         }
 
-                        // 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);
+                        attemptDelivery(session, mail);
 
-                            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.
+                        // 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!
+                        // 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!
                         logger.error("Exception caught in RemoteDelivery.run()", e);
                         LifecycleUtil.dispose(mail);
-                        // workRepository.remove(key);
                         queueItem.done(false);
                         throw new MailQueue.MailQueueException("Unable to perform dequeue", e);
                     }
@@ -182,6 +128,30 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
+    private void attemptDelivery(Session session, Mail mail) throws MailQueue.MailQueueException {
+        if (deliver(mail, session)) {
+            // Message was successfully delivered/fully failed... delete it
+            LifecycleUtil.dispose(mail);
+        } else {
+            // Something happened that will delay delivery. Store it back in the retry repository.
+            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);
+        }
+    }
+
 
     /**
      * We can assume that the recipients of this message are all going to the
@@ -196,264 +166,43 @@ public class DeliveryRunnable implements Runnable {
      */
     private boolean deliver(Mail mail, Session session) {
         try {
-            if (configuration.isDebug()) {
-                logger.debug("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) {
-                logger.info("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().isEmpty()) {
-                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) {
-                    logger.info("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()) {
-                    logger.info("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());
+            Boolean host = tryDeliver(mail, session);
+            if (host != null) {
+                return host;
             }
+            /*
+             * 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);
+        } catch (SendFailedException sfe) {
+            return handleSenderFailedException(mail, sfe);
+        } 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
 
-            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);
-                    }
+            // Unable to deliver message after numerous tries... fail accordingly
 
-                    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));
-                    logger.debug(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()) {
-                                logger.debug(me.getMessage(), me.getCause());
-                            } else {
-                                logger.info(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.
-
-                                    logger.error("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) {
-                                logger.error("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) {
-                                logger.error("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());
-                    logger.debug(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);
-                            logger.debug(logMessageBuffer);
-                        }
-                    }
+            // 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) {
+            logger.error("Generic exception = permanent failure: " + ex.getMessage(), ex);
+            // Generic exception = permanent failure
+            return failMessage(mail, ex, PERMANENT_FAILURE);
+        }
+    }
 
-                    /*
-                     * 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) {
-                        }
-                    }
+    private boolean handleSenderFailedException(Mail mail, SendFailedException sfe) {
+        logSendFailedException(sfe);
 
-                    if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
-                        if (configuration.isDebug())
-                            logger.debug("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();
-                    logger.debug(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());
+        // Copy the recipients as direct modification may not be possible
+        Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());
 
-            boolean deleteMessage = false;
+        boolean deleteMessage = false;
 
             /*
              * If you send a message that has multiple invalid addresses, you'll
@@ -477,138 +226,339 @@ public class DeliveryRunnable implements Runnable {
              * 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);
-                        }
+        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)
             }
+        } 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())
-                logger.debug("Recipients: " + recipients);
+        // log the original set of intended recipients
+        if (configuration.isDebug())
+            logger.debug("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.
-                            logger.debug("Can't parse invalid address: " + pe.getMessage());
-                        }
+        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.
+                        logger.debug("Can't parse invalid address: " + pe.getMessage());
                     }
-                    // Set the recipients for the mail
-                    mail.setRecipients(recipients);
+                }
+                // Set the recipients for the mail
+                mail.setRecipients(recipients);
 
-                    if (configuration.isDebug())
-                        logger.debug("Invalid recipients: " + recipients);
-                    deleteMessage = failMessage(mail, sfe, true);
+                if (configuration.isDebug())
+                    logger.debug("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.
+                        logger.debug("Can't parse unsent address: " + pe.getMessage());
+                    }
+                }
+                // Set the recipients for the mail
+                mail.setRecipients(recipients);
+                if (configuration.isDebug())
+                    logger.debug("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;
+    }
+
+    private Boolean tryDeliver(Mail mail, Session session) throws MessagingException {
+        if (configuration.isDebug()) {
+            logger.debug("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) {
+            logger.info("No recipients specified... not sure how this could have happened.");
+            return true;
+        }
 
-            if (sfe.getValidUnsentAddresses() != null) {
-                Address[] address = sfe.getValidUnsentAddresses();
-                if (address.length > 0) {
-                    recipients.clear();
-                    for (Address addres : address) {
+        // Figure out which servers to try to send to. This collection
+        // will hold all the possible target servers
+        Iterator<HostAddress> targetServers;
+        if (configuration.getGatewayServer().isEmpty()) {
+            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) {
+                return handleTemporaryResolutionException(mail, host);
+            }
+            if (!targetServers.hasNext()) {
+                return handleNoTargetServer(mail, host);
+            }
+        } else {
+            targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
+        }
+
+        MessagingException lastError = null;
+
+        while (targetServers.hasNext()) {
+            HostAddress outgoingMailServer = targetServers.next();
+            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);
+                }
+                logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
+                    + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
+
+                // 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()) {
+                            logger.debug(me.getMessage(), me.getCause());
+                        } else {
+                            logger.info(me.getMessage());
+                        }
+                        continue;
+                    }
+                    // if the transport is a SMTPTransport (from sun) some
+                    // performance enhancement can be done.
+                    if (transport.getClass().getName().endsWith(".SMTPTransport")) {
+                        // 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 (!isSupports8bitmime(transport)) {
+                            try {
+                                convertTo7Bit(message);
+                            } catch (IOException e) {
+                                // An error has occured during the 7bit conversion.
+                                // The error is logged and the message is sent anyway.
+
+                                logger.error("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 {
-                            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.
-                            logger.debug("Can't parse unsent address: " + pe.getMessage());
+                            convertTo7Bit(message);
+                        } catch (IOException e) {
+                            logger.error("Error during the conversion to 7 bit.", e);
                         }
                     }
-                    // Set the recipients for the mail
-                    mail.setRecipients(recipients);
-                    if (configuration.isDebug())
-                        logger.debug("Unsent recipients: " + recipients);
-                    if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
+                    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) {
+                            logger.error("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;
+                    }
+                }
+                logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
+                    " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
+                outgoingMailsMetric.increment();
+                return true;
+            } catch (SendFailedException sfe) {
+                logSendFailedException(sfe);
+
+                if (sfe.getValidSentAddresses() != null) {
+                    Address[] validSent = sfe.getValidSentAddresses();
+                    if (validSent.length > 0) {
+                        logger.debug( "Mail (" + mail.getName() + ") sent successfully for " + Arrays.asList(validSent));
+                    }
+                }
+
+                /*
+                 * 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");
-                        deleteMessage = failMessage(mail, sfe, returnCode >= 500 && returnCode <= 599);
-                    } else {
-                        deleteMessage = failMessage(mail, sfe, false);
+                        // 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())
+                        logger.debug("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.
+                logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
+                if ((me.getNextException() != null) && (me.getNextException() instanceof 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;
+                } else {
+                    // 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;
+        }
+        return null;
+    }
 
-            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) {
-            logger.error("Generic exception = permanent failure: "+ex.getMessage(), ex);
-            // Generic exception = permanent failure
-            return failMessage(mail, ex, true);
+    private boolean isSupports8bitmime(SMTPTransport transport) {
+        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.
         }
+        return supports8bitmime;
+    }
 
-        /*
-         * 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);
+    private boolean handleTemporaryResolutionException(Mail mail, String host) {
+        logger.info("Temporary problem looking up mail server for host: " + host);
+        // temporary problems
+        return failMessage(mail,
+            new MessagingException("Temporary problem looking up mail server for host: " + host + ".  I cannot determine where to send this message."),
+            false);
+    }
+
+    private boolean handleNoTargetServer(Mail mail, String host) {
+        logger.info("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);
+        }
     }
 
-    /**
-     * Returns the javamail Session object.
-     *
-     * @param props
-     * @return the java mail session
-     */
     protected Session obtainSession(Properties props) {
         return Session.getInstance(props);
     }
 
-
-    /**
-     * 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;
@@ -617,15 +567,6 @@ public class DeliveryRunnable implements Runnable {
     }
 
 
-    /**
-     * 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);
@@ -640,10 +581,6 @@ public class DeliveryRunnable implements Runnable {
         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;
@@ -732,28 +669,7 @@ public class DeliveryRunnable implements Runnable {
      * @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);
-        logger.debug(sout.toString());
+        logger.debug(messageComposer.composeFailLogMessage(mail, ex, permanent));
         if (!permanent) {
             if (!mail.getState().equals(Mail.ERROR)) {
                 mail.setState(Mail.ERROR);
@@ -769,15 +685,13 @@ public class DeliveryRunnable implements Runnable {
             }
 
             if (retries < configuration.getMaxRetries()) {
-                logBuffer = new StringBuilder(128).append("Storing message ").append(mail.getName()).append(" into outgoing after ").append(retries).append(" retries");
-                logger.debug(logBuffer.toString());
+                logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
                 ++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");
-                logger.debug(logBuffer.toString());
+                logger.debug("Bouncing message " + mail.getName() + " after " + retries + " retries");
             }
         }
 
@@ -789,22 +703,13 @@ public class DeliveryRunnable implements Runnable {
         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.setAttribute("delivery-error", messageComposer.getErrorMsg(ex));
             mail.setState(configuration.getBounceProcessor());
-            // re-insert the mail into the spool for getting it passed to the
-            // dsn-processor
-            MailetContext mc = mailetContext;
+            // re-insert the mail into the spool for getting it passed to the dsn-processor
             try {
-                mc.sendMail(mail);
+                mailetContext.sendMail(mail);
             } catch (MessagingException e) {
-                // we shouldn't get an exception, because the mail was already
-                // processed
+                // we shouldn't get an exception, because the mail was already processed
                 logger.debug("Exception re-inserting failed mail: ", e);
             }
         } else {
@@ -815,126 +720,10 @@ public class DeliveryRunnable implements Runnable {
     }
 
 
-    /**
-     * 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 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();
-
         logger.debug("Sending failure message " + mail.getName());
         try {
-            mailetContext.bounce(mail, sout.toString());
+            mailetContext.bounce(mail, messageComposer.composeForBounce(mail, ex));
         } catch (MessagingException me) {
             logger.debug("Encountered unexpected messaging exception while bouncing message: " + me.getMessage());
         } catch (Exception e) {
@@ -956,8 +745,6 @@ public class DeliveryRunnable implements Runnable {
      * @since v2.2.0a16-unstable
      */
     private Iterator<HostAddress> getGatewaySMTPHostAddresses(Collection<String> gatewayServers) {
-        Iterator<String> gateways = gatewayServers.iterator();
-
-        return new MXHostAddressIterator(gateways, dnsServer, false, logger);
+        return new MXHostAddressIterator(gatewayServers.iterator(), dnsServer, false, logger);
     }
 }


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


[06/50] [abbrv] james-project git commit: JAMES-1877 Refactor Delays related class

Posted by ro...@apache.org.
JAMES-1877 Refactor Delays related class

 - also add support for no unit in TimeConverter


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

Branch: refs/heads/master
Commit: 61c6309f65d43889f98c1984d3f040603f43d9d0
Parents: 8a8af97
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Nov 30 00:44:17 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 14:40:34 2017 +0700

----------------------------------------------------------------------
 .../org/apache/james/util/TimeConverter.java    |   3 +-
 .../apache/james/util/TimeConverterTest.java    |  10 ++
 .../james/transport/mailets/RemoteDelivery.java |   7 +-
 .../transport/mailets/remoteDelivery/Delay.java | 137 ++++++-------------
 .../remoteDelivery/DelaysAndMaxRetry.java       | 107 +++++++--------
 .../mailets/remoteDelivery/Repeat.java          |  37 +++++
 .../mailets/remoteDelivery/DelayTest.java       |  37 ++---
 .../remoteDelivery/DelaysAndMaxRetryTest.java   |   4 +-
 .../mailets/remoteDelivery/RepeatTest.java      |  58 ++++++++
 9 files changed, 226 insertions(+), 174 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java b/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
index 2a877b7..162fef5 100644
--- a/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
+++ b/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
@@ -27,12 +27,13 @@ public class TimeConverter {
 
     private static final HashMap<String, Integer> multipliers = new HashMap<String, Integer>(10);
 
-    private static final String PATTERN_STRING = "\\s*([0-9]+)\\s*([a-z,A-Z]+)\\s*";
+    private static final String PATTERN_STRING = "\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";
 
     private static Pattern PATTERN = null;
 
     static {
         // add allowed units and their respective multiplier
+        multipliers.put("", 1);
         multipliers.put("ms", 1);
         multipliers.put("msec", 1);
         multipliers.put("msecs", 1);

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java b/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
index b05a935..fc13f58 100644
--- a/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
+++ b/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
@@ -25,6 +25,16 @@ import java.util.concurrent.TimeUnit;
 import org.junit.Test;
 
 public class TimeConverterTest {
+    
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenNoUnitAmountAsString() {
+        //Given
+        long expected = 2;
+        //When
+        long actual = TimeConverter.getMilliSeconds("2");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
 
     @Test 
     public void getMilliSecondsShouldConvertValueWhenMsecUnit() {

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/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 9ffd164..88f8ea7 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
@@ -33,6 +33,7 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
@@ -166,7 +167,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     /**
      * List of Delay Times. Controls frequency of retry attempts.
      */
-    private long[] delayTimes;
+    private List<Long> delayTimes;
 
     /**
      * Maximum no. of retries (Defaults to 5).
@@ -392,10 +393,10 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      * @return the next delay time to use, given the retry count
      */
     private long getNextDelay(int retry_count) {
-        if (retry_count > delayTimes.length) {
+        if (retry_count > delayTimes.size()) {
             return Delay.DEFAULT_DELAY_TIME;
         }
-        return delayTimes[retry_count - 1];
+        return delayTimes.get(retry_count - 1);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
index c066a63..3f50ea7 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
@@ -19,133 +19,82 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import javax.mail.MessagingException;
 
-import org.apache.james.transport.util.Patterns;
 import org.apache.james.util.TimeConverter;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
-/**
- * This class is used to hold a delay time and its corresponding number of
- * retries.
- */
 public class Delay {
-
-    /**
-     * Default Delay Time (Default is 6*60*60*1000 Milliseconds (6 hours)).
-     */
-    public static final long DEFAULT_DELAY_TIME = 21600000;
-    public static final int DEFAULT_ATTEMPTS = 1;
-    /**
-     * Pattern to match [attempts*]delay[units].
-     */
-    private static final String PATTERN_STRING = "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";
-    private static final Pattern PATTERN = Patterns.compilePatternUncheckedException(PATTERN_STRING);
-
-    private int attempts = DEFAULT_ATTEMPTS;
-
-    private long delayTime = DEFAULT_DELAY_TIME;
-
     /**
-     * <p>
-     * This constructor expects Strings of the form
-     * "[attempt\*]delaytime[unit]".
-     * </p>
-     * <p>
-     * The optional attempt is the number of tries this delay should be used
-     * (default = 1). The unit, if present, must be one of
-     * (msec,sec,minute,hour,day). The default value of unit is 'msec'.
-     * </p>
-     * <p>
-     * The constructor multiplies the delaytime by the relevant multiplier
-     * for the unit, so the delayTime instance variable is always in msec.
-     * </p>
+     * <p> The optional attempt is the number of tries this delay should be used (default = 1).
+     * The delayTime is parsed by {@link TimeConverter}</p>
      *
-     * @param initString the string to initialize this Delay object from
+     * @param initString the string to initialize this Delay object from. It has the form "[attempt\*]delaytime[unit]"
      */
-    public Delay(String initString) throws MessagingException {
-        // Default unit value to 'msec'.
-        String unit = "msec";
-
-        Matcher res = PATTERN.matcher(initString);
-        if (res.matches()) {
-            // The capturing groups will now hold:
-            // at 1: attempts * (if present)
-            // at 2: delaytime
-            // at 3: unit (if present)
-            if (res.group(1) != null && !res.group(1).equals("")) {
-                // We have an attempt *
-                String attemptMatch = res.group(1);
-
-                // Strip the * and whitespace.
-                attemptMatch = attemptMatch.substring(0, attemptMatch.length() - 1).trim();
-                attempts = Integer.parseInt(attemptMatch);
-            }
-
-            delayTime = Long.parseLong(res.group(2));
-
-            if (!res.group(3).equals("")) {
-                // We have a value for 'unit'.
-                unit = res.group(3).toLowerCase(Locale.US);
-            }
-        } else {
-            throw new MessagingException(initString + " does not match " + PATTERN_STRING);
+    public static Delay from(String initString) throws MessagingException {
+        if (Strings.isNullOrEmpty(initString)) {
+            throw new NumberFormatException("Null or Empty strings are not permitted");
         }
+        List<String> parts = Splitter.on('*').splitToList(initString);
 
-        // calculate delayTime.
-        try {
-            delayTime = TimeConverter.getMilliSeconds(delayTime, unit);
-        } catch (NumberFormatException e) {
-            throw new MessagingException(e.getMessage());
+        if (parts.size() == 1) {
+            return new Delay(DEFAULT_ATTEMPTS, TimeConverter.getMilliSeconds(parts.get(0)));
+        }
+        if (parts.size() == 2) {
+            int attempts = Integer.parseInt(parts.get(0));
+            if (attempts < 0) {
+                throw new MessagingException("Number of attempts negative in " + initString);
+            }
+            return new Delay(attempts, TimeConverter.getMilliSeconds(parts.get(1)));
         }
+        throw new MessagingException(initString + " contains too much parts");
     }
 
-    /**
-     * This constructor makes a default Delay object with attempts = 1 and
-     * delayTime = DEFAULT_DELAY_TIME.
-     */
+    public static final long DEFAULT_DELAY_TIME = TimeUnit.HOURS.toMillis(6);
+    public static final int DEFAULT_ATTEMPTS = 1;
+
+    private final int attempts;
+    private final long delayTimeInMs;
+
     public Delay() {
+        this(DEFAULT_ATTEMPTS, DEFAULT_DELAY_TIME);
     }
 
     @VisibleForTesting
     Delay(int attempts, long delayTime) {
         this.attempts = attempts;
-        this.delayTime = delayTime;
+        this.delayTimeInMs = delayTime;
     }
 
-    /**
-     * @return the delayTime for this Delay
-     */
-    public long getDelayTime() {
-        return delayTime;
+    public long getDelayTimeInMs() {
+        return delayTimeInMs;
     }
 
-    /**
-     * @return the number attempts this Delay should be used.
-     */
     public int getAttempts() {
         return attempts;
     }
 
-    /**
-     * Set the number attempts this Delay should be used.
-     */
-    public void setAttempts(int value) {
-        attempts = value;
+    public List<Long> getExpendendDelays() {
+        return Repeat.repeat(delayTimeInMs, attempts);
     }
 
-    /**
-     * Pretty prints this Delay
-     */
     @Override
     public String toString() {
-        return getAttempts() + "*" + getDelayTime() + "msecs";
+        return MoreObjects.toStringHelper(this)
+            .add("attempts", attempts)
+            .add("delayTime", delayTimeInMs)
+            .toString();
     }
 
     @Override
@@ -154,13 +103,13 @@ public class Delay {
             Delay that = (Delay) o;
 
             return Objects.equal(this.attempts, that.attempts)
-                && Objects.equal(this.delayTime, that.delayTime);
+                && Objects.equal(this.delayTimeInMs, that.delayTimeInMs);
         }
         return false;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(attempts, delayTime);
+        return Objects.hashCode(attempts, delayTimeInMs);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
index 10b6cca..16ae192 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
@@ -19,10 +19,7 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
-import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
-import java.util.StringTokenizer;
 
 import javax.mail.MessagingException;
 
@@ -31,72 +28,75 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class DelaysAndMaxRetry {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DelaysAndMaxRetry.class);
 
-    public static DelaysAndMaxRetry from(int intendedMaxRetries, String delaysAsString) throws MessagingException {
-        // Create list of Delay Times.
-        ArrayList<Delay> delayTimesList = createDelayList(delaysAsString);
+    public static DelaysAndMaxRetry defaults() {
+        return new DelaysAndMaxRetry(RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY, Repeat.repeat(new Delay(), RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY));
+    }
 
-        // Check consistency of 'maxRetries' with delayTimesList attempts.
-        int totalAttempts = calcTotalAttempts(delayTimesList);
+    public static DelaysAndMaxRetry from(int intendedMaxRetries, String delaysAsString) throws MessagingException {
+        List<Delay> delayTimesList = createDelayList(delaysAsString);
+        int totalAttempts = computeTotalAttempts(delayTimesList);
+        return getDelaysAndMaxRetry(intendedMaxRetries, totalAttempts, delayTimesList);
+    }
 
-        // If inconsistency found, fix it.
+    private static DelaysAndMaxRetry getDelaysAndMaxRetry(int intendedMaxRetries, int totalAttempts, List<Delay> delayTimesList) throws MessagingException {
         if (totalAttempts > intendedMaxRetries) {
-            LOGGER.warn("Total number of delayTime attempts exceeds maxRetries specified. " + " Increasing maxRetries from " + intendedMaxRetries + " to " + totalAttempts);
+            LOGGER.warn("Total number of delayTime attempts exceeds maxRetries specified. Increasing maxRetries from {} to {}", intendedMaxRetries, totalAttempts);
             return new DelaysAndMaxRetry(totalAttempts, delayTimesList);
         } else {
             int extra = intendedMaxRetries - totalAttempts;
-            if (extra != 0) {
-                LOGGER.warn("maxRetries is larger than total number of attempts specified.  " + "Increasing last delayTime with " + extra + " attempts ");
-
-                // Add extra attempts to the last delayTime.
-                if (delayTimesList.size() != 0) {
-                    // Get the last delayTime.
-                    Delay delay = delayTimesList.get(delayTimesList.size() - 1);
-
-                    // Increase no. of attempts.
-                    delay.setAttempts(delay.getAttempts() + extra);
-                    LOGGER.warn("Delay of " + delay.getDelayTime() + " msecs is now attempted: " + delay.getAttempts() + " times");
-                } else {
-                    throw new MessagingException("No delaytimes, cannot continue");
-                }
+            if (extra > 0) {
+                LOGGER.warn("maxRetries is larger than total number of attempts specified. Increasing last delayTime with {} attempts ", extra);
+                return addExtraAttemptToLastDelay(intendedMaxRetries, extra, delayTimesList);
             }
             return new DelaysAndMaxRetry(intendedMaxRetries, delayTimesList);
         }
     }
 
-    private static ArrayList<Delay> createDelayList(String delaysAsString) {
-        ArrayList<Delay> delayTimesList = new ArrayList<Delay>();
+    private static DelaysAndMaxRetry addExtraAttemptToLastDelay(int intendedMaxRetries, int extra, List<Delay> delayTimesList) throws MessagingException {
+        if (delayTimesList.size() != 0) {
+            Delay lastDelay = delayTimesList.get(delayTimesList.size() - 1);
+            LOGGER.warn("Delay of {} msecs is now attempted: {} times", lastDelay.getDelayTimeInMs(), lastDelay.getAttempts());
+            return new DelaysAndMaxRetry(intendedMaxRetries,
+                ImmutableList.copyOf(
+                    Iterables.concat(
+                        Iterables.limit(delayTimesList, delayTimesList.size() - 1),
+                        ImmutableList.of(new Delay(lastDelay.getAttempts() + extra, lastDelay.getDelayTimeInMs())))));
+        } else {
+            throw new MessagingException("No delaytimes, cannot continue");
+        }
+    }
+
+    private static List<Delay> createDelayList(String delaysAsString) {
+        if (delaysAsString == null) {
+            // Use default delayTime.
+            return ImmutableList.of(new Delay());
+        }
+        ImmutableList<String> delayStrings = FluentIterable.from(Splitter.on(',')
+            .omitEmptyStrings()
+            .split(delaysAsString))
+            .toList();
+        ImmutableList.Builder<Delay> builder = ImmutableList.builder();
         try {
-            if (delaysAsString != null) {
-
-                // Split on commas
-                StringTokenizer st = new StringTokenizer(delaysAsString, ",");
-                while (st.hasMoreTokens()) {
-                    String delayTime = st.nextToken();
-                    delayTimesList.add(new Delay(delayTime));
-                }
-            } else {
-                // Use default delayTime.
-                delayTimesList.add(new Delay());
+            for (String s : delayStrings) {
+                builder.add(Delay.from(s));
             }
+            return builder.build();
         } catch (Exception e) {
-            LOGGER.warn("Invalid delayTime setting: " + delaysAsString);
+            LOGGER.warn("Invalid delayTime setting: {}", delaysAsString);
+            return builder.build();
         }
-        return delayTimesList;
     }
 
-    /**
-     * Calculates Total no. of attempts for the specified delayList.
-     *
-     * @param delayList list of 'Delay' objects
-     * @return total no. of retry attempts
-     */
-    private static int calcTotalAttempts(List<Delay> delayList) {
+    private static int computeTotalAttempts(List<Delay> delayList) {
         int sum = 0;
         for (Delay delay : delayList) {
             sum += delay.getAttempts();
@@ -136,17 +136,12 @@ public class DelaysAndMaxRetry {
      * @param list the list to expand
      * @return the expanded list
      */
-    public long[] getExpendedDelays() {
-        long[] delaysAsLong = new long[calcTotalAttempts(delays)];
-        Iterator<Delay> i = delays.iterator();
-        int idx = 0;
-        while (i.hasNext()) {
-            Delay delay = i.next();
-            for (int j = 0; j < delay.getAttempts(); j++) {
-                delaysAsLong[idx++] = delay.getDelayTime();
-            }
+    public List<Long> getExpendedDelays() {
+        ImmutableList.Builder<Long> builder = ImmutableList.builder();
+        for (Delay delay: delays) {
+            builder.addAll(delay.getExpendendDelays());
         }
-        return delaysAsLong;
+        return builder.build();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
new file mode 100644
index 0000000..0252214
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
@@ -0,0 +1,37 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.List;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class Repeat {
+
+    public static <T> List<T> repeat(T element, int times) {
+        Preconditions.checkArgument(times > 0, "Times argument should be strictly positive");
+        return ImmutableList.copyOf(
+            Iterables.limit(
+                Iterables.cycle(element), times));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
index f23c918..b940c3e 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
@@ -40,78 +40,79 @@ public class DelayTest {
 
     @Test
     public void stringConstructorShouldWorkForNumbers() throws Exception {
-        assertThat(new Delay("36")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 36));
+        assertThat(Delay.from("36")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 36));
     }
 
     @Test
     public void stringConstructorShouldWorkForZero() throws Exception {
-        assertThat(new Delay("0")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 0));
+        assertThat(Delay.from("0")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 0));
     }
 
     @Test
-    public void stringConstructorShouldDefaultToZeroForNegatives() throws Exception {
-        assertThat(new Delay("0")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 0));
+    public void stringConstructorShouldThrowOnNegativeNumbers() throws Exception {
+        expectedException.expect(NumberFormatException.class);
+        assertThat(Delay.from("-1s")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 0));
     }
 
     @Test
     public void stringConstructorShouldWorkForNumberAndSecond() throws Exception {
-        assertThat(new Delay("1s")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 1000));
+        assertThat(Delay.from("1s")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 1000));
     }
 
     @Test
     public void stringConstructorShouldWorkForNumberAndAttempts() throws Exception {
-        assertThat(new Delay("2*36")).isEqualTo(new Delay(2, 36));
+        assertThat(Delay.from("2*36")).isEqualTo(new Delay(2, 36));
     }
 
     @Test
     public void stringConstructorShouldWorkForNumberAndZeroAttempts() throws Exception {
-        assertThat(new Delay("0*36")).isEqualTo(new Delay(0, 36));
+        assertThat(Delay.from("0*36")).isEqualTo(new Delay(0, 36));
     }
 
     @Test
     public void stringConstructorShouldThrowOnNegativeAttempts() throws Exception {
         expectedException.expect(MessagingException.class);
 
-        new Delay("-1*36");
+        Delay.from("-1*36");
     }
 
     @Test
     public void stringConstructorShouldThrowWhenAttemptsOmitted() throws Exception {
         expectedException.expect(NumberFormatException.class);
 
-        new Delay("*36");
+        Delay.from("*36");
     }
 
     @Test
     public void stringConstructorShouldThrowWhenDelayOmitted() throws Exception {
-        expectedException.expect(MessagingException.class);
+        expectedException.expect(NumberFormatException.class);
 
-        new Delay("2*");
+        Delay.from("2*");
     }
 
     @Test
     public void stringConstructorShouldWorkForNumberAttemptsAndUnit() throws Exception {
-        assertThat(new Delay("2*36s")).isEqualTo(new Delay(2, 36000));
+        assertThat(Delay.from("2*36s")).isEqualTo(new Delay(2, 36000));
     }
 
     @Test
     public void stringConstructorShouldThrowOnInvalidInput() throws Exception {
-        expectedException.expect(MessagingException.class);
+        expectedException.expect(NumberFormatException.class);
 
-        new Delay("invalid");
+        Delay.from("invalid");
     }
 
     @Test
     public void stringConstructorShouldThrowOnInvalidUnit() throws Exception {
-        expectedException.expect(MessagingException.class);
+        expectedException.expect(NumberFormatException.class);
 
-        new Delay("36invalid");
+        Delay.from("36invalid");
     }
 
     @Test
     public void stringConstructorShouldThrowOnEmptyString() throws Exception {
-        expectedException.expect(MessagingException.class);
+        expectedException.expect(NumberFormatException.class);
 
-        new Delay("");
+        Delay.from("");
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
index da4ecda..dbab3c4 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
@@ -124,13 +124,13 @@ public class DelaysAndMaxRetryTest {
     public void getExpendedDelaysShouldExpandSingleDelays() throws Exception {
         DelaysAndMaxRetry testee = DelaysAndMaxRetry.from(3, "1*1S,1*2S,1*5S");
 
-        assertThat(testee.getExpendedDelays()).containsExactly(1000, 2000, 5000);
+        assertThat(testee.getExpendedDelays()).containsExactly(1000L, 2000L, 5000L);
     }
 
     @Test
     public void getExpendedDelaysShouldExpandMultipleDelays() throws Exception {
         DelaysAndMaxRetry testee = DelaysAndMaxRetry.from(3, "1*1S,2*2S,2*5S");
 
-        assertThat(testee.getExpendedDelays()).containsExactly(1000, 2000, 2000, 5000, 5000);
+        assertThat(testee.getExpendedDelays()).containsExactly(1000L, 2000L, 2000L, 5000L, 5000L);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/61c6309f/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
new file mode 100644
index 0000000..13b9968
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
@@ -0,0 +1,58 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class RepeatTest {
+
+    public static final String ELEMENT = "a";
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void repeatShouldThrowOnNegativeTimes() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Repeat.repeat(new Object(), -1);
+    }
+
+    @Test
+    public void repeatShouldThrowOnZeroTimes() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Repeat.repeat(new Object(), 0);
+    }
+
+    @Test
+    public void repeatShouldWorkWithOneElement() {
+        assertThat(Repeat.repeat(ELEMENT, 1)).containsExactly(ELEMENT);
+    }
+
+    @Test
+    public void repeatShouldWorkWithTwoElements() {
+        assertThat(Repeat.repeat(ELEMENT, 2)).containsExactly(ELEMENT, ELEMENT);
+    }
+
+}


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


[11/50] [abbrv] james-project git commit: JAMES-1877 Tests then refactoring for RemoteDelivery

Posted by ro...@apache.org.
JAMES-1877 Tests then refactoring for 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/6774b877
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/6774b877
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/6774b877

Branch: refs/heads/master
Commit: 6774b8775e8ce5d81dc72b4c15dc9109f3300e67
Parents: 4a5a4ba
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 11:05:13 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:39 2017 +0700

----------------------------------------------------------------------
 mailet/base/pom.xml                             |  11 +
 .../org/apache/mailet/base/test/FakeMail.java   |  56 +++++
 .../apache/mailet/base/test/FakeMailTest.java   |  41 ++++
 .../transport/mailets/ToProcessorTest.java      |  12 +-
 .../james/transport/mailets/RemoteDelivery.java | 143 ++++++-------
 .../remoteDelivery/RemoteDeliveryTest.java      | 208 +++++++++++++++++++
 6 files changed, 390 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/6774b877/mailet/base/pom.xml
----------------------------------------------------------------------
diff --git a/mailet/base/pom.xml b/mailet/base/pom.xml
index 0c47808..fdec517 100644
--- a/mailet/base/pom.xml
+++ b/mailet/base/pom.xml
@@ -71,6 +71,17 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <version>1.7.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/6774b877/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
index acccec3..661882a 100644
--- a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
+++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
@@ -38,9 +38,12 @@ import javax.mail.internet.MimeMessage;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
 
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -176,6 +179,14 @@ public class FakeMail implements Mail {
         return FakeMail.builder().build();
     }
 
+    private static Map<String, Serializable> attributes(Mail mail) {
+        ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.builder();
+        for (String attributeName: ImmutableList.copyOf(mail.getAttributeNames())) {
+            builder.put(attributeName, mail.getAttribute(attributeName));
+        }
+        return builder.build();
+    }
+
     private MimeMessage msg;
     private Collection<MailAddress> recipients;
     private String name;
@@ -201,6 +212,11 @@ public class FakeMail implements Mail {
         this.remoteAddr = remoteAddr;
     }
 
+    public FakeMail(Mail mail) throws MessagingException {
+        this(mail.getMessage(), Lists.newArrayList(mail.getRecipients()), mail.getName(), mail.getSender(), mail.getState(), mail.getErrorMessage(),
+            mail.getLastUpdated(), attributes(mail), mail.getMessageSize(), mail.getRemoteAddr());
+    }
+
     @Override
     public String getName() {
         return name;
@@ -322,4 +338,44 @@ public class FakeMail implements Mail {
     public void setMessageSize(long size) {
         this.size = size;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof FakeMail) {
+            FakeMail that = (FakeMail) o;
+
+            return Objects.equal(this.size, that.size)
+                && Objects.equal(this.msg, that.msg)
+                && Objects.equal(this.recipients, that.recipients)
+                && Objects.equal(this.name, that.name)
+                && Objects.equal(this.sender, that.sender)
+                && Objects.equal(this.state, that.state)
+                && Objects.equal(this.errorMessage, that.errorMessage)
+                && Objects.equal(this.lastUpdated, that.lastUpdated)
+                && Objects.equal(this.attributes, that.attributes)
+                && Objects.equal(this.remoteAddr, that.remoteAddr);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(msg, name, sender, recipients, state, errorMessage, lastUpdated, attributes, size, recipients, remoteAddr);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("msg", msg)
+            .add("recipients", recipients)
+            .add("name", name)
+            .add("sender", sender)
+            .add("state", state)
+            .add("errorMessage", errorMessage)
+            .add("lastUpdated", lastUpdated)
+            .add("attributes", attributes)
+            .add("size", size)
+            .add("remoteAddr", remoteAddr)
+            .toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/6774b877/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailTest.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailTest.java b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailTest.java
new file mode 100644
index 0000000..fc0c03c
--- /dev/null
+++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailTest.java
@@ -0,0 +1,41 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.mailet.base.test;
+
+import static org.mockito.Mockito.mock;
+
+import javax.mail.internet.MimeMessage;
+
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+public class FakeMailTest {
+
+    @Test
+    public void beanShouldRespectBeanContract() {
+        EqualsVerifier.forClass(FakeMail.class)
+            .suppress(Warning.NONFINAL_FIELDS)
+            .withPrefabValues(MimeMessage.class, mock(MimeMessage.class), mock(MimeMessage.class))
+            .verify();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/6774b877/mailet/standard/src/test/java/org/apache/james/transport/mailets/ToProcessorTest.java
----------------------------------------------------------------------
diff --git a/mailet/standard/src/test/java/org/apache/james/transport/mailets/ToProcessorTest.java b/mailet/standard/src/test/java/org/apache/james/transport/mailets/ToProcessorTest.java
index 3db12fd..35175a5 100644
--- a/mailet/standard/src/test/java/org/apache/james/transport/mailets/ToProcessorTest.java
+++ b/mailet/standard/src/test/java/org/apache/james/transport/mailets/ToProcessorTest.java
@@ -21,6 +21,7 @@
 package org.apache.james.transport.mailets;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
@@ -30,6 +31,7 @@ import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
 import org.apache.mailet.Mailet;
 import org.apache.mailet.MailetException;
+import org.apache.mailet.base.MailAddressFixture;
 import org.apache.mailet.base.test.FakeMail;
 import org.apache.mailet.base.test.FakeMailContext;
 import org.apache.mailet.base.test.FakeMailetConfig;
@@ -143,13 +145,13 @@ public class ToProcessorTest {
                 .build();
         mailet.init(mailetConfig);
 
-        Mail mail = FakeMail.builder()
-                .recipients(new MailAddress("test@james.apache.org"), new MailAddress("test2@james.apache.org"))
-                .build();
         String initialErrorMessage = "first";
-        mail.setErrorMessage(initialErrorMessage);
+        Mail mail = FakeMail.builder()
+            .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES)
+            .errorMessage(initialErrorMessage)
+            .build();
         mailet.service(mail);
 
-        verify(logger).info("Sending mail " + mail +" to error");
+        verify(logger).info(anyString());
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/6774b877/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 e7db2c8..8a40298 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
@@ -20,9 +20,7 @@
 package org.apache.james.transport.mailets;
 
 import java.net.UnknownHostException;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Hashtable;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Vector;
@@ -47,6 +45,8 @@ import org.apache.mailet.MailAddress;
 import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
 
+import com.google.common.collect.HashMultimap;
+
 /**
  * <p>The RemoteDelivery mailet delivers messages to a remote SMTP server able to deliver or forward messages to their final
  * destination.
@@ -120,6 +120,8 @@ import org.slf4j.Logger;
 public class RemoteDelivery extends GenericMailet {
 
     private static final String OUTGOING_MAILS = "outgoingMails";
+    private static final boolean DEFAULT_START_THREADS = true;
+    public static final String NAME_JUNCTION = "-to-";
 
     private final DNSService dnsServer;
     private final DomainList domainList;
@@ -127,6 +129,7 @@ public class RemoteDelivery extends GenericMailet {
     private final Metric outgoingMailsMetric;
     private final Collection<Thread> workersThreads;
     private final VolatileIsDestroyed volatileIsDestroyed;
+    private final boolean startThreads;
 
     private MailQueue queue;
     private Logger logger;
@@ -134,31 +137,32 @@ public class RemoteDelivery extends GenericMailet {
 
     @Inject
     public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory) {
+        this(dnsServer, domainList, queueFactory, metricFactory, DEFAULT_START_THREADS);
+    }
+
+    public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory, boolean startThreads) {
         this.dnsServer = dnsServer;
         this.domainList = domainList;
         this.queueFactory = queueFactory;
         this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
-        this.workersThreads = new Vector<Thread>();
         this.volatileIsDestroyed = new VolatileIsDestroyed();
+        this.workersThreads = new Vector<Thread>();
+        this.startThreads = startThreads;
     }
 
-    /**
-     * Initializes all arguments based on configuration values specified in the
-     * James configuration file.
-     *
-     * @throws MessagingException on failure to initialize attributes.
-     */
     public void init() throws MessagingException {
         logger = getMailetContext().getLogger();
         configuration = new RemoteDeliveryConfiguration(getMailetConfig(), domainList);
         queue = queueFactory.getQueue(configuration.getOutGoingQueueName());
-        initDeliveryThreads();
         try {
             if (configuration.isBindUsed())
                 RemoteDeliverySocketFactory.setBindAdress(configuration.getBindAddress());
         } catch (UnknownHostException e) {
             log("Invalid bind setting (" + configuration.getBindAddress() + "): " + e.toString());
         }
+        if (startThreads) {
+            initDeliveryThreads();
+        }
     }
 
     private void initDeliveryThreads() {
@@ -183,80 +187,65 @@ public class RemoteDelivery extends GenericMailet {
         return "RemoteDelivery Mailet";
     }
 
-    /**
-     * For this message, we take the list of recipients, organize these into
-     * distinct servers, and duplicate the message for each of these servers,
-     * and then call the deliver (messagecontainer) method for each
-     * server-specific messagecontainer ... that will handle storing it in the
-     * outgoing queue if needed.
-     *
-     * @param mail org.apache.mailet.Mail
-     */
     @Override
     public void service(Mail mail) throws MessagingException {
-        // Do I want to give the internal key, or the message's Message ID
         if (configuration.isDebug()) {
             log("Remotely delivering mail " + mail.getName());
         }
-        Collection<MailAddress> recipients = mail.getRecipients();
-
         if (configuration.isUsePriority()) {
-
-            // Use highest prio for new emails. See JAMES-1311
             mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, MailPrioritySupport.HIGH_PRIORITY);
         }
-        if (configuration.getGatewayServer() == null) {
-            // Must first organize the recipients into distinct servers (name
-            // made case insensitive)
-            Hashtable<String, Collection<MailAddress>> targets = new Hashtable<String, Collection<MailAddress>>();
-            for (MailAddress target : recipients) {
-                String targetServer = target.getDomain().toLowerCase(Locale.US);
-                Collection<MailAddress> temp = targets.get(targetServer);
-                if (temp == null) {
-                    temp = new ArrayList<MailAddress>();
-                    targets.put(targetServer, temp);
-                }
-                temp.add(target);
+        if (!mail.getRecipients().isEmpty()) {
+            if (configuration.getGatewayServer().isEmpty()) {
+                serviceNoGateway(mail);
+            } else {
+                serviceWithGateway(mail);
             }
+        } else {
+            log("Mail " + mail.getName() + " from " + mail.getSender() + " has no recipients and can not be remotely delivered");
+        }
+        mail.setState(Mail.GHOST);
+    }
 
-            // We have the recipients organized into distinct servers... put
-            // them into the
-            // delivery store organized like this... this is ultra inefficient I
-            // think...
+    private void serviceWithGateway(Mail mail) {
+        if (configuration.isDebug()) {
+            log("Sending mail to " + mail.getRecipients() + " via " + configuration.getGatewayServer());
+        }
+        try {
+            queue.enQueue(mail);
+        } catch (MailQueueException e) {
+            log("Unable to queue mail " + mail.getName() + " for recipients + " + mail.getRecipients().toString(), e);
+        }
+    }
 
-            // Store the new message containers, organized by server, in the
-            // outgoing mail repository
-            String name = mail.getName();
-            for (Map.Entry<String, Collection<MailAddress>> entry : targets.entrySet()) {
-                if (configuration.isDebug()) {
-                    String logMessageBuffer = "Sending mail to " + entry.getValue() + " on host " + entry.getKey();
-                    log(logMessageBuffer);
-                }
-                mail.setRecipients(entry.getValue());
-                String nameBuffer = name + "-to-" + entry.getKey();
-                mail.setName(nameBuffer);
-                try {
-                    queue.enQueue(mail);
-                } catch (MailQueueException e) {
-                    log("Unable to queue mail " + mail.getName() + " for recipients + " + mail.getRecipients().toString(), e);
-                }
-            }
-        } else {
-            // Store the mail unaltered for processing by the gateway server(s)
-            if (configuration.isDebug()) {
-                String logMessageBuffer = "Sending mail to " + mail.getRecipients() + " via " + configuration.getGatewayServer();
-                log(logMessageBuffer);
-            }
+    private void serviceNoGateway(Mail mail) {
+        String mailName = mail.getName();
+        Map<String, Collection<MailAddress>> targets = groupByServer(mail.getRecipients());
+        for (Map.Entry<String, Collection<MailAddress>> entry : targets.entrySet()) {
+            serviceSingleServer(mail, mailName, entry);
+        }
+    }
 
-            // Set it to try to deliver (in a separate thread) immediately
-            // (triggered by storage)
-            try {
-                queue.enQueue(mail);
-            } catch (MailQueueException e) {
-                log("Unable to queue mail " + mail.getName() + " for recipients + " + mail.getRecipients().toString(), e);
-            }
+    private void serviceSingleServer(Mail mail, String originalName, Map.Entry<String, Collection<MailAddress>> entry) {
+        if (configuration.isDebug()) {
+            log("Sending mail to " + entry.getValue() + " on host " + entry.getKey());
+        }
+        mail.setRecipients(entry.getValue());
+        mail.setName(originalName + NAME_JUNCTION + entry.getKey());
+        try {
+            queue.enQueue(mail);
+        } catch (MailQueueException e) {
+            log("Unable to queue mail " + mail.getName() + " for recipients + " + mail.getRecipients().toString(), e);
         }
-        mail.setState(Mail.GHOST);
+    }
+
+    private Map<String, Collection<MailAddress>> groupByServer(Collection<MailAddress> recipients) {
+        // Must first organize the recipients into distinct servers (name made case insensitive)
+        HashMultimap<String, MailAddress> groupByServerMultimap = HashMultimap.create();
+        for (MailAddress recipient : recipients) {
+            groupByServerMultimap.put(recipient.getDomain().toLowerCase(Locale.US), recipient);
+        }
+        return groupByServerMultimap.asMap();
     }
 
     /**
@@ -266,12 +255,14 @@ public class RemoteDelivery extends GenericMailet {
      */
     @Override
     public synchronized void destroy() {
-        volatileIsDestroyed.markAsDestroyed();
-        // Wake up all threads from waiting for an accept
-        for (Thread t : workersThreads) {
-            t.interrupt();
+        if (startThreads) {
+            volatileIsDestroyed.markAsDestroyed();
+            // Wake up all threads from waiting for an accept
+            for (Thread t : workersThreads) {
+                t.interrupt();
+            }
+            notifyAll();
         }
-        notifyAll();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/6774b877/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
new file mode 100644
index 0000000..2012a2e
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
@@ -0,0 +1,208 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.mail.MessagingException;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.domainlist.api.DomainList;
+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.MailQueueFactory;
+import org.apache.james.transport.mailets.RemoteDelivery;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMail;
+import org.apache.mailet.base.test.FakeMailetConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class RemoteDeliveryTest {
+
+    public static final String MAIL_NAME = "mail_name";
+
+    private static class FakeMailQueue implements MailQueue {
+        private final List<Mail> enqueuedMail;
+
+        private FakeMailQueue() {
+            this.enqueuedMail = Lists.newArrayList();
+        }
+
+        @Override
+        public void enQueue(Mail mail, long delay, TimeUnit unit) throws MailQueueException {
+            enQueue(mail);
+        }
+
+        @Override
+        public void enQueue(Mail mail) throws MailQueueException {
+            try {
+                enqueuedMail.add(new FakeMail(mail));
+            } catch (MessagingException e) {
+                throw Throwables.propagate(e);
+            }
+        }
+
+        @Override
+        public MailQueueItem deQueue() throws MailQueueException, InterruptedException {
+            throw new NotImplementedException();
+        }
+
+        public List<Mail> getEnqueuedMail() {
+            return ImmutableList.copyOf(enqueuedMail);
+        }
+    }
+
+    public static final boolean DONT_START_THREADS = false;
+    private RemoteDelivery remoteDelivery;
+    private FakeMailQueue mailQueue;
+
+    @Before
+    public void setUp() {
+        MailQueueFactory queueFactory = mock(MailQueueFactory.class);
+        mailQueue = new FakeMailQueue();
+        when(queueFactory.getQueue(RemoteDeliveryConfiguration.OUTGOING)).thenReturn(mailQueue);
+        remoteDelivery = new RemoteDelivery(mock(DNSService.class), mock(DomainList.class), queueFactory, mock(MetricFactory.class), DONT_START_THREADS);
+    }
+
+    @Test
+    public void remoteDeliveryShouldAddEmailToSpool() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build());
+
+        Mail mail = FakeMail.builder().name(MAIL_NAME).recipients(MailAddressFixture.ANY_AT_JAMES).build();
+        remoteDelivery.service(mail);
+
+        assertThat(mailQueue.getEnqueuedMail()).containsOnly(FakeMail.builder()
+            .name(MAIL_NAME + RemoteDelivery.NAME_JUNCTION + MailAddressFixture.JAMES_APACHE_ORG)
+            .recipient(MailAddressFixture.ANY_AT_JAMES)
+            .build());
+    }
+
+    @Test
+    public void remoteDeliveryShouldSplitMailsByServerWhenNoGateway() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build());
+
+        Mail mail = FakeMail.builder()
+            .name(MAIL_NAME)
+            .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2, MailAddressFixture.OTHER_AT_JAMES)
+            .build();
+        remoteDelivery.service(mail);
+
+        assertThat(mailQueue.getEnqueuedMail()).containsOnly(
+            FakeMail.builder()
+                .name(MAIL_NAME + RemoteDelivery.NAME_JUNCTION + MailAddressFixture.JAMES_APACHE_ORG)
+                .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES)
+                .build(),
+            FakeMail.builder()
+                .name(MAIL_NAME + RemoteDelivery.NAME_JUNCTION + MailAddressFixture.JAMES2_APACHE_ORG)
+                .recipients(MailAddressFixture.ANY_AT_JAMES2)
+                .build());
+    }
+
+    @Test
+    public void remoteDeliveryShouldNotSplitMailsByServerWhenGateway() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, MailAddressFixture.JAMES_LOCAL)
+            .build());
+
+        Mail mail = FakeMail.builder()
+            .name(MAIL_NAME)
+            .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2, MailAddressFixture.OTHER_AT_JAMES)
+            .build();
+        remoteDelivery.service(mail);
+
+        assertThat(mailQueue.getEnqueuedMail()).containsOnly(
+            FakeMail.builder()
+                .name(MAIL_NAME)
+                .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2, MailAddressFixture.OTHER_AT_JAMES)
+                .build());
+    }
+
+    @Test
+    public void remoteDeliveryShouldGhostMails() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build());
+
+        Mail mail = FakeMail.builder().name(MAIL_NAME).recipients(MailAddressFixture.ANY_AT_JAMES).build();
+        remoteDelivery.service(mail);
+
+        assertThat(mail.getState()).isEqualTo(Mail.GHOST);
+    }
+
+    @Test
+    public void remoteDeliveryShouldAddPriorityIfSpecified() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.USE_PRIORITY, "true")
+            .build());
+
+        Mail mail = FakeMail.builder().name(MAIL_NAME).recipients(MailAddressFixture.ANY_AT_JAMES).build();
+        remoteDelivery.service(mail);
+
+        assertThat(mailQueue.getEnqueuedMail()).containsOnly(FakeMail.builder()
+            .name(MAIL_NAME + RemoteDelivery.NAME_JUNCTION + MailAddressFixture.JAMES_APACHE_ORG)
+            .attribute(MailPrioritySupport.MAIL_PRIORITY, MailPrioritySupport.HIGH_PRIORITY)
+            .recipient(MailAddressFixture.ANY_AT_JAMES)
+            .build());
+    }
+
+    @Test
+    public void remoteDeliveryShouldNotForwardMailsWithNoRecipients() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build());
+
+        Mail mail = FakeMail.builder().name(MAIL_NAME).build();
+        remoteDelivery.service(mail);
+
+        assertThat(mailQueue.getEnqueuedMail()).isEmpty();
+    }
+
+    @Test
+    public void remoteDeliveryShouldNotForwardMailsWithNoRecipientsWithGateway() throws Exception{
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, MailAddressFixture.JAMES_LOCAL)
+            .build());
+
+        Mail mail = FakeMail.builder().name(MAIL_NAME).build();
+        remoteDelivery.service(mail);
+
+        assertThat(mailQueue.getEnqueuedMail()).isEmpty();
+    }
+}


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


[34/50] [abbrv] james-project git commit: JAMES-1877 Add tests for running RemoteDelivery

Posted by ro...@apache.org.
JAMES-1877 Add tests for running 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/9b59d55e
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/9b59d55e
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/9b59d55e

Branch: refs/heads/master
Commit: 9b59d55e0d21754d14e6e3da331404c7d0eb6197
Parents: dbeea26
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 8 14:45:21 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:52 2017 +0700

----------------------------------------------------------------------
 .../RemoteDeliveryRunningTest.java              | 85 ++++++++++++++++++++
 1 file changed, 85 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9b59d55e/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryRunningTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryRunningTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryRunningTest.java
new file mode 100644
index 0000000..5c6abf7
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryRunningTest.java
@@ -0,0 +1,85 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.metrics.api.MetricFactory;
+import org.apache.james.queue.api.MailQueue;
+import org.apache.james.queue.api.MailQueueFactory;
+import org.apache.james.transport.mailets.RemoteDelivery;
+import org.apache.mailet.base.test.FakeMailetConfig;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class RemoteDeliveryRunningTest {
+
+    public static final String QUEUE_NAME = "queueName";
+    private RemoteDelivery remoteDelivery;
+    private MailQueue mailQueue;
+    private CountDownLatch countDownLatch;
+
+    @Before
+    public void setUp() throws Exception {
+        countDownLatch = new CountDownLatch(1);
+        MailQueueFactory mailQueueFactory = mock(MailQueueFactory.class);
+        remoteDelivery = new RemoteDelivery(mock(DNSService.class), mock(DomainList.class), mailQueueFactory,
+            mock(MetricFactory.class), RemoteDelivery.THREAD_STATE.START_THREADS);
+
+        mailQueue = mock(MailQueue.class);
+        when(mailQueueFactory.getQueue(QUEUE_NAME)).thenReturn(mailQueue);
+    }
+
+    @Test
+    public void remoteDeliveryShouldStart() throws Exception {
+        when(mailQueue.deQueue()).thenAnswer(new Answer<MailQueue.MailQueueItem>() {
+            @Override
+            public MailQueue.MailQueueItem answer(InvocationOnMock invocation) throws Throwable {
+                countDownLatch.countDown();
+                Thread.sleep(TimeUnit.SECONDS.toMillis(20));
+                return null;
+            }
+        });
+        remoteDelivery.init(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.OUTGOING, QUEUE_NAME)
+            .setProperty(RemoteDeliveryConfiguration.HELO_NAME, "Hello_name")
+            .build());
+
+        countDownLatch.await();
+        verify(mailQueue).deQueue();
+    }
+
+    @After
+    public void tearDown() throws InterruptedException {
+        remoteDelivery.destroy();
+    }
+
+}


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


[33/50] [abbrv] james-project git commit: JAMES-1877 Refactor decision making upon SFE exception

Posted by ro...@apache.org.
JAMES-1877 Refactor decision making upon SFE exception


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

Branch: refs/heads/master
Commit: 1c6d1d06c60c911a59f43df50e233e1319796587
Parents: 6a2fe8a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 09:16:44 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:52 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/ExecutionResult.java |  16 ++
 .../mailets/remoteDelivery/MailDelivrer.java    | 125 ++++---------
 .../mailets/remoteDelivery/SFEHelper.java       |  66 +++++++
 .../remoteDelivery/MailDelivrerTest.java        | 183 +++++++++++++++++++
 4 files changed, 297 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1c6d1d06/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
index f984fd1..b408159 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 
 public class ExecutionResult {
@@ -72,5 +73,20 @@ public class ExecutionResult {
     public boolean isPermanent() {
         return executionState == ExecutionState.PERMANENT_FAILURE;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof ExecutionResult) {
+            ExecutionResult that = (ExecutionResult) o;
+            return Objects.equal(this.executionState, that.executionState)
+                && Objects.equal(this.exception, that.exception);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(executionState, exception);
+    }
 }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/1c6d1d06/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index df77eb8..c89a2a0 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -20,17 +20,15 @@
 package org.apache.james.transport.mailets.remoteDelivery;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 
 import javax.mail.Address;
 import javax.mail.MessagingException;
 import javax.mail.SendFailedException;
 import javax.mail.internet.InternetAddress;
 import javax.mail.internet.MimeMessage;
-import javax.mail.internet.ParseException;
 
 import org.apache.james.dnsservice.api.DNSService;
 import org.apache.james.dnsservice.api.TemporaryResolutionException;
@@ -39,7 +37,7 @@ import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
 import org.slf4j.Logger;
 
-import com.google.common.base.Optional;
+import com.google.common.annotations.VisibleForTesting;
 
 public class MailDelivrer {
 
@@ -177,105 +175,46 @@ public class MailDelivrer {
         }
     }
 
-    private ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {
+    @VisibleForTesting
+    ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {
         logSendFailedException(sfe);
-
-        // Copy the recipients as direct modification may not be possible
-        Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());
-
-        ExecutionResult deleteMessage = ExecutionResult.temporaryFailure();
         EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
-
-            /*
-             * 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
-             */
-        if (enhancedMessagingException.hasReturnCode() || enhancedMessagingException.hasNestedReturnCode()) {
-            if (enhancedMessagingException.isServerError()) {
-                deleteMessage = ExecutionResult.permanentFailure(sfe);
-            } else {
-                deleteMessage = ExecutionResult.temporaryFailure(sfe);
+        List<MailAddress> invalidAddresses = SFEHelper.getAddressesAsMailAddress(sfe.getInvalidAddresses(), logger);
+        List<MailAddress> validUnsentAddresses = SFEHelper.getAddressesAsMailAddress(sfe.getValidUnsentAddresses(), logger);
+        if (configuration.isDebug()) {
+            logger.debug("Mail " + mail.getName() + " has initially recipients: " + mail.getRecipients());
+            if (!invalidAddresses.isEmpty()) {
+                logger.debug("Invalid recipients: " + invalidAddresses);
+            }
+            if (!validUnsentAddresses.isEmpty()) {
+                logger.debug("Unsent recipients: " + validUnsentAddresses);
             }
         }
-
-        // log the original set of intended recipients
-        if (configuration.isDebug())
-            logger.debug("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.
-                        logger.debug("Can't parse invalid address: " + pe.getMessage());
-                    }
-                }
-                // Set the recipients for the mail
-                mail.setRecipients(recipients);
-
-                if (configuration.isDebug())
-                    logger.debug("Invalid recipients: " + recipients);
-                deleteMessage = ExecutionResult.permanentFailure(sfe);
-                logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
+        if (!validUnsentAddresses.isEmpty()) {
+            mail.setRecipients(validUnsentAddresses);
+            if (enhancedMessagingException.hasReturnCode()) {
+                boolean isPermanent = enhancedMessagingException.isServerError();
+                return logAndReturn(mail, ExecutionResult.onFailure(isPermanent, sfe));
+            } else {
+                return logAndReturn(mail, ExecutionResult.temporaryFailure(sfe));
             }
         }
+        if (!invalidAddresses.isEmpty()) {
+            mail.setRecipients(invalidAddresses);
+            return logAndReturn(mail, ExecutionResult.permanentFailure(sfe));
+        }
 
-        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.
-                        logger.debug("Can't parse unsent address: " + pe.getMessage());
-                    }
-                }
-                // Set the recipients for the mail
-                mail.setRecipients(recipients);
-                if (configuration.isDebug())
-                    logger.debug("Unsent recipients: " + recipients);
-
-                if (enhancedMessagingException.hasReturnCode()) {
-                    boolean isPermanent = enhancedMessagingException.isServerError();
-                    deleteMessage = ExecutionResult.onFailure(isPermanent, sfe);
-                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
-                } else {
-                    deleteMessage = ExecutionResult.temporaryFailure(sfe);
-                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
-                }
+        if (enhancedMessagingException.hasReturnCode() || enhancedMessagingException.hasNestedReturnCode()) {
+            if (enhancedMessagingException.isServerError()) {
+                return ExecutionResult.permanentFailure(sfe);
             }
         }
+        return ExecutionResult.temporaryFailure(sfe);
+    }
 
-
-        return deleteMessage;
+    private ExecutionResult logAndReturn(Mail mail, ExecutionResult executionResult) {
+        logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+        return executionResult;
     }
 
     private MessagingException handleSendFailException(Mail mail, SendFailedException sfe) throws SendFailedException {

http://git-wip-us.apache.org/repos/asf/james-project/blob/1c6d1d06/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java
new file mode 100644
index 0000000..a9650d3
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.mail.Address;
+import javax.mail.internet.AddressException;
+
+import org.apache.mailet.MailAddress;
+import org.slf4j.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+public class SFEHelper {
+
+    public static List<MailAddress> getAddressesAsMailAddress(Address[] addresses, final Logger logger) {
+        if (addresses == null) {
+            return ImmutableList.of();
+        }
+        return FluentIterable.from(Arrays.asList(addresses)).transform(new Function<Address, Optional<MailAddress>>() {
+            @Override
+            public Optional<MailAddress> apply(Address input) {
+                try {
+                    return Optional.of(new MailAddress(input.toString()));
+                } catch (AddressException e) {
+                    logger.debug("Can't parse unsent address: " + e.getMessage());
+                    return Optional.absent();
+                }
+            }
+        }).filter(new Predicate<Optional<MailAddress>>() {
+            @Override
+            public boolean apply(Optional<MailAddress> input) {
+                return input.isPresent();
+            }
+        }).transform(new Function<Optional<MailAddress>, MailAddress>() {
+            @Override
+            public MailAddress apply(Optional<MailAddress> input) {
+                return input.get();
+            }
+        }).toList();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1c6d1d06/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
new file mode 100644
index 0000000..9c5fe69
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
@@ -0,0 +1,183 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.mockito.Mockito.mock;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.mail.Address;
+import javax.mail.SendFailedException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.mail.smtp.SMTPSenderFailedException;
+
+public class MailDelivrerTest {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrerTest.class);
+
+    private MailDelivrer testee;
+
+    @Before
+    public void setUp() {
+        testee = new MailDelivrer(mock(RemoteDeliveryConfiguration.class), mock(MailDelivrerToHost.class), mock(DNSService.class), LOGGER);
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldReturnTemporaryFailureByDefault() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        SendFailedException sfe = new SendFailedException();
+        ExecutionResult executionResult = testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(executionResult).isEqualTo(ExecutionResult.temporaryFailure(sfe));
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldReturnTemporaryFailureWhenNotServerException() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        SendFailedException sfe = new SMTPSenderFailedException(new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString()), "Comand", 400, "An temporary error");
+        ExecutionResult executionResult = testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(executionResult).isEqualTo(ExecutionResult.temporaryFailure(sfe));
+    }
+
+    @Ignore("Return code is always ignored")
+    @Test
+    public void handleSenderFailedExceptionShouldReturnPermanentFailureWhenServerException() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        SendFailedException sfe = new SMTPSenderFailedException(new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString()), "Comand", 505, "An temporary error");
+        ExecutionResult executionResult = testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(executionResult).isEqualTo(ExecutionResult.permanentFailure(sfe));
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldReturnPermanentFailureWhenInvalidAndNotValidUnsent() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {};
+        Address[] invalid = {new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString())};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        ExecutionResult executionResult = testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(executionResult).isEqualTo(ExecutionResult.permanentFailure(sfe));
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldReturnTemporaryFailureWhenValidUnsent() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        ExecutionResult executionResult = testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(executionResult).isEqualTo(ExecutionResult.temporaryFailure(sfe));
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldReturnTemporaryFailureWhenInvalidAndValidUnsent() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString())};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        ExecutionResult executionResult = testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(executionResult).isEqualTo(ExecutionResult.temporaryFailure(sfe));
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldSetRecipientToInvalidWhenOnlyInvalid() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {};
+        Address[] invalid = {new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString())};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(mail.getRecipients()).containsOnly(MailAddressFixture.ANY_AT_JAMES);
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldSetRecipientToValidUnsentWhenOnlyValidUnsent() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(mail.getRecipients()).containsOnly(MailAddressFixture.OTHER_AT_JAMES);
+    }
+
+    @Test
+    public void handleSenderFailedExceptionShouldSetRecipientToValidUnsentWhenValidUnsentAndInvalid() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString())};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        testee.handleSenderFailedException(mail, sfe);
+
+        assertThat(mail.getRecipients()).containsOnly(MailAddressFixture.OTHER_AT_JAMES);
+    }
+}


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


[16/50] [abbrv] james-project git commit: MAILET-119 Don't count retry using errorMessage

Posted by ro...@apache.org.
MAILET-119 Don't count retry using errorMessage

Use Attribute for that


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

Branch: refs/heads/master
Commit: 2e60b2dcced3a90bbe408414f5a1f91df0d010c2
Parents: b563349
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 13:37:30 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:49 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRetriesHelper.java      | 17 ++++++++++++-----
 .../remoteDelivery/DeliveryRetryHelperTest.java    |  4 ++--
 2 files changed, 14 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/2e60b2dc/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
index 485127e..81e5c8c 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
@@ -19,25 +19,32 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
+import java.io.Serializable;
+
 import org.apache.mailet.Mail;
 
 public class DeliveryRetriesHelper {
 
+    public static final String DELIVERY_RETRY_COUNT = "delivery_retry_count";
+
     public static int retrieveRetries(Mail mail) {
         try {
-            return Integer.parseInt(mail.getErrorMessage());
-        } catch (NumberFormatException e) {
-            // Something strange was happen with the errorMessage..
+            Serializable value = mail.getAttribute(DELIVERY_RETRY_COUNT);
+            if (value != null) {
+                return (Integer) value;
+            }
+            return 0;
+        } catch (ClassCastException e) {
             return 0;
         }
     }
 
     public static void initRetries(Mail mail) {
-        mail.setErrorMessage("0");
+        mail.setAttribute(DELIVERY_RETRY_COUNT, 0);
     }
 
     public static void incrementRetries(Mail mail) {
-        mail.setErrorMessage(String.valueOf(retrieveRetries(mail) + 1));
+        mail.setAttribute(DELIVERY_RETRY_COUNT, retrieveRetries(mail) + 1);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/2e60b2dc/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
index 72ad294..406cd31 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
@@ -65,7 +65,7 @@ public class DeliveryRetryHelperTest {
 
     @Test
     public void retrieveRetriesShouldBeZeroOnInvalidValue() throws Exception {
-        FakeMail mail = FakeMail.builder().errorMessage("invalid").build();
+        FakeMail mail = FakeMail.builder().attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, "invalid").build();
 
         assertThat(DeliveryRetriesHelper.retrieveRetries(mail))
             .isEqualTo(0);
@@ -73,7 +73,7 @@ public class DeliveryRetryHelperTest {
 
     @Test
     public void incrementRetriesShouldWorkOnInvalidMails() throws Exception {
-        FakeMail mail = FakeMail.builder().errorMessage("invalid").build();
+        FakeMail mail = FakeMail.builder().attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, "invalid").build();
 
         DeliveryRetriesHelper.incrementRetries(mail);
 


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


[40/50] [abbrv] james-project git commit: JAMES-1877 VolatileIsDestroyed is not needed

Posted by ro...@apache.org.
JAMES-1877 VolatileIsDestroyed is not needed


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

Branch: refs/heads/master
Commit: 1f01e8ad204e7b1620457e77026b510066f74b68
Parents: 9646571
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 8 09:59:02 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:26 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 10 ++---
 .../remoteDelivery/DeliveryRunnable.java        | 15 +++----
 .../remoteDelivery/VolatileIsDestroyed.java     | 36 -----------------
 .../remoteDelivery/VolatileIsDestroyedTest.java | 41 --------------------
 4 files changed, 13 insertions(+), 89 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1f01e8ad/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 3a1f17d..006c6bc 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
@@ -25,6 +25,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.inject.Inject;
 import javax.mail.MessagingException;
@@ -41,7 +42,6 @@ import org.apache.james.transport.mailets.remoteDelivery.Bouncer;
 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.james.transport.mailets.remoteDelivery.VolatileIsDestroyed;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
 import org.apache.mailet.base.GenericMailet;
@@ -133,7 +133,7 @@ public class RemoteDelivery extends GenericMailet {
     private final DomainList domainList;
     private final MailQueueFactory queueFactory;
     private final Metric outgoingMailsMetric;
-    private final VolatileIsDestroyed volatileIsDestroyed;
+    private final AtomicBoolean isDestroyed;
     private final THREAD_STATE startThreads;
 
     private MailQueue queue;
@@ -151,7 +151,7 @@ public class RemoteDelivery extends GenericMailet {
         this.domainList = domainList;
         this.queueFactory = queueFactory;
         this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
-        this.volatileIsDestroyed = new VolatileIsDestroyed();
+        this.isDestroyed = new AtomicBoolean(false);
         this.startThreads = startThreads;
     }
 
@@ -181,7 +181,7 @@ public class RemoteDelivery extends GenericMailet {
                     logger,
                     getMailetContext(),
                     new Bouncer(configuration, getMailetContext(), logger),
-                    volatileIsDestroyed));
+                    isDestroyed));
         }
     }
 
@@ -259,7 +259,7 @@ public class RemoteDelivery extends GenericMailet {
     @Override
     public synchronized void destroy() {
         if (startThreads == THREAD_STATE.START_THREADS) {
-            volatileIsDestroyed.markAsDestroyed();
+            isDestroyed.set(true);
             executor.shutdown();
             notifyAll();
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1f01e8ad/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 7af269b..4841402 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -21,6 +21,7 @@ package org.apache.james.transport.mailets.remoteDelivery;
 
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.james.dnsservice.api.DNSService;
 import org.apache.james.lifecycle.api.LifecycleUtil;
@@ -50,33 +51,33 @@ public class DeliveryRunnable implements Runnable {
     private final Logger logger;
     private final Bouncer bouncer;
     private final MailDelivrer mailDelivrer;
-    private final VolatileIsDestroyed volatileIsDestroyed;
+    private final AtomicBoolean isDestroyed;
     private final Supplier<Date> dateSupplier;
 
     public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric,
-                            Logger logger, MailetContext mailetContext, Bouncer bouncer, VolatileIsDestroyed volatileIsDestroyed) {
+                            Logger logger, MailetContext mailetContext, Bouncer bouncer, AtomicBoolean isDestroyed) {
         this(queue, configuration, outgoingMailsMetric, logger, bouncer,
             new MailDelivrer(configuration, new MailDelivrerToHost(configuration, mailetContext, logger), dnsServer, bouncer, logger),
-            volatileIsDestroyed, CURRENT_DATE_SUPPLIER);
+            isDestroyed, CURRENT_DATE_SUPPLIER);
     }
 
     @VisibleForTesting
     DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, Metric outgoingMailsMetric, Logger logger, Bouncer bouncer,
-                     MailDelivrer mailDelivrer, VolatileIsDestroyed volatileIsDestroyed, Supplier<Date> dateSupplier) {
+                     MailDelivrer mailDelivrer, AtomicBoolean isDestroyeds, Supplier<Date> dateSupplier) {
         this.queue = queue;
         this.configuration = configuration;
         this.outgoingMailsMetric = outgoingMailsMetric;
         this.logger = logger;
         this.bouncer = bouncer;
         this.mailDelivrer = mailDelivrer;
-        this.volatileIsDestroyed = volatileIsDestroyed;
+        this.isDestroyed = isDestroyeds;
         this.dateSupplier = dateSupplier;
     }
 
     @Override
     public void run() {
         try {
-            while (!Thread.interrupted() && !volatileIsDestroyed.isDestroyed()) {
+            while (!Thread.interrupted() && !isDestroyed.get()) {
                 runStep();
             }
         } finally {
@@ -112,7 +113,7 @@ public class DeliveryRunnable implements Runnable {
             }
 
         } catch (Throwable e) {
-            if (!volatileIsDestroyed.isDestroyed()) {
+            if (!isDestroyed.get()) {
                 logger.error("Exception caught in RemoteDelivery.run()", e);
             }
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1f01e8ad/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java
deleted file mode 100644
index 19d4b36..0000000
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.transport.mailets.remoteDelivery;
-
-public class VolatileIsDestroyed {
-    private volatile boolean isDestroyed;
-
-    public VolatileIsDestroyed() {
-        this.isDestroyed = false;
-    }
-
-    public boolean isDestroyed() {
-        return isDestroyed;
-    }
-
-    public void markAsDestroyed() {
-        isDestroyed = true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1f01e8ad/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java
deleted file mode 100644
index dd10c8c..0000000
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.transport.mailets.remoteDelivery;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.Test;
-
-public class VolatileIsDestroyedTest {
-
-    @Test
-    public void isDestroyedShouldBeFalseByDefault() {
-        assertThat(new VolatileIsDestroyed().isDestroyed()).isFalse();
-    }
-
-    @Test
-    public void isDestroyedShouldBeTrueWhenMarkedAsDestroyed() {
-        VolatileIsDestroyed volatileIsDestroyed = new VolatileIsDestroyed();
-
-        volatileIsDestroyed.markAsDestroyed();
-
-        assertThat(volatileIsDestroyed.isDestroyed()).isTrue();
-    }
-}


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


[43/50] [abbrv] james-project git commit: JAMES-1877 Correct DNS error handling

Posted by ro...@apache.org.
JAMES-1877 Correct DNS error handling


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

Branch: refs/heads/master
Commit: d004e6ec653156da13742248ed2e6c36786b650e
Parents: 9898e18
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 10:54:10 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:27 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/remoteDelivery/MailDelivrer.java      | 2 +-
 .../james/transport/mailets/remoteDelivery/MailDelivrerTest.java  | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/d004e6ec/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index 27f66aa..1dfc81b 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -263,7 +263,7 @@ public class MailDelivrer {
         MessagingException messagingException = new MessagingException("There are no DNS entries for the hostname " + host + ".  I cannot determine where to send this message.");
         int retry = DeliveryRetriesHelper.retrieveRetries(mail);
         System.out.println("retyry " + retry);
-        if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
+        if (retry >= configuration.getDnsProblemRetry()) {
             return logAndReturn(mail, ExecutionResult.permanentFailure(messagingException));
         } else {
             return logAndReturn(mail, ExecutionResult.temporaryFailure(messagingException));

http://git-wip-us.apache.org/repos/asf/james-project/blob/d004e6ec/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
index 27de817..3f7b726 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
@@ -37,7 +37,6 @@ import org.apache.mailet.base.MailAddressFixture;
 import org.apache.mailet.base.test.FakeMail;
 import org.apache.mailet.base.test.FakeMailetConfig;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -223,7 +222,6 @@ public class MailDelivrerTest {
 
     @SuppressWarnings("deprecation")
     @Test
-    @Ignore("Fails if first delivery attempt")
     public void deliverShouldReturnTemporaryErrorWhenFirstDNSProblem() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
@@ -261,7 +259,6 @@ public class MailDelivrerTest {
 
     @SuppressWarnings("deprecation")
     @Test
-    @Ignore("One more failure is tolerated than specified by the configuration")
     public void deliverShouldReturnPermanentErrorWhenLimitDNSProblemReached() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()


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


[17/50] [abbrv] james-project git commit: JAMES-1877 Extract helper for counting delivery retries of a mail

Posted by ro...@apache.org.
JAMES-1877 Extract helper for counting delivery retries of a mail


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

Branch: refs/heads/master
Commit: b5633492b792ed4c86560e598dce492b033549b6
Parents: a62e460
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 15:57:56 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:49 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRetriesHelper.java   | 43 ++++++++++
 .../remoteDelivery/DeliveryRunnable.java        | 31 ++------
 .../remoteDelivery/DeliveryRetryHelperTest.java | 83 ++++++++++++++++++++
 3 files changed, 131 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/b5633492/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
new file mode 100644
index 0000000..485127e
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetriesHelper.java
@@ -0,0 +1,43 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import org.apache.mailet.Mail;
+
+public class DeliveryRetriesHelper {
+
+    public static int retrieveRetries(Mail mail) {
+        try {
+            return Integer.parseInt(mail.getErrorMessage());
+        } catch (NumberFormatException e) {
+            // Something strange was happen with the errorMessage..
+            return 0;
+        }
+    }
+
+    public static void initRetries(Mail mail) {
+        mail.setErrorMessage("0");
+    }
+
+    public static void incrementRetries(Mail mail) {
+        mail.setErrorMessage(String.valueOf(retrieveRetries(mail) + 1));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/b5633492/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index d9db12c..75fb3e2 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -136,14 +136,7 @@ public class DeliveryRunnable implements Runnable {
             LifecycleUtil.dispose(mail);
         } else {
             // Something happened that will delay delivery. Store it back in the retry repository.
-            int retries = 0;
-            try {
-                retries = Integer.parseInt(mail.getErrorMessage());
-            } catch (NumberFormatException e) {
-                // Something strange was happen with the errorMessage..
-            }
-
-            long delay = getNextDelay(retries);
+            long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
 
             if (configuration.isUsePriority()) {
                 // Use lowest priority for retries. See JAMES-1311
@@ -154,7 +147,6 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-
     /**
      * 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
@@ -558,12 +550,7 @@ public class DeliveryRunnable implements Runnable {
         logger.info("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
-        }
+        int retry = DeliveryRetriesHelper.retrieveRetries(mail);
         if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
             // The domain has no dns entry.. Return a permanent error
             return failMessage(mail, new MessagingException(exceptionBuffer), true);
@@ -647,8 +634,6 @@ public class DeliveryRunnable implements Runnable {
     }
 
     /**
-     * Insert the method's description here.
-     *
      * @param mail      org.apache.james.core.MailImpl
      * @param ex        javax.mail.MessagingException
      * @param permanent
@@ -659,21 +644,15 @@ public class DeliveryRunnable implements Runnable {
         if (!permanent) {
             if (!mail.getState().equals(Mail.ERROR)) {
                 mail.setState(Mail.ERROR);
-                mail.setErrorMessage("0");
+                DeliveryRetriesHelper.initRetries(mail);
                 mail.setLastUpdated(new Date());
             }
 
-            int retries = 0;
-            try {
-                retries = Integer.parseInt(mail.getErrorMessage());
-            } catch (NumberFormatException e) {
-                // Something strange was happen with the errorMessage..
-            }
+            int retries = DeliveryRetriesHelper.retrieveRetries(mail);
 
             if (retries < configuration.getMaxRetries()) {
                 logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
-                ++retries;
-                mail.setErrorMessage(retries + "");
+                DeliveryRetriesHelper.incrementRetries(mail);
                 mail.setLastUpdated(new Date());
                 return false;
             } else {

http://git-wip-us.apache.org/repos/asf/james-project/blob/b5633492/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
new file mode 100644
index 0000000..72ad294
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRetryHelperTest.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.Test;
+
+public class DeliveryRetryHelperTest {
+
+    @Test
+    public void retrieveRetriesShouldBeZeroByDefault() throws Exception {
+        assertThat(DeliveryRetriesHelper.retrieveRetries(FakeMail.defaultFakeMail()))
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void retrieveRetriesShouldBeZeroAfterInit() throws Exception {
+        FakeMail mail = FakeMail.defaultFakeMail();
+
+        DeliveryRetriesHelper.initRetries(mail);
+
+        assertThat(DeliveryRetriesHelper.retrieveRetries(mail))
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void retrieveRetriesShouldBeOneAfterIncrement() throws Exception {
+        FakeMail mail = FakeMail.defaultFakeMail();
+
+        DeliveryRetriesHelper.initRetries(mail);
+        DeliveryRetriesHelper.incrementRetries(mail);
+
+        assertThat(DeliveryRetriesHelper.retrieveRetries(mail))
+            .isEqualTo(1);
+    }
+
+    @Test
+    public void incrementRetriesShouldWorkOnNonInitializedMails() throws Exception {
+        FakeMail mail = FakeMail.defaultFakeMail();
+
+        DeliveryRetriesHelper.incrementRetries(mail);
+
+        assertThat(DeliveryRetriesHelper.retrieveRetries(mail))
+            .isEqualTo(1);
+    }
+
+    @Test
+    public void retrieveRetriesShouldBeZeroOnInvalidValue() throws Exception {
+        FakeMail mail = FakeMail.builder().errorMessage("invalid").build();
+
+        assertThat(DeliveryRetriesHelper.retrieveRetries(mail))
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void incrementRetriesShouldWorkOnInvalidMails() throws Exception {
+        FakeMail mail = FakeMail.builder().errorMessage("invalid").build();
+
+        DeliveryRetriesHelper.incrementRetries(mail);
+
+        assertThat(DeliveryRetriesHelper.retrieveRetries(mail))
+            .isEqualTo(1);
+    }
+}


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


[39/50] [abbrv] james-project git commit: JAMES-1877 Make naming on Error management clearer

Posted by ro...@apache.org.
JAMES-1877 Make naming on Error management clearer


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

Branch: refs/heads/master
Commit: 70ad2337775d27557fada77d6d0cc9258b57ed2e
Parents: aec9b2f
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 10:19:03 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:26 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/remoteDelivery/MailDelivrer.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/70ad2337/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index bb03b62..e6f1ec3 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -135,7 +135,7 @@ public class MailDelivrer {
                     return ExecutionResult.success();
                 }
             } catch (SendFailedException sfe) {
-                lastError = handleSendFailException(mail, sfe);
+                lastError = handleSendFailExceptionOnMxIteration(mail, sfe);
             } catch (MessagingException me) {
                 lastError = handleMessagingException(mail, me);
                 if (configuration.isDebug()) {
@@ -223,7 +223,7 @@ public class MailDelivrer {
         return executionResult;
     }
 
-    private MessagingException handleSendFailException(Mail mail, SendFailedException sfe) throws SendFailedException {
+    private MessagingException handleSendFailExceptionOnMxIteration(Mail mail, SendFailedException sfe) throws SendFailedException {
         logSendFailedException(sfe);
 
         if (sfe.getValidSentAddresses() != null) {


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


[21/50] [abbrv] james-project git commit: JAMES-1877 Extract a utility for sending bounce

Posted by ro...@apache.org.
JAMES-1877 Extract a utility for sending bounce


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

Branch: refs/heads/master
Commit: 26c6d9c4d1d332642a78464a386308f2c6846746
Parents: bc52f33
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 18:18:30 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:50 2017 +0700

----------------------------------------------------------------------
 .../mailet/base/test/FakeMailContext.java       | 112 +++--
 .../mailets/remoteDelivery/Bouncer.java         | 124 ++++++
 .../remoteDelivery/DeliveryRunnable.java        |  41 +-
 .../mailets/remoteDelivery/MessageComposer.java |  48 ---
 .../mailets/remoteDelivery/BouncerTest.java     | 425 +++++++++++++++++++
 5 files changed, 641 insertions(+), 109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/26c6d9c4/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java
index 6296e94..23188ea 100644
--- a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java
+++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java
@@ -56,6 +56,25 @@ public class FakeMailContext implements MailetContext {
         return new SentMail.Builder();
     }
 
+    public static SentMail fromMail(Mail mail ) throws MessagingException {
+        return sentMailBuilder()
+            .sender(mail.getSender())
+            .recipients(mail.getRecipients())
+            .message(mail.getMessage())
+            .state(mail.getState())
+            .attributes(buildAttributesMap(mail))
+            .build();
+    }
+
+    private static ImmutableMap<String, Serializable> buildAttributesMap(Mail mail) {
+        Map<String, Serializable> result = new HashMap<String, Serializable>();
+        List<String> attributesNames = Lists.newArrayList(mail.getAttributeNames());
+        for (String attributeName: attributesNames) {
+            result.put(attributeName, mail.getAttribute(attributeName));
+        }
+        return ImmutableMap.copyOf(result);
+    }
+
     public static FakeMailContext defaultContext() {
         return builder().build();
     }
@@ -80,7 +99,7 @@ public class FakeMailContext implements MailetContext {
             private MailAddress sender;
             private Optional<Collection<MailAddress>> recipients = Optional.absent();
             private MimeMessage msg;
-            private Optional<Map<String, Serializable>> attributes = Optional.absent();
+            private Map<String, Serializable> attributes = new HashMap<String, Serializable>();
             private Optional<String> state = Optional.absent();
 
             public Builder sender(MailAddress sender) {
@@ -104,7 +123,12 @@ public class FakeMailContext implements MailetContext {
             }
 
             public Builder attributes(Map<String, Serializable> attributes) {
-                this.attributes = Optional.of(attributes);
+                this.attributes.putAll(attributes);
+                return this;
+            }
+
+            public Builder attribute(String key, Serializable value) {
+                this.attributes.put(key, value);
                 return this;
             }
 
@@ -115,7 +139,7 @@ public class FakeMailContext implements MailetContext {
 
             public SentMail build() {
                 return new SentMail(sender, recipients.or(ImmutableList.<MailAddress>of()), msg,
-                    attributes.or(ImmutableMap.<String, Serializable>of()), state.or(Mail.DEFAULT));
+                    ImmutableMap.copyOf(attributes), state.or(Mail.DEFAULT));
             }
         }
 
@@ -179,22 +203,73 @@ public class FakeMailContext implements MailetContext {
         }
     }
 
+    public static class BouncedMail {
+        private final SentMail sentMail;
+        private final String message;
+        private final Optional<MailAddress> bouncer;
+
+        public BouncedMail(SentMail sentMail, String message, Optional<MailAddress> bouncer) {
+            this.sentMail = sentMail;
+            this.message = message;
+            this.bouncer = bouncer;
+        }
+
+        public SentMail getSentMail() {
+            return sentMail;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public Optional<MailAddress> getBouncer() {
+            return bouncer;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof BouncedMail) {
+                BouncedMail that = (BouncedMail) o;
+                return Objects.equal(this.sentMail, that.sentMail)
+                    && Objects.equal(this.message, that.message)
+                    && Objects.equal(this.bouncer, that.bouncer);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(sentMail, message, bouncer);
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this)
+                .add("sentMail", sentMail)
+                .add("message", message)
+                .add("bouncer", bouncer)
+                .toString();
+        }
+    }
+
     private final HashMap<String, Object> attributes;
     private final List<SentMail> sentMails;
+    private final List<BouncedMail> bouncedMails;
     private final Optional<Logger> logger;
 
     private FakeMailContext(Optional<Logger> logger) {
         attributes = new HashMap<String, Object>();
         sentMails = new ArrayList<SentMail>();
+        bouncedMails = new ArrayList<BouncedMail>();
         this.logger = logger;
     }
 
     public void bounce(Mail mail, String message) throws MessagingException {
-        // trivial implementation
+        bouncedMails.add(new BouncedMail(fromMail(mail), message, Optional.<MailAddress>absent()));
     }
 
     public void bounce(Mail mail, String message, MailAddress bouncer) throws MessagingException {
-        // trivial implementation
+        bouncedMails.add(new BouncedMail(fromMail(mail), message, Optional.fromNullable(bouncer)));
     }
 
     /**
@@ -263,13 +338,13 @@ public class FakeMailContext implements MailetContext {
     }
 
     public void sendMail(MimeMessage mimemessage) throws MessagingException {
-        sentMails.add(new SentMail.Builder()
+        sentMails.add(sentMailBuilder()
             .message(mimemessage)
             .build());
     }
 
     public void sendMail(MailAddress sender, Collection<MailAddress> recipients, MimeMessage msg) throws MessagingException {
-        sentMails.add(new SentMail.Builder()
+        sentMails.add(sentMailBuilder()
             .recipients(recipients)
             .sender(sender)
             .message(msg)
@@ -277,7 +352,7 @@ public class FakeMailContext implements MailetContext {
     }
 
     public void sendMail(MailAddress sender, Collection<MailAddress> recipients, MimeMessage msg, String state) throws MessagingException {
-        sentMails.add(new SentMail.Builder()
+        sentMails.add(sentMailBuilder()
             .recipients(recipients)
             .message(msg)
             .state(state)
@@ -286,22 +361,7 @@ public class FakeMailContext implements MailetContext {
     }
 
     public void sendMail(Mail mail) throws MessagingException {
-        sentMails.add(new SentMail.Builder()
-            .sender(mail.getSender())
-            .recipients(mail.getRecipients())
-            .message(mail.getMessage())
-            .state(mail.getState())
-            .attributes(buildAttributesMap(mail))
-            .build());
-    }
-
-    private ImmutableMap<String, Serializable> buildAttributesMap(Mail mail) {
-        Map<String, Serializable> result = new HashMap<String, Serializable>();
-        List<String> attributesNames = Lists.newArrayList(mail.getAttributeNames());
-        for (String attributeName: attributesNames) {
-            result.put(attributeName, mail.getAttribute(attributeName));
-        }
-        return ImmutableMap.copyOf(result);
+        sentMails.add(fromMail(mail));
     }
 
     public void setAttribute(String name, Serializable object) {
@@ -372,6 +432,10 @@ public class FakeMailContext implements MailetContext {
         return sentMails;
     }
 
+    public List<BouncedMail> getBouncedMails() {
+        return bouncedMails;
+    }
+
     @Override
     public Logger getLogger() {
         return logger.orNull();

http://git-wip-us.apache.org/repos/asf/james-project/blob/26c6d9c4/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
new file mode 100644
index 0000000..feebf84
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
@@ -0,0 +1,124 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.ConnectException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+import org.slf4j.Logger;
+
+public class Bouncer {
+
+    public static final String DELIVERY_ERROR = "delivery-error";
+    private final RemoteDeliveryConfiguration configuration;
+    private final MessageComposer messageComposer;
+    private final MailetContext mailetContext;
+    private final Logger logger;
+
+    public Bouncer(RemoteDeliveryConfiguration configuration, MessageComposer messageComposer, MailetContext mailetContext, Logger logger) {
+        this.configuration = configuration;
+        this.messageComposer = messageComposer;
+        this.mailetContext = mailetContext;
+        this.logger = logger;
+    }
+
+    public void bounce(Mail mail, Exception ex) {
+        if (mail.getSender() == null) {
+            logger.debug("Null Sender: no bounce will be generated for " + mail.getName());
+        } else {
+            if (configuration.getBounceProcessor() != null) {
+                mail.setAttribute(DELIVERY_ERROR, messageComposer.getErrorMsg(ex));
+                mail.setState(configuration.getBounceProcessor());
+                try {
+                    mailetContext.sendMail(mail);
+                } catch (MessagingException e) {
+                    logger.debug("Exception re-inserting failed mail: ", e);
+                }
+            } else {
+                bounceWithMailetContext(mail, ex);
+            }
+        }
+    }
+
+
+    private void bounceWithMailetContext(Mail mail, Exception ex) {
+        logger.debug("Sending failure message " + mail.getName());
+        try {
+            mailetContext.bounce(mail, explanationText(mail, ex));
+        } catch (MessagingException me) {
+            logger.debug("Encountered unexpected messaging exception while bouncing message: " + me.getMessage());
+        } catch (Exception e) {
+            logger.debug("Encountered unexpected exception while bouncing message: " + e.getMessage());
+        }
+    }
+
+    public String explanationText(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();
+        return sout.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/26c6d9c4/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index a3e08ef..39b38d0 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -116,7 +116,7 @@ public class DeliveryRunnable implements Runnable {
     private final DNSService dnsServer;
     private final Metric outgoingMailsMetric;
     private final Logger logger;
-    private final MailetContext mailetContext;
+    private final Bouncer bouncer;
     private final VolatileIsDestroyed volatileIsDestroyed;
     private final MessageComposer messageComposer;
     private final Converter7Bit converter7Bit;
@@ -127,10 +127,10 @@ public class DeliveryRunnable implements Runnable {
         this.dnsServer = dnsServer;
         this.outgoingMailsMetric = outgoingMailsMetric;
         this.logger = logger;
-        this.mailetContext = mailetContext;
         this.volatileIsDestroyed = volatileIsDestroyed;
         this.messageComposer = new MessageComposer(configuration);
         this.converter7Bit = new Converter7Bit(mailetContext);
+        this.bouncer = new Bouncer(configuration, messageComposer, mailetContext, logger);
     }
 
     /**
@@ -189,7 +189,7 @@ public class DeliveryRunnable implements Runnable {
                 handleTemporaryFailure(mail, executionResult);
                 break;
             case PERMANENT_FAILURE:
-                bounce(mail, executionResult.getException().orNull());
+                bouncer.bounce(mail, executionResult.getException().orNull());
                 break;
         }
     }
@@ -206,7 +206,7 @@ public class DeliveryRunnable implements Runnable {
             reAttemptDelivery(mail, retries);
         } else {
             logger.debug("Bouncing message " + mail.getName() + " after " + retries + " retries");
-            bounce(mail, new Exception("Too many retries failure. Bouncing after " + retries + " retries.", executionResult.getException().orNull()));
+            bouncer.bounce(mail, new Exception("Too many retries failure. Bouncing after " + retries + " retries.", executionResult.getException().orNull()));
         }
     }
 
@@ -711,39 +711,6 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private void bounce(Mail mail, Exception ex) {
-        if (mail.getSender() == null) {
-            logger.debug("Null Sender: no bounce will be generated for " + mail.getName());
-        }
-
-        if (configuration.getBounceProcessor() != null) {
-            // do the new DSN bounce setting attributes for DSN mailet
-            mail.setAttribute("delivery-error", messageComposer.getErrorMsg(ex));
-            mail.setState(configuration.getBounceProcessor());
-            // re-insert the mail into the spool for getting it passed to the dsn-processor
-            try {
-                mailetContext.sendMail(mail);
-            } catch (MessagingException e) {
-                // we shouldn't get an exception, because the mail was already processed
-                logger.debug("Exception re-inserting failed mail: ", e);
-            }
-        } else {
-            bounceWithMailetContext(mail, ex);
-        }
-    }
-
-
-    private void bounceWithMailetContext(Mail mail, Exception ex) {
-        logger.debug("Sending failure message " + mail.getName());
-        try {
-            mailetContext.bounce(mail, messageComposer.composeForBounce(mail, ex));
-        } catch (MessagingException me) {
-            logger.debug("Encountered unexpected messaging exception while bouncing message: " + me.getMessage());
-        } catch (Exception e) {
-            logger.debug("Encountered unexpected exception while bouncing message: " + e.getMessage());
-        }
-    }
-
     /**
      * Returns an Iterator over org.apache.mailet.HostAddress, a specialized
      * subclass of javax.mail.URLName, which provides location information for

http://git-wip-us.apache.org/repos/asf/james-project/blob/26c6d9c4/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
index a57b496..a323ba9 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
@@ -23,9 +23,6 @@ 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.Arrays;
 
 import javax.mail.MessagingException;
@@ -33,7 +30,6 @@ import javax.mail.SendFailedException;
 import javax.mail.internet.InternetAddress;
 
 import org.apache.mailet.Mail;
-import org.apache.mailet.MailAddress;
 
 public class MessageComposer {
 
@@ -43,50 +39,6 @@ public class MessageComposer {
         this.configuration = configuration;
     }
 
-    public String composeForBounce(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();
-        return sout.toString();
-    }
-
     /**
      * Try to return a usefull logString created of the Exception which was
      * given. Return null if nothing usefull could be done

http://git-wip-us.apache.org/repos/asf/james-project/blob/26c6d9c4/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
new file mode 100644
index 0000000..de683c0
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
@@ -0,0 +1,425 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.apache.james.transport.mailets.remoteDelivery.Bouncer.DELIVERY_ERROR;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import java.net.ConnectException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMail;
+import org.apache.mailet.base.test.FakeMailContext;
+import org.apache.mailet.base.test.FakeMailetConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class BouncerTest {
+    private static final Logger LOGGER = LoggerFactory.getLogger(BouncerTest.class);
+    public static final String HELLO_NAME = "hello_name";
+    public static final String BOUNCE_PROCESSOR = "bounce_processor";
+
+    private FakeMailContext mailetContext;
+
+    @Before
+    public void setUp() {
+        mailetContext = FakeMailContext.defaultContext();
+    }
+
+    @Test
+    public void bounceShouldCallMailetContextBounceByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        testee.bounce(mail, new Exception("Exception message"));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldIncludeMessagingExceptionMessageByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String exceptionMessage = "Exception message";
+        testee.bounce(mail, new MessagingException(exceptionMessage));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n" +
+                "\n" +
+                exceptionMessage + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldCustomizeSendFailedExceptionByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String exceptionMessage = "Error from remote server";
+        testee.bounce(mail, new MessagingException("Exception message", new SendFailedException(exceptionMessage)));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n" +
+                "\n" +
+                "Remote mail server told me: " + exceptionMessage + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldCustomizeUnknownHostExceptionByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String exceptionMessage = "I don't know him";
+        testee.bounce(mail, new MessagingException("Exception message", new UnknownHostException(exceptionMessage)));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n" +
+                "\n" +
+                "Unknown host: " + exceptionMessage + "\n" +
+                "This could be a DNS server error, a typo, or a problem with the recipient's mail server.\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldCustomizeConnectionExceptionByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String exceptionMessage = "Can not connect";
+        testee.bounce(mail, new MessagingException("Exception message", new ConnectException(exceptionMessage)));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n" +
+                "\n" +
+                exceptionMessage + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldCustomizeSocketExceptionByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String exceptionMessage = "Can not connect";
+        testee.bounce(mail, new MessagingException("Exception message", new SocketException(exceptionMessage)));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n" +
+                "\n" +
+                "Socket exception: " + exceptionMessage + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldCustomizeNestedMessagingExceptionByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String exceptionMessage = "Can not connect";
+        testee.bounce(mail, new MessagingException("Exception message", new MessagingException(exceptionMessage)));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n" +
+                "\n" +
+                exceptionMessage + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldNotBounceWithNoSenderByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .build();
+        String exceptionMessage = "Can not connect";
+        testee.bounce(mail, new MessagingException("Exception message", new ConnectException(exceptionMessage)));
+
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).isEmpty();
+    }
+
+    @Test
+    public void bounceShouldSupportExceptionWithoutMessagesByDefaultByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        testee.bounce(mail, new Exception("Exception message"));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldNotSupportMessagingExceptionWithoutMessagesByDefaultByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        testee.bounce(mail, new MessagingException());
+
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).isEmpty();
+    }
+
+    @Test
+    public void bounceShouldWorkWhenProcessorSpecified() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        String errorMessage = "message";
+        testee.bounce(mail, new MessagingException(errorMessage));
+
+        FakeMailContext.SentMail expected = FakeMailContext.sentMailBuilder()
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .attribute(DELIVERY_ERROR, errorMessage)
+            .state(BOUNCE_PROCESSOR)
+            .build();
+        assertThat(mailetContext.getSentMails()).containsOnly(expected);
+        assertThat(mailetContext.getBouncedMails()).isEmpty();
+    }
+
+    @Test
+    public void bounceShouldNotBounceWhenNoSenderWhenProcessorSpecified() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .build();
+        testee.bounce(mail, new MessagingException("message"));
+
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).isEmpty();
+    }
+
+    @Test
+    public void bounceShouldDisplayAddressByDefaultByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .recipient(MailAddressFixture.ANY_AT_JAMES2)
+            .build();
+        testee.bounce(mail, new Exception("Exception message"));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n\n" +
+                MailAddressFixture.ANY_AT_JAMES2.asString() + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+
+    @Test
+    public void bounceShouldDisplayAddressesByDefaultByDefault() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .recipients(MailAddressFixture.ANY_AT_JAMES2, MailAddressFixture.OTHER_AT_JAMES2)
+            .build();
+        testee.bounce(mail, new Exception("Exception message"));
+
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n\n" +
+                MailAddressFixture.ANY_AT_JAMES2.asString() + "\n" +
+                MailAddressFixture.OTHER_AT_JAMES2.asString() + "\n\n",
+            Optional.<MailAddress>absent());
+        assertThat(mailetContext.getSentMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
+    }
+}


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


[03/50] [abbrv] james-project git commit: JAMES-1877 Improve TimeConverter

Posted by ro...@apache.org.
JAMES-1877 Improve TimeConverter


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

Branch: refs/heads/master
Commit: 03fc53a4760e2d28a312fd57f9768f4ef54e3264
Parents: 788b393
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Nov 29 11:23:48 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 14:35:27 2017 +0700

----------------------------------------------------------------------
 .../org/apache/james/util/TimeConverter.java    |   8 +-
 .../apache/james/util/TimeConverterTest.java    | 242 +++++++++++++++++--
 2 files changed, 229 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/03fc53a4/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java b/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
index 6709de0..2a877b7 100644
--- a/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
+++ b/server/container/util/src/main/java/org/apache/james/util/TimeConverter.java
@@ -19,6 +19,7 @@
 package org.apache.james.util;
 
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -32,14 +33,19 @@ public class TimeConverter {
 
     static {
         // add allowed units and their respective multiplier
+        multipliers.put("ms", 1);
         multipliers.put("msec", 1);
         multipliers.put("msecs", 1);
+        multipliers.put("s", 1000);
         multipliers.put("sec", 1000);
         multipliers.put("secs", 1000);
+        multipliers.put("m", 1000 * 60);
         multipliers.put("minute", 1000 * 60);
         multipliers.put("minutes", 1000 * 60);
+        multipliers.put("h", 1000 * 60 * 60);
         multipliers.put("hour", 1000 * 60 * 60);
         multipliers.put("hours", 1000 * 60 * 60);
+        multipliers.put("d", 1000 * 60 * 60 * 24);
         multipliers.put("day", 1000 * 60 * 60 * 24);
         multipliers.put("days", 1000 * 60 * 60 * 24);
 
@@ -63,7 +69,7 @@ public class TimeConverter {
      *             Get thrown if an illegal unit was used
      */
     public static long getMilliSeconds(long amount, String unit) throws NumberFormatException {
-        Object multiplierObject = multipliers.get(unit);
+        Object multiplierObject = multipliers.get(unit.toLowerCase(Locale.US));
         if (multiplierObject == null) {
             throw new NumberFormatException("Unknown unit: " + unit);
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/03fc53a4/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java b/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
index adf620f..b05a935 100644
--- a/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
+++ b/server/container/util/src/test/java/org/apache/james/util/TimeConverterTest.java
@@ -20,6 +20,8 @@ package org.apache.james.util;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.util.concurrent.TimeUnit;
+
 import org.junit.Test;
 
 public class TimeConverterTest {
@@ -33,8 +35,8 @@ public class TimeConverterTest {
         //Then
         assertThat(actual).isEqualTo(expected);
     }
-        
-    @Test 
+
+    @Test
     public void getMilliSecondsShouldConvertValueWhenMsecAmountAsString() {
         //Given
         long expected = 2;
@@ -43,6 +45,46 @@ public class TimeConverterTest {
         //Then
         assertThat(actual).isEqualTo(expected);
     }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMsUnit() {
+        //Given
+        long expected = 2;
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "ms");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMsAmountAsString() {
+        //Given
+        long expected = 2;
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 ms");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMsUnitCapital() {
+        //Given
+        long expected = 2;
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "Ms");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMsCapitalAmountAsString() {
+        //Given
+        long expected = 2;
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 Ms");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
    
     @Test
     public void getMilliSecondsShouldConvertValueWhenMsecsUnit() {
@@ -63,11 +105,31 @@ public class TimeConverterTest {
         //Then
         assertThat(actual).isEqualTo(expected);
     }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenSUnit() {
+        //Given
+        long expected = TimeUnit.SECONDS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "s");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenSAmountAsString() {
+        //Given
+        long expected = TimeUnit.SECONDS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 s");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
     
     @Test 
     public void getMilliSecondsShouldConvertValueWhenSecUnit() { 
         //Given
-        long expected = 2000;
+        long expected = TimeUnit.SECONDS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "sec");
         //Then
@@ -77,16 +139,36 @@ public class TimeConverterTest {
     @Test 
     public void getMilliSecondsShouldConvertValueWhenSecAmountAsString() {
         //Given
-        long expected = 2000;
+        long expected = TimeUnit.SECONDS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 sec");
         //Then
         assertThat(actual).isEqualTo(expected);
     }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenSecCapitalUnit() {
+        //Given
+        long expected = TimeUnit.SECONDS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "Sec");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenSecCapitalAmountAsString() {
+        //Given
+        long expected = TimeUnit.SECONDS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 Sec");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
     
     @Test 
     public void getMilliSecondsShouldConvertValueWhenSecsUnit() {
-        long expected = 2000;
+        long expected = TimeUnit.SECONDS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "secs");
         //Then
@@ -96,17 +178,37 @@ public class TimeConverterTest {
     @Test 
     public void getMilliSecondsShouldConvertValueWhenSecsAmountAsString() {
         //Given
-        long expected = 2000;
+        long expected = TimeUnit.SECONDS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 secs");
         //Then
         assertThat(actual).isEqualTo(expected);
     }
-    
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMUnit() {
+        //Given
+        long expected = TimeUnit.MINUTES.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "m");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMAmountAsString() {
+        //Given
+        long expected = TimeUnit.MINUTES.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 m");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
     @Test 
     public void getMilliSecondsShouldConvertValueWhenMinuteUnit() {
         //Given
-        long expected = 120000;
+        long expected = TimeUnit.MINUTES.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "minute");
         //Then
@@ -116,7 +218,7 @@ public class TimeConverterTest {
     @Test 
     public void getMilliSecondsShouldConvertValueWhenMinuteAmountAsString() {
         //Given
-        long expected = 120000;
+        long expected = TimeUnit.MINUTES.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 minute");
         //Then
@@ -124,9 +226,29 @@ public class TimeConverterTest {
     }
 
     @Test
+    public void getMilliSecondsShouldConvertValueWhenMinuteCapitalUnit() {
+        //Given
+        long expected = TimeUnit.MINUTES.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "Minute");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenMinuteCapitalAmountAsString() {
+        //Given
+        long expected = TimeUnit.MINUTES.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 Minute");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
     public void getMilliSecondsShouldConvertValueWhenMinutesUnit() {
         //Given
-        long expected = 120000;
+        long expected = TimeUnit.MINUTES.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "minutes");
         //Then
@@ -136,17 +258,37 @@ public class TimeConverterTest {
     @Test 
     public void getMilliSecondsShouldConvertValueWhenMinutesAmountAsString() {
         //Given
-        long expected = 120000;
+        long expected = TimeUnit.MINUTES.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 minutes");
         //Then
         assertThat(actual).isEqualTo(expected);
     }
-    
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenHUnit() {
+        //Given
+        long expected = TimeUnit.HOURS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "h");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenHAmountAsString() {
+        //Given
+        long expected = TimeUnit.HOURS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 h");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
     @Test
     public void getMilliSecondsShouldConvertValueWhenHourUnit() {
         //Given
-        long expected = 7200000;
+        long expected = TimeUnit.HOURS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "hour");
         //Then
@@ -156,17 +298,37 @@ public class TimeConverterTest {
     @Test
     public void getMilliSecondsShouldConvertValueWhenHourAmountAsString() {
         //Given
-        long expected = 7200000;
+        long expected = TimeUnit.HOURS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 hour");
         //Then
         assertThat(actual).isEqualTo(expected);
     }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenHourCapitalUnit() {
+        //Given
+        long expected = TimeUnit.HOURS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "Hour");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenHourCapitalAmountAsString() {
+        //Given
+        long expected = TimeUnit.HOURS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 Hour");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
     
     @Test
     public void getMilliSecondsShouldConvertValueWhenHoursUnit() {
         //Given
-        long expected = 7200000;
+        long expected = TimeUnit.HOURS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "hours");
         //Then
@@ -176,17 +338,37 @@ public class TimeConverterTest {
     @Test 
     public void getMilliSecondsShouldConvertValueWhenHoursAmountAsString() {
         //Given
-        long expected = 7200000;
+        long expected = TimeUnit.HOURS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 hours");
         //Then
         assertThat(actual).isEqualTo(expected);
     }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenDUnit() {
+        //Given
+        long expected = TimeUnit.DAYS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "d");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenDAmountAsString() {
+        //Given
+        long expected = TimeUnit.DAYS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 d");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
     
     @Test
     public void getMilliSecondsShouldConvertValueWhenDayUnit() {
         //Given
-        long expected = 172800000;
+        long expected = TimeUnit.DAYS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "day");
         //Then
@@ -196,17 +378,37 @@ public class TimeConverterTest {
     @Test
     public void getMilliSecondsShouldConvertValueWhenDayAmountAsString() {
         //Given
-        long expected = 172800000;
+        long expected = TimeUnit.DAYS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 day");
         //Then
         assertThat(actual).isEqualTo(expected);
     }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenDayCapitalUnit() {
+        //Given
+        long expected = TimeUnit.DAYS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds(2, "Day");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void getMilliSecondsShouldConvertValueWhenDayCapitalAmountAsString() {
+        //Given
+        long expected = TimeUnit.DAYS.toMillis(2);
+        //When
+        long actual = TimeConverter.getMilliSeconds("2 Day");
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
     
     @Test
     public void getMilliSecondsShouldConvertValueWhenDaysUnit() {
         //Given
-        long expected = 172800000;
+        long expected = TimeUnit.DAYS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds(2, "days");
         //Then
@@ -216,7 +418,7 @@ public class TimeConverterTest {
     @Test 
     public void getMilliSecondsShouldConvertValueWhenDaysAmountAsString() {
         //Given
-        long expected = 172800000;
+        long expected = TimeUnit.DAYS.toMillis(2);
         //When
         long actual = TimeConverter.getMilliSeconds("2 days");
         //Then


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


[18/50] [abbrv] james-project git commit: JAMES-1877 Continue to flatten DeliveryRunnable

Posted by ro...@apache.org.
JAMES-1877 Continue to flatten DeliveryRunnable


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

Branch: refs/heads/master
Commit: 5742cbf0839e3213a6ef34ef4f8da00660e975dd
Parents: 80039f1
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 14:43:07 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:49 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 234 ++++++++++---------
 1 file changed, 130 insertions(+), 104 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/5742cbf0/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index ea35f7c..c917f19 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -314,30 +314,19 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private Boolean tryDeliver(Mail mail, Session session) throws MessagingException {
-        if (configuration.isDebug()) {
-            logger.debug("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) {
+        if (mail.getRecipients().isEmpty()) {
             logger.info("No recipients specified... not sure how this could have happened.");
             return true;
         }
+        if (configuration.isDebug()) {
+            logger.debug("Attempting to deliver " + mail.getName());
+        }
 
         // Figure out which servers to try to send to. This collection
         // will hold all the possible target servers
         Iterator<HostAddress> targetServers;
         if (configuration.getGatewayServer().isEmpty()) {
-            MailAddress rcpt = recipients.iterator().next();
+            MailAddress rcpt = mail.getRecipients().iterator().next();
             String host = rcpt.getDomain();
 
             // Lookup the possible targets
@@ -353,100 +342,18 @@ public class DeliveryRunnable implements Runnable {
             targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
         }
 
+        return doDeliver(mail, session, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
+    }
+
+    private Boolean doDeliver(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
         MessagingException lastError = null;
 
         while (targetServers.hasNext()) {
             HostAddress outgoingMailServer = targetServers.next();
             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);
+                if (tryDeliveryToHost(mail, session, message, addr, outgoingMailServer)) {
+                    return true;
                 }
-                logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
-                    + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
-
-                // 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()) {
-                            logger.debug(me.getMessage(), me.getCause());
-                        } else {
-                            logger.info(me.getMessage());
-                        }
-                        continue;
-                    }
-                    // if the transport is a SMTPTransport (from sun) some
-                    // performance enhancement can be done.
-                    if (transport.getClass().getName().endsWith(".SMTPTransport")) {
-                        // 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 (!transport.supportsExtension(BIT_MIME_8)) {
-                            try {
-                                convertTo7Bit(message);
-                            } catch (IOException e) {
-                                // An error has occured during the 7bit conversion.
-                                // The error is logged and the message is sent anyway.
-
-                                logger.error("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) {
-                            logger.error("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) {
-                            logger.error("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;
-                    }
-                }
-                logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
-                    " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
-                outgoingMailsMetric.increment();
-                return true;
             } catch (SendFailedException sfe) {
                 logSendFailedException(sfe);
 
@@ -514,6 +421,125 @@ public class DeliveryRunnable implements Runnable {
         return null;
     }
 
+    private InternetAddress[] convertToInetAddr(Collection<MailAddress> recipients) {
+        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();
+        }
+        return addr;
+    }
+
+    private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
+        boolean success = false;
+        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);
+        }
+        logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
+            + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
+
+        // 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()) );
+            if (!connect(outgoingMailServer, transport)) {
+                success = false;
+            }
+            transport.sendMessage(adaptToTransport(message, transport), addr);
+            success = true;
+            logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
+                " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
+            outgoingMailsMetric.increment();
+        } finally {
+            closeTransport(mail, outgoingMailServer, transport);
+        }
+        return success;
+    }
+
+    private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException {
+        // if the transport is a SMTPTransport (from sun) some
+        // performance enhancement can be done.
+        if (transport.getClass().getName().endsWith(".SMTPTransport")) {
+            // 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 (!transport.supportsExtension(BIT_MIME_8)) {
+                try {
+                    convertTo7Bit(message);
+                } catch (IOException e) {
+                    // An error has occured during the 7bit conversion.
+                    // The error is logged and the message is sent anyway.
+
+                    logger.error("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) {
+                logger.error("Error during the conversion to 7 bit.", e);
+            }
+        }
+        return message;
+    }
+
+    private void closeTransport(Mail mail, HostAddress outgoingMailServer, SMTPTransport transport) {
+        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) {
+                logger.error("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;
+        }
+    }
+
+    private boolean connect(HostAddress outgoingMailServer, SMTPTransport transport) {
+        try {
+            if (configuration.getAuthUser() != null) {
+                transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
+            } else {
+                transport.connect();
+            }
+            return true;
+        } 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()) {
+                logger.debug(me.getMessage(), me.getCause());
+            } else {
+                logger.info(me.getMessage());
+            }
+            return false;
+        }
+    }
+
     private boolean handleTemporaryResolutionException(Mail mail, String host) {
         logger.info("Temporary problem looking up mail server for host: " + host);
         // temporary problems


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


[42/50] [abbrv] james-project git commit: JAMES-1877 Avoid inspection for SFE known and present on the ClassPath

Posted by ro...@apache.org.
JAMES-1877 Avoid inspection for SFE known and present on the ClassPath

This actually should improve the way return code is handled


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

Branch: refs/heads/master
Commit: aec9b2fc40b3f508c926a4432197a00a4aa0c540
Parents: b299e32
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 10:02:52 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:26 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/EnhancedMessagingException.java   | 15 +++++++++++++++
 .../mailets/remoteDelivery/MailDelivrerTest.java     |  2 --
 2 files changed, 15 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/aec9b2fc/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
index 44b40bd..61a7152 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
@@ -28,6 +28,9 @@ import javax.mail.internet.InternetAddress;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.sun.mail.smtp.SMTPAddressFailedException;
+import com.sun.mail.smtp.SMTPSendFailedException;
+import com.sun.mail.smtp.SMTPSenderFailedException;
 
 public class EnhancedMessagingException {
 
@@ -76,6 +79,18 @@ public class EnhancedMessagingException {
     }
 
     private Optional<Integer> computeReturnCode() {
+        if (messagingException instanceof SMTPAddressFailedException) {
+            SMTPAddressFailedException addressFailedException = (SMTPAddressFailedException) this.messagingException;
+            return Optional.of(addressFailedException.getReturnCode());
+        }
+        if (messagingException instanceof SMTPSendFailedException) {
+            SMTPSendFailedException sendFailedException = (SMTPSendFailedException) this.messagingException;
+            return Optional.of(sendFailedException.getReturnCode());
+        }
+        if (messagingException instanceof SMTPSenderFailedException) {
+            SMTPSenderFailedException senderFailedException = (SMTPSenderFailedException) this.messagingException;
+            return Optional.of(senderFailedException.getReturnCode());
+        }
         if (messagingException.getClass().getName().endsWith(".SMTPSendFailedException")
             || messagingException.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
             try {

http://git-wip-us.apache.org/repos/asf/james-project/blob/aec9b2fc/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
index c96e9a1..477d945 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
@@ -33,7 +33,6 @@ import org.apache.mailet.Mail;
 import org.apache.mailet.base.MailAddressFixture;
 import org.apache.mailet.base.test.FakeMail;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -72,7 +71,6 @@ public class MailDelivrerTest {
         assertThat(executionResult).isEqualTo(ExecutionResult.temporaryFailure(sfe));
     }
 
-    @Ignore("Return code is always ignored")
     @Test
     public void handleSenderFailedExceptionShouldReturnPermanentFailureWhenServerException() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();


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


[20/50] [abbrv] james-project git commit: JAMES-1877 Finish flatten DeliveryRunnable

Posted by ro...@apache.org.
JAMES-1877 Finish flatten DeliveryRunnable


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

Branch: refs/heads/master
Commit: 3b02d3f32f7d69593593acdb7c6a2ea4f7bcd386
Parents: 5742cbf
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 15:46:40 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:49 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 282 ++++++++++---------
 1 file changed, 144 insertions(+), 138 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/3b02d3f3/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index c917f19..9c31696 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -53,6 +53,7 @@ import org.apache.mailet.MailAddress;
 import org.apache.mailet.MailetContext;
 import org.slf4j.Logger;
 
+import com.google.common.base.Optional;
 import com.sun.mail.smtp.SMTPTransport;
 
 @SuppressWarnings("deprecation")
@@ -167,17 +168,14 @@ public class DeliveryRunnable implements Runnable {
      */
     private boolean deliver(Mail mail, Session session) {
         try {
-            Boolean host = tryDeliver(mail, session);
-            if (host != null) {
-                return host;
-            }
+            return Optional.fromNullable(tryDeliver(mail, session))
+                .or(failMessage(mail, new MessagingException("No mail server(s) available at this time."), false));
             /*
              * 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);
         } catch (SendFailedException sfe) {
             return handleSenderFailedException(mail, sfe);
         } catch (MessagingException ex) {
@@ -197,6 +195,124 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
+    private Boolean tryDeliver(Mail mail, Session session) throws MessagingException {
+        if (mail.getRecipients().isEmpty()) {
+            logger.info("No recipients specified... not sure how this could have happened.");
+            return true;
+        }
+        if (configuration.isDebug()) {
+            logger.debug("Attempting to deliver " + mail.getName());
+        }
+
+        // Figure out which servers to try to send to. This collection
+        // will hold all the possible target servers
+        Iterator<HostAddress> targetServers;
+        if (configuration.getGatewayServer().isEmpty()) {
+            MailAddress rcpt = mail.getRecipients().iterator().next();
+            String host = rcpt.getDomain();
+
+            // Lookup the possible targets
+            try {
+                targetServers = new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
+            } catch (TemporaryResolutionException e) {
+                return handleTemporaryResolutionException(mail, host);
+            }
+            if (!targetServers.hasNext()) {
+                return handleNoTargetServer(mail, host);
+            }
+        } else {
+            targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
+        }
+
+        return doDeliver(mail, session, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
+    }
+
+    private Boolean doDeliver(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
+        MessagingException lastError = null;
+
+        while (targetServers.hasNext()) {
+            try {
+                if (tryDeliveryToHost(mail, session, message, addr, targetServers.next())) {
+                    return true;
+                }
+            } catch (SendFailedException sfe) {
+                lastError = handleSendFailException(mail, sfe);
+            } catch (MessagingException me) {
+                lastError = handleMessagingException(mail, 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;
+        }
+        return null;
+    }
+
+    private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
+        boolean success = false;
+        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);
+        }
+        logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
+            + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
+
+        // 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()) );
+            if (!connect(outgoingMailServer, transport)) {
+                success = false;
+            }
+            transport.sendMessage(adaptToTransport(message, transport), addr);
+            success = true;
+            logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
+                " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
+            outgoingMailsMetric.increment();
+        } finally {
+            closeTransport(mail, outgoingMailServer, transport);
+        }
+        return success;
+    }
+
+    private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
+        MessagingException lastError;// MessagingException are horribly difficult to figure out what actually happened.
+        logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
+        if ((me.getNextException() != null) && (me.getNextException() instanceof 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;
+        } else {
+            // 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;
+        }
+        return lastError;
+    }
+
     private boolean handleSenderFailedException(Mail mail, SendFailedException sfe) {
         logSendFailedException(sfe);
 
@@ -313,112 +429,40 @@ public class DeliveryRunnable implements Runnable {
         return deleteMessage;
     }
 
-    private Boolean tryDeliver(Mail mail, Session session) throws MessagingException {
-        if (mail.getRecipients().isEmpty()) {
-            logger.info("No recipients specified... not sure how this could have happened.");
-            return true;
-        }
-        if (configuration.isDebug()) {
-            logger.debug("Attempting to deliver " + mail.getName());
-        }
-
-        // Figure out which servers to try to send to. This collection
-        // will hold all the possible target servers
-        Iterator<HostAddress> targetServers;
-        if (configuration.getGatewayServer().isEmpty()) {
-            MailAddress rcpt = mail.getRecipients().iterator().next();
-            String host = rcpt.getDomain();
+    private MessagingException handleSendFailException(Mail mail, SendFailedException sfe) throws SendFailedException {
+        logSendFailedException(sfe);
 
-            // Lookup the possible targets
-            try {
-                targetServers = new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
-            } catch (TemporaryResolutionException e) {
-                return handleTemporaryResolutionException(mail, host);
+        if (sfe.getValidSentAddresses() != null) {
+            Address[] validSent = sfe.getValidSentAddresses();
+            if (validSent.length > 0) {
+                logger.debug( "Mail (" + mail.getName() + ") sent successfully for " + Arrays.asList(validSent));
             }
-            if (!targetServers.hasNext()) {
-                return handleNoTargetServer(mail, host);
-            }
-        } else {
-            targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
         }
 
-        return doDeliver(mail, session, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
-    }
-
-    private Boolean doDeliver(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
-        MessagingException lastError = null;
-
-        while (targetServers.hasNext()) {
-            HostAddress outgoingMailServer = targetServers.next();
-            try {
-                if (tryDeliveryToHost(mail, session, message, addr, outgoingMailServer)) {
-                    return true;
-                }
-            } catch (SendFailedException sfe) {
-                logSendFailedException(sfe);
-
-                if (sfe.getValidSentAddresses() != null) {
-                    Address[] validSent = sfe.getValidSentAddresses();
-                    if (validSent.length > 0) {
-                        logger.debug( "Mail (" + mail.getName() + ") sent successfully for " + Arrays.asList(validSent));
-                    }
-                }
-
                 /*
                  * 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())
-                        logger.debug("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
+        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 (MessagingException me) {
-                // MessagingException are horribly difficult to figure out what actually happened.
-                logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
-                if ((me.getNextException() != null) && (me.getNextException() instanceof 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;
-                } else {
-                    // 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;
-                }
+            } catch (ClassCastException cce) {
+            } catch (IllegalArgumentException iae) {
             }
-        } // 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;
         }
-        return null;
+
+        if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
+            if (configuration.isDebug())
+                logger.debug("Send failed, " + sfe.getValidUnsentAddresses().length + " valid addresses remain, continuing with any other servers");
+            return sfe;
+        } else {
+            // There are no valid addresses left to send, so rethrow
+            throw sfe;
+        }
     }
 
     private InternetAddress[] convertToInetAddr(Collection<MailAddress> recipients) {
@@ -431,44 +475,6 @@ public class DeliveryRunnable implements Runnable {
         return addr;
     }
 
-    private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
-        boolean success = false;
-        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);
-        }
-        logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
-            + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
-
-        // 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()) );
-            if (!connect(outgoingMailServer, transport)) {
-                success = false;
-            }
-            transport.sendMessage(adaptToTransport(message, transport), addr);
-            success = true;
-            logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
-                " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
-            outgoingMailsMetric.increment();
-        } finally {
-            closeTransport(mail, outgoingMailServer, transport);
-        }
-        return success;
-    }
-
     private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException {
         // if the transport is a SMTPTransport (from sun) some
         // performance enhancement can be done.


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


[41/50] [abbrv] james-project git commit: JAMES-1877 Refactor DnsHelper

Posted by ro...@apache.org.
JAMES-1877 Refactor DnsHelper


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

Branch: refs/heads/master
Commit: b299e3265423bcd64a3be52e0cc10d4f02c75120
Parents: 1f01e8a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 14:38:00 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:26 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/DnsHelper.java       | 23 +++-----------------
 1 file changed, 3 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/b299e326/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
index 5e92040..6d396be 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
@@ -19,7 +19,6 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
-import java.util.Collection;
 import java.util.Iterator;
 
 import org.apache.james.dnsservice.api.DNSService;
@@ -31,6 +30,7 @@ import org.slf4j.Logger;
 @SuppressWarnings("deprecation")
 public class DnsHelper {
 
+    public static final boolean USE_SEVERAL_IP = false;
     private final DNSService dnsServer;
     private final RemoteDeliveryConfiguration configuration;
     private final Logger logger;
@@ -43,27 +43,10 @@ public class DnsHelper {
 
     public Iterator<HostAddress> retrieveHostAddressIterator(String host) throws TemporaryResolutionException {
         if (configuration.getGatewayServer().isEmpty()) {
-            return new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
+            return new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, USE_SEVERAL_IP, logger);
         } else {
-            return getGatewaySMTPHostAddresses(configuration.getGatewayServer());
+            return new MXHostAddressIterator(configuration.getGatewayServer().iterator(), dnsServer, USE_SEVERAL_IP, logger);
         }
     }
 
-    /**
-     * 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) {
-        return new MXHostAddressIterator(gatewayServers.iterator(), dnsServer, false, logger);
-    }
-
 }


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


[09/50] [abbrv] james-project git commit: JAMES-1877 Extract DeliveryRunnable from RemoteDelivery

Posted by ro...@apache.org.
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 <bt...@linagora.com>
Authored: Wed Nov 30 19:30:37 2016 +0700
Committer: Benoit Tellier <bt...@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-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[07/50] [abbrv] james-project git commit: JAMES-1877 Extract RemoteDelivery configuration to an other class

Posted by ro...@apache.org.
JAMES-1877 Extract RemoteDelivery configuration to an other class


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

Branch: refs/heads/master
Commit: 6fa52984020803b4d4c7de43d42e6dad3bfad6a6
Parents: 61c6309
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Nov 30 11:09:09 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:04:27 2017 +0700

----------------------------------------------------------------------
 mailet/base/pom.xml                             |   5 +
 .../org/apache/mailet/base/GenericMailet.java   |   2 +-
 .../java/org/apache/mailet/base/MailetUtil.java |  12 +-
 .../org/apache/mailet/base/MailetUtilTest.java  |   9 +-
 .../james/transport/mailets/RemoteDelivery.java | 281 +-----
 .../RemoteDeliveryConfiguration.java            | 288 ++++++
 .../mailets/remoteDelivery/Repeat.java          |   2 +-
 .../RemoteDeliveryConfigurationTest.java        | 911 +++++++++++++++++++
 .../mailets/remoteDelivery/RepeatTest.java      |   6 +-
 9 files changed, 1267 insertions(+), 249 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/mailet/base/pom.xml
----------------------------------------------------------------------
diff --git a/mailet/base/pom.xml b/mailet/base/pom.xml
index d40cc5d..0c47808 100644
--- a/mailet/base/pom.xml
+++ b/mailet/base/pom.xml
@@ -76,6 +76,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-guava</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/mailet/base/src/main/java/org/apache/mailet/base/GenericMailet.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/main/java/org/apache/mailet/base/GenericMailet.java b/mailet/base/src/main/java/org/apache/mailet/base/GenericMailet.java
index c56a226..741f3f7 100644
--- a/mailet/base/src/main/java/org/apache/mailet/base/GenericMailet.java
+++ b/mailet/base/src/main/java/org/apache/mailet/base/GenericMailet.java
@@ -79,7 +79,7 @@ public abstract class GenericMailet implements Mailet, MailetConfig {
         if (config == null) {
             throw new NullPointerException("Mailet configuration must be set before getInitParameter is called.");
         }
-        return MailetUtil.getInitParameter(config, name, defaultValue);
+        return MailetUtil.getInitParameter(config, name).or(defaultValue);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/mailet/base/src/main/java/org/apache/mailet/base/MailetUtil.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/main/java/org/apache/mailet/base/MailetUtil.java b/mailet/base/src/main/java/org/apache/mailet/base/MailetUtil.java
index 0155d5c..642f760 100644
--- a/mailet/base/src/main/java/org/apache/mailet/base/MailetUtil.java
+++ b/mailet/base/src/main/java/org/apache/mailet/base/MailetUtil.java
@@ -23,6 +23,8 @@ package org.apache.mailet.base;
 
 import org.apache.mailet.MailetConfig;
 
+import com.google.common.base.Optional;
+
 
 /**
  * Collects utility methods.
@@ -91,20 +93,18 @@ public class MailetUtil {
      * <p>Gets a boolean valued init parameter.</p>
      * @param config not null
      * @param name name of the init parameter to be queried
-     * @param defaultValue this value will be substituted when the named value
-     * cannot be parse or when the init parameter is absent
      * @return true when the init parameter is <code>true</code> (ignoring case);
      * false when the init parameter is <code>false</code> (ignoring case);
      * otherwise the default value
      */
-    public static boolean getInitParameter(MailetConfig config, String name, boolean defaultValue) {
+    public static Optional<Boolean> getInitParameter(MailetConfig config, String name) {
         String value = config.getInitParameter(name);
         if ("true".equalsIgnoreCase(value)) {
-            return true;
+            return Optional.of(true);
         }
         if ("false".equalsIgnoreCase(value)){
-            return false;
+            return Optional.of(false);
         }
-        return defaultValue;
+        return Optional.absent();
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/mailet/base/src/test/java/org/apache/mailet/base/MailetUtilTest.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/test/java/org/apache/mailet/base/MailetUtilTest.java b/mailet/base/src/test/java/org/apache/mailet/base/MailetUtilTest.java
index db1f131..cb56b99 100644
--- a/mailet/base/src/test/java/org/apache/mailet/base/MailetUtilTest.java
+++ b/mailet/base/src/test/java/org/apache/mailet/base/MailetUtilTest.java
@@ -20,6 +20,7 @@
 package org.apache.mailet.base;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
 
 import org.apache.mailet.base.test.FakeMailetConfig;
 import org.junit.Test;
@@ -27,6 +28,7 @@ import org.junit.Test;
 public class MailetUtilTest {
 
     private static final String A_PARAMETER = "aParameter";
+    public static final String DEFAULT_VALUE = "default";
 
     @Test
     public void getInitParameterShouldReturnTrueWhenIsValueTrueLowerCase() {
@@ -77,17 +79,16 @@ public class MailetUtilTest {
     }
 
     @Test
-    public void getInitParameterShouldReturnDefaultValueWhenNull() {
+    public void getInitParameterShouldReturnAbsentWhenNull() {
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
                 .build();
-        assertThat(MailetUtil.getInitParameter(mailetConfig, A_PARAMETER, false)).isFalse();
-        assertThat(MailetUtil.getInitParameter(mailetConfig, A_PARAMETER, true)).isTrue();
+        assertThat(MailetUtil.getInitParameter(mailetConfig, A_PARAMETER)).isAbsent();
     }
 
     private boolean getParameterValued(String value, boolean defaultValue) {
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
             .setProperty(A_PARAMETER, value)
             .build();
-        return MailetUtil.getInitParameter(mailetConfig, A_PARAMETER, defaultValue);
+        return MailetUtil.getInitParameter(mailetConfig, A_PARAMETER).or(defaultValue);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/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 88f8ea7..0067607 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
@@ -33,11 +33,9 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
-import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.concurrent.TimeUnit;
 
@@ -65,8 +63,7 @@ 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.DelaysAndMaxRetry;
-import org.apache.james.transport.mailets.remoteDelivery.HeloNameProvider;
+import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliveryConfiguration;
 import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
 import org.apache.mailet.HostAddress;
 import org.apache.mailet.Mail;
@@ -156,101 +153,23 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     private final DomainList domainList;
     private final MailQueueFactory queueFactory;
     private final Metric outgoingMailsMetric;
-    private final Properties defprops; // Default properties for the JavaMail Session
     private final Collection<Thread> workersThreads;
 
-    /**
-     * Flag to define verbose logging messages.
-     */
-    private boolean isDebug = false;
-
-    /**
-     * List of Delay Times. Controls frequency of retry attempts.
-     */
-    private List<Long> delayTimes;
-
-    /**
-     * Maximum no. of retries (Defaults to 5).
-     */
-    private int maxRetries;
-
-    /**
-     * Default number of ms to timeout on smtp delivery
-     */
-    private long smtpTimeout = 180000;
-
-    /**
-     * If false then ANY address errors will cause the transmission to fail
-     */
-    private boolean sendPartial = false;
-
-    /**
-     * The amount of time JavaMail will wait before giving up on a socket
-     * connect()
-     */
-    private int connectionTimeout = 60000;
-
-    /**
-     * No. of threads used to process messages that should be retried.
-     */
-    private int workersThreadCount = 1;
-
-    /**
-     * The server(s) to send all email to
-     */
-    private Collection<String> gatewayServer = null;
-
-    /**
-     * Auth for gateway server
-     */
-    private String authUser = null;
-
-    /**
-     * Password for gateway server
-     */
-    private String authPass = null;
-
-    /**
-     * True, if the bind configuration parameter is supplied,
-     * RemoteDeliverySocketFactory will be used in this case.
-     */
-    private boolean isBindUsed = false;
+    private MailQueue queue;
+    private Logger logger;
+    private RemoteDeliveryConfiguration configuration;
 
     /**
      * Flag used by 'run' method to end itself.
      */
     private volatile boolean destroyed = false;
 
-    /**
-     * the processor for creating Bounces
-     */
-    private String bounceProcessor = null;
-
-
-    /**
-     * The retry count dnsProblemErrors
-     */
-    private int dnsProblemRetry = 0;
-
-    private MailQueue queue;
-
-    private Logger logger;
-
-    private boolean usePriority;
-
-    private boolean startTLS = false;
-
-    private boolean isSSLEnable = false;
-
-    private HeloNameProvider heloNameProvider;
-
     @Inject
     public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory) {
         this.dnsServer = dnsServer;
         this.domainList = domainList;
         this.queueFactory = queueFactory;
         this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
-        this.defprops = new Properties();
         this.workersThreads = new Vector<Thread>();
     }
 
@@ -261,124 +180,20 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      * @throws MessagingException on failure to initialize attributes.
      */
     public void init() throws MessagingException {
-        // Set isDebug flag.
-        isDebug = (getInitParameter("debug") == null) ? false : Boolean.valueOf(getInitParameter("debug"));
-
         logger = getMailetContext().getLogger();
-
-        try {
-            int intendedMaxRetries = Integer.parseInt(getInitParameter("maxRetries", "5"));
-            DelaysAndMaxRetry delaysAndMaxRetry = DelaysAndMaxRetry.from(intendedMaxRetries, getInitParameter("delayTime"));
-            maxRetries = delaysAndMaxRetry.getMaxRetries();
-            delayTimes = delaysAndMaxRetry.getExpendedDelays();
-        } catch (Exception e) {
-            log("Invalid maxRetries setting: " + getInitParameter("maxRetries"));
-        }
-        // Get the path for the 'Outgoing' repository. This is the place on the
-        // file system where Mail objects will be saved during the 'delivery'
-        // processing. This can be changed to a repository on a database (e.g.
-        // db://maildb/spool/retry).
-        String outgoing = getInitParameter("outgoing");
-        if (outgoing == null) {
-            outgoing = "outgoing";
-        }
-        queue = queueFactory.getQueue(outgoing);
-
-        try {
-            if (getInitParameter("timeout") != null) {
-                smtpTimeout = Integer.parseInt(getInitParameter("timeout"));
-            }
-        } catch (Exception e) {
-            log("Invalid timeout setting: " + getInitParameter("timeout"));
-        }
-
-        try {
-            if (getInitParameter("connectiontimeout") != null) {
-                connectionTimeout = Integer.parseInt(getInitParameter("connectiontimeout"));
-            }
-        } catch (Exception e) {
-            log("Invalid timeout setting: " + getInitParameter("timeout"));
-        }
-
-        sendPartial = (getInitParameter("sendpartial") == null) ? false : Boolean.valueOf(getInitParameter("sendpartial"));
-
-        bounceProcessor = getInitParameter("bounceProcessor");
-
-        String sTLS = getInitParameter("startTLS");
-        if (sTLS != null) {
-            startTLS = Boolean.valueOf(sTLS);
-        }
-
-        isSSLEnable = (getInitParameter("sslEnable") == null) ? false : Boolean.valueOf(getInitParameter("sslEnable"));
-
-        String gateway = getInitParameter("gateway");
-        String gatewayPort = getInitParameter("gatewayPort");
-
-        if (gateway != null) {
-            gatewayServer = new ArrayList<String>();
-            StringTokenizer st = new StringTokenizer(gateway, ",");
-            while (st.hasMoreTokens()) {
-                String server = st.nextToken().trim();
-                if (server.indexOf(':') < 0 && gatewayPort != null) {
-                    server += ":";
-                    server += gatewayPort;
-                }
-
-                if (isDebug)
-                    log("Adding SMTP gateway: " + server);
-                gatewayServer.add(server);
-            }
-            authUser = getInitParameter("gatewayUsername");
-            // backward compatibility with 2.3.x
-            if (authUser == null) {
-                authUser = getInitParameter("gatewayusername");
-            }
-            authPass = getInitParameter("gatewayPassword");
-        }
-
-        /*
-      JavaMail delivery socket binds to this local address. If null the
-      JavaMail default will be used.
-     */
-        String bindAddress = getInitParameter("bind");
-        isBindUsed = bindAddress != null;
+        configuration = new RemoteDeliveryConfiguration(getMailetConfig(), domainList);
+        queue = queueFactory.getQueue(configuration.getOutGoingQueueName());
+        initDeliveryThreads();
         try {
-            if (isBindUsed)
-                RemoteDeliverySocketFactory.setBindAdress(bindAddress);
+            if (configuration.isBindUsed())
+                RemoteDeliverySocketFactory.setBindAdress(configuration.getBindAddress());
         } catch (UnknownHostException e) {
-            log("Invalid bind setting (" + bindAddress + "): " + e.toString());
+            log("Invalid bind setting (" + configuration.getBindAddress() + "): " + e.toString());
         }
-
-
-        // deal with <mail.*> attributes, passing them to javamail
-        Iterator<String> i = getInitParameterNames();
-        while (i.hasNext()) {
-            String name = i.next();
-            if (name.startsWith("mail.")) {
-                defprops.put(name, getInitParameter(name));
-            }
-
-        }
-
-        String dnsRetry = getInitParameter("maxDnsProblemRetries");
-        if (dnsRetry != null && !dnsRetry.equals("")) {
-            dnsProblemRetry = Integer.parseInt(dnsRetry);
-        }
-
-        heloNameProvider = new HeloNameProvider(getInitParameter("heloName"), domainList);
-
-        String prio = getInitParameter("usePriority");
-        if (prio != null) {
-            usePriority = Boolean.valueOf(prio);
-        }
-
-        // Start Workers Threads.
-        workersThreadCount = Integer.parseInt(getInitParameter("deliveryThreads"));
-        initDeliveryThreads();
     }
 
     private void initDeliveryThreads() {
-        for (int a = 0; a < workersThreadCount; a++) {
+        for (int a = 0; a < configuration.getWorkersThreadCount(); a++) {
             String threadName = "Remote delivery thread (" + a + ")";
             Thread t = new Thread(this, threadName);
             t.start();
@@ -393,10 +208,10 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      * @return the next delay time to use, given the retry count
      */
     private long getNextDelay(int retry_count) {
-        if (retry_count > delayTimes.size()) {
+        if (retry_count > configuration.getDelayTimes().size()) {
             return Delay.DEFAULT_DELAY_TIME;
         }
-        return delayTimes.get(retry_count - 1);
+        return configuration.getDelayTimes().get(retry_count - 1);
     }
 
     @Override
@@ -416,17 +231,17 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     @Override
     public void service(Mail mail) throws MessagingException {
         // Do I want to give the internal key, or the message's Message ID
-        if (isDebug) {
+        if (configuration.isDebug()) {
             log("Remotely delivering mail " + mail.getName());
         }
         Collection<MailAddress> recipients = mail.getRecipients();
 
-        if (usePriority) {
+        if (configuration.isUsePriority()) {
 
             // Use highest prio for new emails. See JAMES-1311
             mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, MailPrioritySupport.HIGH_PRIORITY);
         }
-        if (gatewayServer == null) {
+        if (configuration.getGatewayServer() == null) {
             // Must first organize the recipients into distinct servers (name
             // made case insensitive)
             Hashtable<String, Collection<MailAddress>> targets = new Hashtable<String, Collection<MailAddress>>();
@@ -449,7 +264,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             // outgoing mail repository
             String name = mail.getName();
             for (Map.Entry<String, Collection<MailAddress>> entry : targets.entrySet()) {
-                if (isDebug) {
+                if (configuration.isDebug()) {
                     String logMessageBuffer = "Sending mail to " + entry.getValue() + " on host " + entry.getKey();
                     log(logMessageBuffer);
                 }
@@ -464,8 +279,8 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             }
         } else {
             // Store the mail unaltered for processing by the gateway server(s)
-            if (isDebug) {
-                String logMessageBuffer = "Sending mail to " + mail.getRecipients() + " via " + gatewayServer;
+            if (configuration.isDebug()) {
+                String logMessageBuffer = "Sending mail to " + mail.getRecipients() + " via " + configuration.getGatewayServer();
                 log(logMessageBuffer);
             }
 
@@ -521,20 +336,20 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
         // messages created by an inputstream.
         props.setProperty("mail.smtp.allow8bitmime", "true");
         // Sets timeout on going connections
-        props.put("mail.smtp.timeout", smtpTimeout + "");
+        props.put("mail.smtp.timeout", configuration.getSmtpTimeout() + "");
 
-        props.put("mail.smtp.connectiontimeout", connectionTimeout + "");
-        props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
+        props.put("mail.smtp.connectiontimeout", configuration.getConnectionTimeout() + "");
+        props.put("mail.smtp.sendpartial", String.valueOf(configuration.isSendPartial()));
 
-        props.put("mail.smtp.localhost", heloNameProvider.getHeloName());
+        props.put("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName());
 
         // handle starttls
-        props.put("mail.smtp.starttls.enable", String.valueOf(startTLS));
+        props.put("mail.smtp.starttls.enable", String.valueOf(configuration.isStartTLS()));
 
         // handle SSLEnable
-        props.put("mail.smtp.ssl.enable", String.valueOf(isSSLEnable));
+        props.put("mail.smtp.ssl.enable", String.valueOf(configuration.isSSLEnable()));
 
-        if (isBindUsed) {
+        if (configuration.isBindUsed()) {
             // undocumented JavaMail 1.2 feature, smtp transport will use
             // our socket factory, which will also set the local address
             props.put("mail.smtp.socketFactory.class", RemoteDeliverySocketFactory.class.getClass());
@@ -543,11 +358,11 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             props.put("mail.smtp.socketFactory.fallback", "false");
         }
 
-        if (authUser != null) {
+        if (configuration.getAuthUser() != null) {
             props.put("mail.smtp.auth", "true");
         }
 
-        props.putAll(defprops);
+        props.putAll(configuration.getJavaxAdditionalProperties());
 
         final Session session = obtainSession(props);
         try {
@@ -567,7 +382,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     String key = mail.getName();
 
                     try {
-                        if (isDebug) {
+                        if (configuration.isDebug()) {
                             String message = Thread.currentThread().getName() + " will process mail " + key;
                             log(message);
                         }
@@ -593,7 +408,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
 
                             long delay = getNextDelay(retries);
 
-                            if (usePriority) {
+                            if (configuration.isUsePriority()) {
                                 // Use lowest priority for retries. See JAMES-1311
                                 mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, MailPrioritySupport.LOW_PRIORITY);
                             }
@@ -657,7 +472,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      */
     private boolean deliver(Mail mail, Session session) {
         try {
-            if (isDebug) {
+            if (configuration.isDebug()) {
                 log("Attempting to deliver " + mail.getName());
             }
             MimeMessage message = mail.getMessage();
@@ -679,7 +494,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             // Figure out which servers to try to send to. This collection
             // will hold all the possible target servers
             Iterator<HostAddress> targetServers;
-            if (gatewayServer == null) {
+            if (configuration.getGatewayServer() == null) {
                 MailAddress rcpt = recipients.iterator().next();
                 String host = rcpt.getDomain();
 
@@ -703,7 +518,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     } catch (NumberFormatException e) {
                         // Unable to parse retryCount
                     }
-                    if (retry == 0 || retry > dnsProblemRetry) {
+                    if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
                         // The domain has no dns entry.. Return a permanent
                         // error
                         return failMessage(mail, new MessagingException(exceptionBuffer), true);
@@ -712,7 +527,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     }
                 }
             } else {
-                targetServers = getGatewaySMTPHostAddresses(gatewayServer);
+                targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
             }
 
             MessagingException lastError = null;
@@ -745,10 +560,10 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     SMTPTransport transport = null;
                     try {
                         transport =  (SMTPTransport) session.getTransport(outgoingMailServer);
-                        transport.setLocalHost( props.getProperty("mail.smtp.localhost", heloNameProvider.getHeloName()) );
+                        transport.setLocalHost( props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName()) );
                         try {
-                            if (authUser != null) {
-                                transport.connect(outgoingMailServer.getHostName(), authUser, authPass);
+                            if (configuration.getAuthUser() != null) {
+                                transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
                             } else {
                                 transport.connect();
                             }
@@ -762,7 +577,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                             // failing the message at the end of the loop.
 
                             // Also include the stacktrace if debug is enabled. See JAMES-1257
-                            if (isDebug) {
+                            if (configuration.isDebug()) {
                                 log(me.getMessage(), me.getCause());
                             } else {
                                 log(me.getMessage());
@@ -868,7 +683,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     }
 
                     if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
-                        if (isDebug)
+                        if (configuration.isDebug())
                             log("Send failed, " + sfe.getValidUnsentAddresses().length + " valid addresses remain, continuing with any other servers");
                         lastError = sfe;
                     } else {
@@ -967,7 +782,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             }
 
             // log the original set of intended recipients
-            if (isDebug)
+            if (configuration.isDebug())
                 log("Recipients: " + recipients);
 
             if (sfe.getInvalidAddresses() != null) {
@@ -987,7 +802,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     // Set the recipients for the mail
                     mail.setRecipients(recipients);
 
-                    if (isDebug)
+                    if (configuration.isDebug())
                         log("Invalid recipients: " + recipients);
                     deleteMessage = failMessage(mail, sfe, true);
                 }
@@ -1009,7 +824,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     }
                     // Set the recipients for the mail
                     mail.setRecipients(recipients);
-                    if (isDebug)
+                    if (configuration.isDebug())
                         log("Unsent recipients: " + recipients);
                     if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
                         int returnCode = (Integer) invokeGetter(sfe, "getReturnCode");
@@ -1141,7 +956,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      * JavaMail 1.3.2.
      */
     private void logSendFailedException(SendFailedException sfe) {
-        if (isDebug) {
+        if (configuration.isDebug()) {
             MessagingException me = sfe;
             if (me.getClass().getName().endsWith(".SMTPSendFailedException")) {
                 try {
@@ -1247,7 +1062,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
 
         logBuffer.append(": ");
         out.print(logBuffer.toString());
-        if (isDebug)
+        if (configuration.isDebug())
             ex.printStackTrace(out);
         log(sout.toString());
         if (!permanent) {
@@ -1264,7 +1079,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                 // Something strange was happen with the errorMessage..
             }
 
-            if (retries < maxRetries) {
+            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;
@@ -1282,7 +1097,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             return true;
         }
 
-        if (bounceProcessor != null) {
+        if (configuration.getBounceProcessor() != null) {
             // do the new DSN bounce
             // setting attributes for DSN mailet
             String cause;
@@ -1292,7 +1107,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                 cause = ex.getMessage();
             }
             mail.setAttribute("delivery-error", cause);
-            mail.setState(bounceProcessor);
+            mail.setState(configuration.getBounceProcessor());
             // re-insert the mail into the spool for getting it passed to the
             // dsn-processor
             MailetContext mc = getMailetContext();
@@ -1330,7 +1145,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
         PrintWriter out = new PrintWriter(sout, true);
         String machine;
         try {
-            machine = heloNameProvider.getHeloName();
+            machine = configuration.getHeloNameProvider().getHeloName();
 
         } catch (Exception e) {
             machine = "[address unknown]";

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
new file mode 100644
index 0000000..621f3fe
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
@@ -0,0 +1,288 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.mailet.MailetConfig;
+import org.apache.mailet.base.MailetUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+
+public class RemoteDeliveryConfiguration {
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(RemoteDeliveryConfiguration.class);
+
+    public static final String DELIVERY_THREADS = "deliveryThreads";
+    public static final String USE_PRIORITY = "usePriority";
+    public static final String MAX_DNS_PROBLEM_RETRIES = "maxDnsProblemRetries";
+    public static final String HELO_NAME = "heloName";
+    public static final String JAVAX_PREFIX = "mail.";
+    public static final String BIND = "bind";
+    public static final String GATEWAY_PASSWORD = "gatewayPassword";
+    public static final String GATEWAY_USERNAME_COMPATIBILITY = "gatewayusername";
+    public static final String GATEWAY_USERNAME = "gatewayUsername";
+    public static final String GATEWAY_PORT = "gatewayPort";
+    public static final String GATEWAY = "gateway";
+    public static final String SSL_ENABLE = "sslEnable";
+    public static final String START_TLS = "startTLS";
+    public static final String BOUNCE_PROCESSOR = "bounceProcessor";
+    public static final String SENDPARTIAL = "sendpartial";
+    public static final String TIMEOUT = "timeout";
+    public static final String CONNECTIONTIMEOUT = "connectiontimeout";
+    public static final String OUTGOING = "outgoing";
+    public static final String MAX_RETRIES = "maxRetries";
+    public static final String DELAY_TIME = "delayTime";
+    public static final String DEBUG = "debug";
+    public static final int DEFAULT_SMTP_TIMEOUT = 180000;
+    public static final String DEFAULT_OUTGOING_QUEUE_NAME = "outgoing";
+    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+    public static final int DEFAULT_DNS_RETRY_PROBLEM = 0;
+    public static final int DEFAULT_MAX_RETRY = 5;
+    public static final String ADDRESS_PORT_SEPARATOR = ":";
+
+    private final boolean isDebug;
+    private final boolean usePriority;
+    private final boolean startTLS;
+    private final boolean isSSLEnable;
+    private final boolean isBindUsed;
+    private final boolean sendPartial;
+    private final int maxRetries;
+    private final long smtpTimeout;
+    private final int dnsProblemRetry;
+    private final int connectionTimeout;
+    private final int workersThreadCount;
+    private final List<Long> delayTimes;
+    private final HeloNameProvider heloNameProvider;
+    private final String outGoingQueueName;
+    private final String bindAddress;
+    private final String bounceProcessor;
+    private final Collection<String> gatewayServer;
+    private final String authUser;
+    private final String authPass;
+    private final Properties javaxAdditionalProperties;
+
+    public RemoteDeliveryConfiguration(MailetConfig mailetConfig, DomainList domainList) {
+        isDebug = MailetUtil.getInitParameter(mailetConfig, DEBUG).or(false);
+        startTLS = MailetUtil.getInitParameter(mailetConfig, START_TLS).or(false);
+        isSSLEnable = MailetUtil.getInitParameter(mailetConfig, SSL_ENABLE).or(false);
+        usePriority = MailetUtil.getInitParameter(mailetConfig, USE_PRIORITY).or(false);
+        sendPartial = MailetUtil.getInitParameter(mailetConfig, SENDPARTIAL).or(false);
+        outGoingQueueName = Optional.fromNullable(mailetConfig.getInitParameter(OUTGOING)).or(DEFAULT_OUTGOING_QUEUE_NAME);
+        bounceProcessor = mailetConfig.getInitParameter(BOUNCE_PROCESSOR);
+        bindAddress = mailetConfig.getInitParameter(BIND);
+
+        DelaysAndMaxRetry delaysAndMaxRetry = computeDelaysAndMaxRetry(mailetConfig);
+        maxRetries = delaysAndMaxRetry.getMaxRetries();
+        delayTimes = delaysAndMaxRetry.getExpendedDelays();
+        smtpTimeout = computeSmtpTimeout(mailetConfig);
+        connectionTimeout = computeConnectionTimeout(mailetConfig);
+        dnsProblemRetry = computeDnsProblemRetry(mailetConfig);
+        heloNameProvider = new HeloNameProvider(mailetConfig.getInitParameter(HELO_NAME), domainList);
+        workersThreadCount = Integer.valueOf(mailetConfig.getInitParameter(DELIVERY_THREADS));
+
+        String gatewayPort = mailetConfig.getInitParameter(GATEWAY_PORT);
+        String gateway = mailetConfig.getInitParameter(GATEWAY);
+        gatewayServer = computeGatewayServers(gatewayPort, gateway);
+        if (gateway != null) {
+            authUser = computeGatewayUser(mailetConfig);
+            authPass = mailetConfig.getInitParameter(GATEWAY_PASSWORD);
+        } else {
+            authUser = null;
+            authPass = null;
+        }
+        isBindUsed = bindAddress != null;
+        javaxAdditionalProperties = computeJavaxProperties(mailetConfig);
+    }
+
+    private Properties computeJavaxProperties(MailetConfig mailetConfig) {
+        Properties result = new Properties();
+        // deal with <mail.*> attributes, passing them to javamail
+        for (String propertyName : ImmutableList.copyOf(mailetConfig.getInitParameterNames())) {
+            if (propertyName.startsWith(JAVAX_PREFIX)) {
+                result.put(propertyName, mailetConfig.getInitParameter(propertyName));
+            }
+        }
+        return result;
+    }
+
+    private int computeDnsProblemRetry(MailetConfig mailetConfig) {
+        String dnsRetry = mailetConfig.getInitParameter(MAX_DNS_PROBLEM_RETRIES);
+        if (!Strings.isNullOrEmpty(dnsRetry)) {
+            return Integer.valueOf(dnsRetry);
+        } else {
+            return DEFAULT_DNS_RETRY_PROBLEM;
+        }
+    }
+
+    private int computeConnectionTimeout(MailetConfig mailetConfig) {
+        try {
+            return Integer.valueOf(
+                Optional.fromNullable(mailetConfig.getInitParameter(CONNECTIONTIMEOUT))
+                    .or(String.valueOf(DEFAULT_CONNECTION_TIMEOUT)));
+        } catch (Exception e) {
+            LOGGER.warn("Invalid timeout setting: " + mailetConfig.getInitParameter(TIMEOUT));
+            return DEFAULT_CONNECTION_TIMEOUT;
+        }
+    }
+
+    private long computeSmtpTimeout(MailetConfig mailetConfig) {
+        try {
+            if (mailetConfig.getInitParameter(TIMEOUT) != null) {
+                return Integer.valueOf(mailetConfig.getInitParameter(TIMEOUT));
+            } else {
+                return DEFAULT_SMTP_TIMEOUT;
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Invalid timeout setting: " + mailetConfig.getInitParameter(TIMEOUT));
+            return DEFAULT_SMTP_TIMEOUT;
+        }
+    }
+
+    private DelaysAndMaxRetry computeDelaysAndMaxRetry(MailetConfig mailetConfig) {
+        try {
+            int intendedMaxRetries = Integer.valueOf(
+                Optional.fromNullable(mailetConfig.getInitParameter(MAX_RETRIES))
+                    .or(String.valueOf(DEFAULT_MAX_RETRY)));
+            return DelaysAndMaxRetry.from(intendedMaxRetries, mailetConfig.getInitParameter(DELAY_TIME));
+        } catch (Exception e) {
+            LOGGER.warn("Invalid maxRetries setting: " + mailetConfig.getInitParameter(MAX_RETRIES));
+            return DelaysAndMaxRetry.defaults();
+        }
+    }
+
+    private String computeGatewayUser(MailetConfig mailetConfig) {
+        // backward compatibility with 2.3.x
+        String user = mailetConfig.getInitParameter(GATEWAY_USERNAME);
+        if (user == null) {
+            return mailetConfig.getInitParameter(GATEWAY_USERNAME_COMPATIBILITY);
+        }
+        return user;
+    }
+
+    private List<String> computeGatewayServers(String gatewayPort, String gateway) {
+        if (gateway != null) {
+            ImmutableList.Builder<String> builder = ImmutableList.builder();
+            Iterable<String> gatewayParts = Splitter.on(',').split(gateway);
+            for(String gatewayPart : gatewayParts) {
+                builder.add(parsePart(gatewayPort, gatewayPart));
+            }
+            return builder.build();
+        } else {
+            return ImmutableList.of();
+        }
+    }
+
+    private String parsePart(String gatewayPort, String gatewayPart) {
+        String address = gatewayPart.trim();
+        if (address.contains(ADDRESS_PORT_SEPARATOR) && gatewayPort != null) {
+            return address + ADDRESS_PORT_SEPARATOR + gatewayPort;
+        }
+        return address;
+    }
+
+    public boolean isDebug() {
+        return isDebug;
+    }
+
+    public List<Long> getDelayTimes() {
+        return delayTimes;
+    }
+
+    public int getMaxRetries() {
+        return maxRetries;
+    }
+
+    public long getSmtpTimeout() {
+        return smtpTimeout;
+    }
+
+    public boolean isSendPartial() {
+        return sendPartial;
+    }
+
+    public int getConnectionTimeout() {
+        return connectionTimeout;
+    }
+
+    public int getWorkersThreadCount() {
+        return workersThreadCount;
+    }
+
+    public Collection<String> getGatewayServer() {
+        return gatewayServer;
+    }
+
+    public String getAuthUser() {
+        return authUser;
+    }
+
+    public String getAuthPass() {
+        return authPass;
+    }
+
+    public boolean isBindUsed() {
+        return isBindUsed;
+    }
+
+    public String getBounceProcessor() {
+        return bounceProcessor;
+    }
+
+    public boolean isUsePriority() {
+        return usePriority;
+    }
+
+    public boolean isStartTLS() {
+        return startTLS;
+    }
+
+    public boolean isSSLEnable() {
+        return isSSLEnable;
+    }
+
+    public HeloNameProvider getHeloNameProvider() {
+        return heloNameProvider;
+    }
+
+    public String getOutGoingQueueName() {
+        return outGoingQueueName;
+    }
+
+    public Properties getJavaxAdditionalProperties() {
+        return javaxAdditionalProperties;
+    }
+
+    public int getDnsProblemRetry() {
+        return dnsProblemRetry;
+    }
+
+    public String getBindAddress() {
+        return bindAddress;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
index 0252214..19682b1 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Repeat.java
@@ -28,7 +28,7 @@ import com.google.common.collect.Iterables;
 public class Repeat {
 
     public static <T> List<T> repeat(T element, int times) {
-        Preconditions.checkArgument(times > 0, "Times argument should be strictly positive");
+        Preconditions.checkArgument(times >= 0, "Times argument should be strictly positive");
         return ImmutableList.copyOf(
             Iterables.limit(
                 Iterables.cycle(element), times));

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfigurationTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfigurationTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfigurationTest.java
new file mode 100644
index 0000000..ec4e23f
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfigurationTest.java
@@ -0,0 +1,911 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Properties;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.mailet.base.test.FakeMailetConfig;
+import org.assertj.core.data.MapEntry;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class RemoteDeliveryConfigurationTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void isDebugShouldBeFalseByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isDebug()).isFalse();
+    }
+
+    @Test
+    public void isDebugShouldBeTrueIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.DEBUG, "true")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isDebug()).isTrue();
+    }
+
+    @Test
+    public void isDebugShouldBeFalseIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.DEBUG, "false")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isDebug()).isFalse();
+    }
+
+    @Test
+    public void isDebugShouldBeFalseIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.DEBUG, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isDebug()).isFalse();
+    }
+
+    @Test
+    public void getSmtpTimeoutShouldReturnDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getSmtpTimeout())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_SMTP_TIMEOUT);
+    }
+
+    @Test
+    public void getSmtpTimeoutShouldReturnProvidedValue() {
+        int value = 150000;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.TIMEOUT, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getSmtpTimeout())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void getSmtpTimeoutShouldReturnDefaultIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.TIMEOUT, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getSmtpTimeout())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_SMTP_TIMEOUT);
+    }
+
+    @Test
+    public void getSmtpTimeoutShouldReturnProvidedValueWhenZero() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.TIMEOUT, "0")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getSmtpTimeout())
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void getSmtpTimeoutShouldReturnProvidedValueWhenNegativeNumber() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.TIMEOUT, "-1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getSmtpTimeout())
+            .isEqualTo(-1);
+    }
+
+    @Test
+    public void getOutGoingQueueNameShouldReturnDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getOutGoingQueueName())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_OUTGOING_QUEUE_NAME);
+    }
+
+    @Test
+    public void getOutGoingQueueNameShouldReturnProvidedValue() {
+        String value = "value";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.OUTGOING, value)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getOutGoingQueueName())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void getConnectionTimeoutShouldReturnDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getConnectionTimeout())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_CONNECTION_TIMEOUT);
+    }
+
+    @Test
+    public void getConnectionTimeoutShouldReturnProvidedValue() {
+        int value = 150000;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getConnectionTimeout())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void getConnectionTimeoutShouldReturnDefaultIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getConnectionTimeout())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_CONNECTION_TIMEOUT);
+    }
+
+    @Test
+    public void getConnectionTimeoutShouldReturnProvidedValueWhenZero() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, "0")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getConnectionTimeout())
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void getConnectionTimeoutShouldReturnProvidedValueWhenNegativeNumber() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, "-1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getConnectionTimeout())
+            .isEqualTo(-1);
+    }
+
+    @Test
+    public void isSendPartialShouldBeFalseByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSendPartial()).isFalse();
+    }
+
+    @Test
+    public void isSendPartialShouldBeTrueIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "true")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSendPartial()).isTrue();
+    }
+
+    @Test
+    public void isSendPartialShouldBeFalseIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "false")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSendPartial()).isFalse();
+    }
+
+    @Test
+    public void isSendPartialShouldBeFalseIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSendPartial()).isFalse();
+    }
+
+    @Test
+    public void getBounceProcessorShouldReturnNullByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getBounceProcessor())
+            .isNull();
+    }
+
+    @Test
+    public void getBounceProcessorShouldReturnProvidedValue() {
+        String value = "value";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, value)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getBounceProcessor())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void isStartTLSShouldBeFalseByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isStartTLS()).isFalse();
+    }
+
+    @Test
+    public void isStartTLSShouldBeTrueIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.START_TLS, "true")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isStartTLS()).isTrue();
+    }
+
+    @Test
+    public void isStartTLSShouldBeFalseIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.START_TLS, "false")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isStartTLS()).isFalse();
+    }
+
+    @Test
+    public void isStartTLSShouldBeFalseIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.START_TLS, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isStartTLS()).isFalse();
+    }
+
+    @Test
+    public void isSSLEnableShouldBeFalseByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSSLEnable()).isFalse();
+    }
+
+    @Test
+    public void isSSLEnableShouldBeTrueIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SSL_ENABLE, "true")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSSLEnable()).isTrue();
+    }
+
+    @Test
+    public void isSSLEnableShouldBeFalseIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SSL_ENABLE, "false")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSSLEnable()).isFalse();
+    }
+
+    @Test
+    public void isSSLEnableShouldBeFalseIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SSL_ENABLE, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSSLEnable()).isFalse();
+    }
+
+    @Test
+    public void isBindUsedShouldBeFalseByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.BIND, "127.0.0.1:25")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isBindUsed()).isTrue();
+    }
+
+    @Test
+    public void getBindAddressShouldBeNullByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getBindAddress()).isNull();
+    }
+
+    @Test
+    public void getBindAddressShouldReturnProvidedValue() {
+        String value = "127.0.0.1:25";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.BIND, value)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getBindAddress()).isEqualTo(value);
+    }
+
+    @Test
+    public void getDnsProblemRetryShouldReturnDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDnsProblemRetry())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_DNS_RETRY_PROBLEM);
+    }
+
+    @Test
+    public void getDnsProblemRetryShouldReturnProvidedValue() {
+        int value = 4;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDnsProblemRetry())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void constructorShouldThrowOnInvalidDnsRetries() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "invalid")
+            .build();
+
+        expectedException.expect(NumberFormatException.class);
+
+        new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class));
+    }
+
+    @Test
+    public void getDnsProblemRetryShouldReturnProvidedValueWhenZero() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "0")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDnsProblemRetry())
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void getDnsProblemRetryShouldReturnProvidedValueWhenEmpty() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDnsProblemRetry())
+            .isEqualTo(0);
+    }
+
+    @Test
+    public void getDnsProblemRetryShouldReturnProvidedValueWhenNegativeNumber() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "-1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDnsProblemRetry())
+            .isEqualTo(-1);
+    }
+
+    @Test
+    public void constructorShouldThrowOnNonSpecifiedThreadCount() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .build();
+
+        expectedException.expect(NumberFormatException.class);
+
+        new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getWorkersThreadCount();
+    }
+
+    @Test
+    public void getWorkersThreadCountShouldReturnProvidedValue() {
+        int value = 36;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getWorkersThreadCount())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void constructorShouldThrowOnInvalidThreadCount() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "invalid")
+            .build();
+
+        expectedException.expect(NumberFormatException.class);
+
+        new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getWorkersThreadCount();
+    }
+
+    @Test
+    public void isUsePriorityShouldBeFalseByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isUsePriority()).isFalse();
+    }
+
+    @Test
+    public void isUsePriorityShouldBeTrueIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.USE_PRIORITY, "true")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isUsePriority()).isTrue();
+    }
+
+    @Test
+    public void isUsePriorityShouldBeFalseIfSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.USE_PRIORITY, "false")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isUsePriority()).isFalse();
+    }
+
+    @Test
+    public void isUsePriorityShouldBeFalseIfParsingException() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.USE_PRIORITY, "invalid")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isUsePriority()).isFalse();
+    }
+
+    @Test
+    public void getHeloNameProviderShouldCallDomainListByDefault() throws Exception {
+        DomainList domainList = mock(DomainList.class);
+        String value = "value";
+        when(domainList.getDefaultDomain()).thenReturn(value);
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, domainList).getHeloNameProvider().getHeloName())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void getHeloNameProviderShouldTakeCareOfProvidedValue() {
+        String value = "value";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.HELO_NAME, value)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getHeloNameProvider().getHeloName())
+            .isEqualTo(value);
+    }
+
+    @Test
+    public void getJavaxAdditionalPropertiesShouldBeEmptyByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getJavaxAdditionalProperties())
+            .isEmpty();
+    }
+
+    @Test
+    public void getJavaxAdditionalPropertiesShouldTakeOneEntryIntoAccount() {
+        String key1 = RemoteDeliveryConfiguration.JAVAX_PREFIX + "property1";
+        String value1 = "value1";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(key1, value1)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getJavaxAdditionalProperties())
+            .containsOnly(MapEntry.entry(key1, value1));
+    }
+
+    @Test
+    public void getJavaxAdditionalPropertiesShouldTakeTwoEntriesIntoAccount() {
+        String key1 = RemoteDeliveryConfiguration.JAVAX_PREFIX + "property1";
+        String value1 = "value1";
+        String key2 = RemoteDeliveryConfiguration.JAVAX_PREFIX + "property2";
+        String value2 = "value2";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(key1, value1)
+            .setProperty(key2, value2)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getJavaxAdditionalProperties())
+            .containsOnly(MapEntry.entry(key1, value1), MapEntry.entry(key2, value2));
+    }
+
+    @Test
+    public void constructorShouldThrowOnNullValueJavaxProperty() {
+        expectedException.expect(NullPointerException.class);
+
+        String key1 = RemoteDeliveryConfiguration.JAVAX_PREFIX + "property1";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(key1, null)
+            .build();
+
+        new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class));
+    }
+
+    @Test
+    public void getJavaxAdditionalPropertiesShouldTakeOneEmptyEntryIntoAccount() {
+        String key1 = RemoteDeliveryConfiguration.JAVAX_PREFIX + "property1";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(key1, "")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getJavaxAdditionalProperties())
+            .containsOnly(MapEntry.entry(key1, ""));
+    }
+
+    @Test
+    public void getGatewayServerShouldBeNullByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getGatewayServer()).isEmpty();
+    }
+
+    @Test
+    public void getGatewayServerShouldReturnProvidedValue() {
+        String value = "127.0.0.1";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, value)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getGatewayServer())
+            .containsOnly(value);
+    }
+
+    @Test
+    public void getGatewayServerShouldReturnProvidedValues() {
+        String value1 = "127.0.0.1";
+        String value2 = "domain";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, value1 + ',' + value2)
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getGatewayServer())
+            .containsOnly(value1, value2);
+    }
+
+    @Test
+    public void getAuthUserShouldBeNullByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isNull();
+    }
+
+    @Test
+    public void getAuthUserShouldBeNullWhenGatewayIsNotSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME, "name")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isNull();
+    }
+
+    @Test
+    public void getAuthUserShouldReturnSpecifiedValueWhenGatewaySpecified() {
+        String value = "name";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isEqualTo(value);
+    }
+
+    @Test
+    public void getAuthUserShouldReturnSpecifiedEmptyValueWhenGatewaySpecified() {
+        String value = "";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isEqualTo(value);
+    }
+
+    @Test
+    public void getAuthUserShouldReturnSpecifiedCompatibilityValueWhenGatewaySpecified() {
+        String value = "name";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME_COMPATIBILITY, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isEqualTo(value);
+    }
+
+    @Test
+    public void getAuthUserShouldReturnSpecifiedEmptyCompatibilityValueWhenGatewaySpecified() {
+        String value = "";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME_COMPATIBILITY, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isEqualTo(value);
+    }
+
+    @Test
+    public void getAuthUserShouldReturnSpecifiedValueWhenValueAndCompatibilitySpecified() {
+        String value = "name";
+        String compatibilityValue = "compatibilityValue";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME_COMPATIBILITY, compatibilityValue)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthUser()).isEqualTo(value);
+    }
+
+    @Test
+    public void getAuthPassShouldBeNullByDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthPass()).isNull();
+    }
+
+    @Test
+    public void getAuthPassShouldBeNullWhenGatewayIsNotSpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_PASSWORD, "name")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthPass()).isNull();
+    }
+
+    @Test
+    public void getAuthPassShouldReturnSpecifiedValueWhenGatewaySpecified() {
+        String value = "name";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_PASSWORD, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthPass()).isEqualTo(value);
+    }
+
+    @Test
+    public void getAuthPassShouldReturnSpecifiedEmptyValueWhenGatewaySpecified() {
+        String value = "";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_PASSWORD, value)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "127.0.0.1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getAuthPass()).isEqualTo(value);
+    }
+
+    @Test
+    public void getMaxRetriesShouldReturnProvidedValue() {
+        int value = 36;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_RETRIES, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getMaxRetries()).isEqualTo(value);
+    }
+
+    @Test
+    public void getMaxRetriesShouldReturnOneWhenZero() {
+        int value = 0;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_RETRIES, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getMaxRetries()).isEqualTo(1);
+    }
+
+    @Test
+    public void getMaxRetriesShouldReturnOneWhenNegativeNumber() {
+        int value = -1;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_RETRIES, String.valueOf(value))
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getMaxRetries()).isEqualTo(1);
+    }
+
+    @Test
+    public void getMaxRetriesShouldReturnDefaultWhenNoySpecified() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getMaxRetries())
+            .isEqualTo(RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY);
+    }
+
+    @Test
+    public void getDelayTimesShouldReturnDefault() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDelayTimes())
+            .containsOnly(Delay.DEFAULT_DELAY_TIME, Delay.DEFAULT_DELAY_TIME, Delay.DEFAULT_DELAY_TIME, Delay.DEFAULT_DELAY_TIME, Delay.DEFAULT_DELAY_TIME);
+    }
+
+    @Test
+    public void getDelayTimesShouldWorkWithDefaultConfiguration() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.DELAY_TIME, "5000, 100000, 500000")
+            .build();
+
+        assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).getDelayTimes())
+            .containsOnly(5000L, 100000L, 500000L);
+    }
+
+    @Test
+    public void createFinalJavaxPropertiesShouldProvidePropertiesWithMinimalConfiguration() {
+        String helo = "domain.com";
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.HELO_NAME, helo)
+            .build();
+
+        Properties properties = new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).createFinalJavaxProperties();
+
+
+        assertThat(properties)
+            .containsOnly(MapEntry.entry("mail.smtp.ssl.enable", "false"),
+                MapEntry.entry("mail.smtp.sendpartial", "false"),
+                MapEntry.entry("mail.smtp.allow8bitmime", "true"),
+                MapEntry.entry("mail.smtp.ehlo", "true"),
+                MapEntry.entry("mail.smtp.connectiontimeout", "60000"),
+                MapEntry.entry("mail.smtp.localhost", helo),
+                MapEntry.entry("mail.smtp.timeout", "180000"),
+                MapEntry.entry("mail.debug", "false"),
+                MapEntry.entry("mail.smtp.starttls.enable", "false"));
+    }
+
+    @Test
+    public void createFinalJavaxPropertiesShouldProvidePropertiesWithFullConfigurationWithoutGateway() {
+        String helo = "domain.com";
+        int connectionTimeout = 1856;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SSL_ENABLE, "true")
+            .setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "true")
+            .setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, String.valueOf(connectionTimeout))
+            .setProperty(RemoteDeliveryConfiguration.START_TLS, "true")
+            .setProperty(RemoteDeliveryConfiguration.HELO_NAME, helo)
+            .build();
+
+        Properties properties = new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).createFinalJavaxProperties();
+
+
+        assertThat(properties)
+            .containsOnly(MapEntry.entry("mail.smtp.ssl.enable", "true"),
+                MapEntry.entry("mail.smtp.sendpartial", "true"),
+                MapEntry.entry("mail.smtp.allow8bitmime", "true"),
+                MapEntry.entry("mail.smtp.ehlo", "true"),
+                MapEntry.entry("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout)),
+                MapEntry.entry("mail.smtp.localhost", helo),
+                MapEntry.entry("mail.smtp.timeout", "180000"),
+                MapEntry.entry("mail.debug", "false"),
+                MapEntry.entry("mail.smtp.starttls.enable", "true"));
+    }
+
+    @Test
+    public void createFinalJavaxPropertiesShouldProvidePropertiesWithFullConfigurationWithGateway() {
+        String helo = "domain.com";
+        int connectionTimeout = 1856;
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.SSL_ENABLE, "true")
+            .setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "true")
+            .setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, String.valueOf(connectionTimeout))
+            .setProperty(RemoteDeliveryConfiguration.START_TLS, "true")
+            .setProperty(RemoteDeliveryConfiguration.HELO_NAME, helo)
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY, "gateway.domain.com")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME, "user")
+            .setProperty(RemoteDeliveryConfiguration.GATEWAY_PASSWORD, "password")
+            .build();
+
+        Properties properties = new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).createFinalJavaxProperties();
+
+
+        assertThat(properties)
+            .containsOnly(MapEntry.entry("mail.smtp.ssl.enable", "true"),
+                MapEntry.entry("mail.smtp.sendpartial", "true"),
+                MapEntry.entry("mail.smtp.allow8bitmime", "true"),
+                MapEntry.entry("mail.smtp.ehlo", "true"),
+                MapEntry.entry("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout)),
+                MapEntry.entry("mail.smtp.localhost", helo),
+                MapEntry.entry("mail.smtp.timeout", "180000"),
+                MapEntry.entry("mail.debug", "false"),
+                MapEntry.entry("mail.smtp.starttls.enable", "true"),
+                MapEntry.entry("mail.smtp.auth", "true"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/6fa52984/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
index 13b9968..4ea1640 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RepeatTest.java
@@ -39,10 +39,8 @@ public class RepeatTest {
     }
 
     @Test
-    public void repeatShouldThrowOnZeroTimes() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        Repeat.repeat(new Object(), 0);
+    public void repeatShouldReturnEmptyListOnZeroTimes() {
+        assertThat(Repeat.repeat(new Object(), 0)).isEmpty();
     }
 
     @Test


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


[35/50] [abbrv] james-project git commit: JAMES-1877 Extract mailAddress conversion

Posted by ro...@apache.org.
JAMES-1877 Extract mailAddress conversion


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

Branch: refs/heads/master
Commit: 7f8cf9e9f5c4f227759da77b82520df3e510fb9f
Parents: fc1b1d3
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 10:47:24 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:52 2017 +0700

----------------------------------------------------------------------
 .../InternetAddressConverter.java               | 44 ++++++++++++++
 .../mailets/remoteDelivery/MailDelivrer.java    | 12 +---
 .../InternetAddressConverterTest.java           | 62 ++++++++++++++++++++
 3 files changed, 107 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7f8cf9e9/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java
new file mode 100644
index 0000000..52bd2a6
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverter.java
@@ -0,0 +1,44 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.Collection;
+
+import javax.mail.internet.InternetAddress;
+
+import org.apache.mailet.MailAddress;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+
+public class InternetAddressConverter {
+
+    public static InternetAddress[] convert(Collection<MailAddress> recipients) {
+        Preconditions.checkNotNull(recipients);
+        return FluentIterable.from(recipients).transform(new Function<MailAddress, InternetAddress>() {
+            @Override
+            public InternetAddress apply(MailAddress input) {
+                return input.toInternetAddress();
+            }
+        }).toArray(InternetAddress.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7f8cf9e9/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index bcfe330..941ef21 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -127,7 +127,7 @@ public class MailDelivrer {
             targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
         }
 
-        return doDeliver(mail, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
+        return doDeliver(mail, mail.getMessage(), InternetAddressConverter.convert(mail.getRecipients()), targetServers);
     }
 
     @SuppressWarnings("deprecation")
@@ -312,16 +312,6 @@ public class MailDelivrer {
         }
     }
 
-    private InternetAddress[] convertToInetAddr(Collection<MailAddress> recipients) {
-        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();
-        }
-        return addr;
-    }
-
     private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
         ExecutionResult executionResult = ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
             "up mail server for host: " + host + ".  I cannot determine where to send this message."));

http://git-wip-us.apache.org/repos/asf/james-project/blob/7f8cf9e9/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverterTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverterTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverterTest.java
new file mode 100644
index 0000000..9020533
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/InternetAddressConverterTest.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.mail.internet.InternetAddress;
+
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.base.MailAddressFixture;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.google.common.collect.ImmutableList;
+
+public class InternetAddressConverterTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void convertShouldWorkWithEmptyAddressList() {
+        assertThat(InternetAddressConverter.convert(ImmutableList.<MailAddress>of())).isEmpty();
+    }
+
+    @Test
+    public void convertShouldThrowOnNullAddress() {
+        expectedException.expect(NullPointerException.class);
+
+        InternetAddressConverter.convert(null);
+    }
+
+    @Test
+    public void convertShouldWorkWithOneAddress() throws Exception {
+        assertThat(InternetAddressConverter.convert(ImmutableList.of(MailAddressFixture.ANY_AT_JAMES)))
+            .containsOnly(new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString()));
+    }
+
+    @Test
+    public void convertShouldWorkWithTwoAddress() throws Exception {
+        assertThat(InternetAddressConverter.convert(ImmutableList.of(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES)))
+            .containsOnly(new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString()), new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString()));
+    }
+}


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


[10/50] [abbrv] james-project git commit: JAMES-1877 Creating javax properties is a responsibility of RemoteDeliveryConfiguration

Posted by ro...@apache.org.
JAMES-1877 Creating javax properties is a responsibility of RemoteDeliveryConfiguration


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

Branch: refs/heads/master
Commit: 69f66c7a6282c9b904ac5c1a0dc120b4e73a04f4
Parents: 6fa5298
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Nov 30 17:28:42 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:04:32 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 48 +-------------------
 .../RemoteDeliveryConfiguration.java            | 29 ++++++++++++
 2 files changed, 30 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/69f66c7a/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 0067607..c4c18d2 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
@@ -318,53 +318,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      */
     @Override
     public void run() {
-
-        // Checks the pool and delivers a mail message
-        Properties props = new Properties();
-        // Not needed for production environment
-        props.put("mail.debug", "false");
-        // Reactivated: javamail 1.3.2 should no more have problems with
-        // "250 OK"
-        // messages (WAS "false": Prevents problems encountered with 250 OK
-        // Messages)
-        props.put("mail.smtp.ehlo", "true");
-        // By setting this property to true the transport is allowed to
-        // send 8 bit data to the server (if it supports the 8bitmime
-        // extension).
-        // 2006/03/01 reverted to false because of a javamail bug converting to
-        // 8bit
-        // messages created by an inputstream.
-        props.setProperty("mail.smtp.allow8bitmime", "true");
-        // Sets timeout on going connections
-        props.put("mail.smtp.timeout", configuration.getSmtpTimeout() + "");
-
-        props.put("mail.smtp.connectiontimeout", configuration.getConnectionTimeout() + "");
-        props.put("mail.smtp.sendpartial", String.valueOf(configuration.isSendPartial()));
-
-        props.put("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName());
-
-        // handle starttls
-        props.put("mail.smtp.starttls.enable", String.valueOf(configuration.isStartTLS()));
-
-        // handle SSLEnable
-        props.put("mail.smtp.ssl.enable", String.valueOf(configuration.isSSLEnable()));
-
-        if (configuration.isBindUsed()) {
-            // undocumented JavaMail 1.2 feature, smtp transport will use
-            // our socket factory, which will also set the local address
-            props.put("mail.smtp.socketFactory.class", RemoteDeliverySocketFactory.class.getClass());
-            // Don't fallback to the standard socket factory on error, do throw
-            // an exception
-            props.put("mail.smtp.socketFactory.fallback", "false");
-        }
-
-        if (configuration.getAuthUser() != null) {
-            props.put("mail.smtp.auth", "true");
-        }
-
-        props.putAll(configuration.getJavaxAdditionalProperties());
-
-        final Session session = obtainSession(props);
+        final Session session = obtainSession(configuration.createFinalJavaxProperties());
         try {
             while (!Thread.interrupted() && !destroyed) {
                 try {

http://git-wip-us.apache.org/repos/asf/james-project/blob/69f66c7a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
index 621f3fe..3bfde9e 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
@@ -206,6 +206,35 @@ public class RemoteDeliveryConfiguration {
         return address;
     }
 
+    public Properties createFinalJavaxProperties() {
+        Properties props = new Properties();
+        props.put("mail.debug", "false");
+        // Reactivated: javamail 1.3.2 should no more have problems with "250 OK" messages
+        // (WAS "false": Prevents problems encountered with 250 OK Messages)
+        props.put("mail.smtp.ehlo", "true");
+        // By setting this property to true the transport is allowed to send 8 bit data to the server (if it supports
+        // the 8bitmime extension).
+        props.setProperty("mail.smtp.allow8bitmime", "true");
+        props.put("mail.smtp.timeout", String.valueOf(smtpTimeout));
+        props.put("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout));
+        props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
+        props.put("mail.smtp.localhost", heloNameProvider.getHeloName());
+        props.put("mail.smtp.starttls.enable", String.valueOf(startTLS));
+        props.put("mail.smtp.ssl.enable", String.valueOf(isSSLEnable));
+        if (isBindUsed()) {
+            // undocumented JavaMail 1.2 feature, smtp transport will use
+            // our socket factory, which will also set the local address
+            props.put("mail.smtp.socketFactory.class", RemoteDeliverySocketFactory.class.getClass());
+            // Don't fallback to the standard socket factory on error, do throw an exception
+            props.put("mail.smtp.socketFactory.fallback", "false");
+        }
+        if (authUser != null) {
+            props.put("mail.smtp.auth", "true");
+        }
+        props.putAll(javaxAdditionalProperties);
+        return props;
+    }
+
     public boolean isDebug() {
         return isDebug;
     }


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


[45/50] [abbrv] james-project git commit: JAMES-1877 Tests MX iteration behavior upon deliver

Posted by ro...@apache.org.
JAMES-1877 Tests MX iteration behavior upon deliver


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

Branch: refs/heads/master
Commit: c06d312a4facc24e281934706f7439ae20ef29aa
Parents: 97e23ae
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 11:43:35 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:27 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/MailDelivrer.java    |  91 +++------
 .../remoteDelivery/MailDelivrerToHost.java      |  74 ++++---
 .../remoteDelivery/MailDelivrerTest.java        | 201 +++++++++++++++++--
 3 files changed, 245 insertions(+), 121 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c06d312a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index f42a0fc..1036734 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -37,6 +37,8 @@ import org.apache.mailet.MailAddress;
 import org.slf4j.Logger;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 
 public class MailDelivrer {
 
@@ -62,15 +64,13 @@ public class MailDelivrer {
     }
 
     /**
-     * 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.
+     * 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
+     * @return boolean Whether the delivery was successful and the message can be deleted
      */
     public ExecutionResult deliver(Mail mail) {
         try {
@@ -78,25 +78,13 @@ public class MailDelivrer {
         } catch (SendFailedException sfe) {
             return handleSenderFailedException(mail, sfe);
         } 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
-
-            boolean isPermanent = '5' == ex.getMessage().charAt(0);
-            ExecutionResult executionResult = ExecutionResult.onFailure(isPermanent, ex);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
+            boolean isPermanent = new EnhancedMessagingException(ex).isServerError();
+            return logAndReturn(mail, ExecutionResult.onFailure(isPermanent, ex));
         } catch (Exception ex) {
-            logger.error("Generic exception = permanent failure: " + ex.getMessage(), ex);
-            // Generic exception = permanent failure
-            ExecutionResult executionResult = ExecutionResult.permanentFailure(ex);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
+            logger.error("Generic exception = permanent failure: {}", ex.getMessage(), ex);
+            return logAndReturn(mail, ExecutionResult.permanentFailure(ex));
         }
     }
 
@@ -107,7 +95,7 @@ public class MailDelivrer {
             return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
         }
         if (configuration.isDebug()) {
-            logger.debug("Attempting to deliver " + mail.getName());
+            logger.debug("Attempting to deliver {}", mail.getName());
         }
 
         String host = retrieveTargetHostname(mail);
@@ -126,7 +114,8 @@ public class MailDelivrer {
     }
 
     private String retrieveTargetHostname(Mail mail) {
-        MailAddress rcpt = mail.getRecipients().iterator().next();
+        Preconditions.checkArgument(!mail.getRecipients().isEmpty(), "Mail should have recipients to attempt delivery");
+        MailAddress rcpt = Iterables.getFirst(mail.getRecipients(), null);
         return rcpt.getDomain();
     }
 
@@ -136,9 +125,7 @@ public class MailDelivrer {
 
         while (targetServers.hasNext()) {
             try {
-                if (mailDelivrerToHost.tryDeliveryToHost(mail, addr, targetServers.next())) {
-                    return ExecutionResult.success();
-                }
+                return mailDelivrerToHost.tryDeliveryToHost(mail, addr, targetServers.next());
             } catch (SendFailedException sfe) {
                 lastError = handleSendFailExceptionOnMxIteration(mail, sfe);
             } catch (MessagingException me) {
@@ -163,21 +150,16 @@ public class MailDelivrer {
     }
 
     private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
-        logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
+        logger.debug("Exception delivering message ({}) - {}", mail.getName(), me.getMessage());
         if ((me.getNextException() != null) && (me.getNextException() instanceof 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.
             return me;
         } else {
-            // 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.
+            // 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;
         }
     }
@@ -189,12 +171,12 @@ public class MailDelivrer {
         List<MailAddress> invalidAddresses = SFEHelper.getAddressesAsMailAddress(sfe.getInvalidAddresses(), logger);
         List<MailAddress> validUnsentAddresses = SFEHelper.getAddressesAsMailAddress(sfe.getValidUnsentAddresses(), logger);
         if (configuration.isDebug()) {
-            logger.debug("Mail " + mail.getName() + " has initially recipients: " + mail.getRecipients());
+            logger.debug("Mail {} has initially recipients: {}", mail.getName(), mail.getRecipients());
             if (!invalidAddresses.isEmpty()) {
-                logger.debug("Invalid recipients: " + invalidAddresses);
+                logger.debug("Invalid recipients: {}", invalidAddresses);
             }
             if (!validUnsentAddresses.isEmpty()) {
-                logger.debug("Unsent recipients: " + validUnsentAddresses);
+                logger.debug("Unsent recipients: {}", validUnsentAddresses);
             }
         }
         if (!validUnsentAddresses.isEmpty()) {
@@ -234,14 +216,10 @@ public class MailDelivrer {
         if (sfe.getValidSentAddresses() != null) {
             Address[] validSent = sfe.getValidSentAddresses();
             if (validSent.length > 0) {
-                logger.debug( "Mail (" + mail.getName() + ") sent successfully for " + Arrays.asList(validSent));
+                logger.debug( "Mail ({}) sent successfully for {}", mail.getName(), Arrays.asList(validSent));
             }
         }
 
-        /*
-        * SMTPSendFailedException introduced in JavaMail 1.3.2, and
-        * provides detailed protocol reply code for the operation
-        */
         EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
         if (enhancedMessagingException.isServerError()) {
             throw sfe;
@@ -249,7 +227,7 @@ public class MailDelivrer {
 
         if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
             if (configuration.isDebug())
-                logger.debug("Send failed, " + sfe.getValidUnsentAddresses().length + " valid addresses remain, continuing with any other servers");
+                logger.debug("Send failed, {} valid addresses remain, continuing with any other servers", sfe.getValidUnsentAddresses().length);
             return sfe;
         } else {
             // There are no valid addresses left to send, so rethrow
@@ -258,10 +236,9 @@ public class MailDelivrer {
     }
 
     private ExecutionResult handleNoTargetServer(Mail mail, String host) {
-        logger.info("No mail server found for: " + host);
+        logger.info("No mail server found for: {}", host);
         MessagingException messagingException = new MessagingException("There are no DNS entries for the hostname " + host + ".  I cannot determine where to send this message.");
         int retry = DeliveryRetriesHelper.retrieveRetries(mail);
-        System.out.println("retyry " + retry);
         if (retry >= configuration.getDnsProblemRetry()) {
             return logAndReturn(mail, ExecutionResult.permanentFailure(messagingException));
         } else {
@@ -273,13 +250,10 @@ public class MailDelivrer {
         if (configuration.isDebug()) {
             EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
             if (enhancedMessagingException.hasReturnCode()) {
-                logger.debug("SMTP SEND FAILED:");
-                logger.debug(sfe.toString());
-                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
-                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
-                logger.debug("  Response: " + sfe.getMessage());
+                logger.debug("SMTP SEND FAILED: Command [{}] RetCode: [{}] Response[{}]", enhancedMessagingException.computeCommand(),
+                    enhancedMessagingException.getReturnCode(), sfe.getMessage());
             } else {
-                logger.debug("Send failed: " + sfe.toString());
+                logger.debug("Send failed: {}", sfe.toString());
             }
             logLevels(sfe);
         }
@@ -291,12 +265,9 @@ public class MailDelivrer {
             me = (MessagingException) ne;
             EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me);
             if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
-                logger.debug("ADDRESS " + enhancedMessagingException.computeAction() + ":");
-                logger.debug(me.toString());
-                logger.debug("  Address: " + enhancedMessagingException.computeAddress());
-                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
-                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
-                logger.debug("  Response: " + me.getMessage());
+                logger.debug("ADDRESS :[{}] Address:[{}] Command : [{}] RetCode[{}] Response [{}]",
+                    enhancedMessagingException.computeAction(), me.toString(), enhancedMessagingException.computeAddress(),
+                    enhancedMessagingException.computeCommand(), enhancedMessagingException.getReturnCode());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c06d312a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
index a9f5758..a6ffbad 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
@@ -50,24 +50,16 @@ public class MailDelivrerToHost {
         this.logger = logger;
     }
 
-    public boolean tryDeliveryToHost(Mail mail, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
-        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);
-        }
-        logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
-            + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
+    public ExecutionResult tryDeliveryToHost(Mail mail, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
+        Properties props = getPropertiesForMail(mail);
+        logger.debug("Attempting delivery of {} to host {} at {} from {}",
+            mail.getName(), outgoingMailServer.getHostName(), outgoingMailServer.getHost(), props.get("mail.smtp.from"));
 
         // 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.
+        // "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 {
@@ -75,12 +67,23 @@ public class MailDelivrerToHost {
             transport.setLocalHost( props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName()) );
             connect(outgoingMailServer, transport);
             transport.sendMessage(adaptToTransport(mail.getMessage(), transport), addr);
-            logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
-                " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
-            return true;
+            logger.debug("Mail ({})  sent successfully to {} at {} from {} for {}", mail.getName(), outgoingMailServer.getHostName(),
+                outgoingMailServer.getHost(), props.get("mail.smtp.from"), mail.getRecipients());
         } finally {
             closeTransport(mail, outgoingMailServer, transport);
         }
+        return ExecutionResult.success();
+    }
+
+    private Properties getPropertiesForMail(Mail mail) {
+        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);
+        }
+        return props;
     }
 
     private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException {
@@ -92,27 +95,7 @@ public class MailDelivrerToHost {
     }
 
     private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException {
-        // if the transport is a SMTPTransport (from sun) some
-        // performance enhancement can be done.
-        if (transport.getClass().getName().endsWith(".SMTPTransport")) {
-            // 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 (!transport.supportsExtension(BIT_MIME_8)) {
-                try {
-                    converter7Bit.convertTo7Bit(message);
-                } catch (IOException e) {
-                    // An error has occured during the 7bit conversion.
-                    // The error is logged and the message is sent anyway.
-
-                    logger.error("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.
+        if (shouldAdapt(transport)) {
             try {
                 converter7Bit.convertTo7Bit(message);
             } catch (IOException e) {
@@ -122,6 +105,16 @@ public class MailDelivrerToHost {
         return message;
     }
 
+    private boolean shouldAdapt(SMTPTransport transport) {
+        // If the transport is a SMTPTransport (from sun) some performance enhancement can be done.
+        // 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.
+        return !transport.getClass().getName().endsWith(".SMTPTransport")
+            || !transport.supportsExtension(BIT_MIME_8);
+        // if the message is already 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.
+    }
+
     private void closeTransport(Mail mail, HostAddress outgoingMailServer, SMTPTransport transport) {
         if (transport != null) {
             try {
@@ -131,8 +124,9 @@ public class MailDelivrerToHost {
                 // of the mail transaction (MAIL, RCPT, DATA).
                 transport.close();
             } catch (MessagingException e) {
-                logger.error("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());
+                logger.error("Warning: could not close the SMTP transport after sending mail ({}) to {} at {} for {}; " +
+                        "probably the server has already closed the connection. Message is considered to be delivered. Exception: {}",
+                    mail.getName(), outgoingMailServer.getHostName(), outgoingMailServer.getHost(), mail.getRecipients(), e.getMessage());
             }
             transport = null;
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c06d312a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
index 3f7b726..b77fcb4 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
@@ -20,12 +20,18 @@
 package org.apache.james.transport.mailets.remoteDelivery;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import java.io.IOException;
+
 import javax.mail.Address;
+import javax.mail.MessagingException;
 import javax.mail.SendFailedException;
 import javax.mail.internet.InternetAddress;
 
@@ -47,16 +53,32 @@ import com.sun.mail.smtp.SMTPSenderFailedException;
 
 public class MailDelivrerTest {
     private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrerTest.class);
+    public static final String MX1_HOSTNAME = "mx1." + MailAddressFixture.JAMES2_APACHE_ORG;
+    public static final String MX2_HOSTNAME = "mx2." + MailAddressFixture.JAMES2_APACHE_ORG;
+    public static final String SMTP_URI2 = "protocol://userid:password@host:119/file1";
+    public static final String SMTP_URI1 = "protocol://userid:password@host:119/file2";
+    @SuppressWarnings("deprecation")
+    public static final HostAddress HOST_ADDRESS_1 = new HostAddress(MX1_HOSTNAME, SMTP_URI1);
+    @SuppressWarnings("deprecation")
+    public static final HostAddress HOST_ADDRESS_2 = new HostAddress(MX2_HOSTNAME, SMTP_URI2);
 
     private MailDelivrer testee;
     private Bouncer bouncer;
     private DnsHelper dnsHelper;
+    private MailDelivrerToHost mailDelivrerToHost;
 
     @Before
     public void setUp() {
         bouncer = mock(Bouncer.class);
         dnsHelper = mock(DnsHelper.class);
-        testee = new MailDelivrer(mock(RemoteDeliveryConfiguration.class), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
+        mailDelivrerToHost = mock(MailDelivrerToHost.class);
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
+            .setProperty(RemoteDeliveryConfiguration.DEBUG, "true")
+            .build(),
+            mock(DomainList.class));
+        testee = new MailDelivrer(configuration, mailDelivrerToHost, dnsHelper, bouncer, LOGGER);
     }
 
     @Test
@@ -224,11 +246,6 @@ public class MailDelivrerTest {
     @Test
     public void deliverShouldReturnTemporaryErrorWhenFirstDNSProblem() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
-        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
-            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
-            .build();
-        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
 
         UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
         when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
@@ -242,12 +259,7 @@ public class MailDelivrerTest {
     @Test
     public void deliverShouldReturnTemporaryErrorWhenToleratedDNSProblem() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
-        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
-            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
-            .build();
         DeliveryRetriesHelper.incrementRetries(mail);
-        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
 
         UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
         when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
@@ -261,14 +273,9 @@ public class MailDelivrerTest {
     @Test
     public void deliverShouldReturnPermanentErrorWhenLimitDNSProblemReached() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
-        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
-            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
-            .build();
         DeliveryRetriesHelper.incrementRetries(mail);
         DeliveryRetriesHelper.incrementRetries(mail);
         DeliveryRetriesHelper.incrementRetries(mail);
-        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
 
         UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
         when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
@@ -282,15 +289,11 @@ public class MailDelivrerTest {
     @Test
     public void deliverShouldReturnPermanentErrorWhenLimitDNSProblemExceeded() throws Exception {
         Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
-        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
-            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
-            .build();
+
         DeliveryRetriesHelper.incrementRetries(mail);
         DeliveryRetriesHelper.incrementRetries(mail);
         DeliveryRetriesHelper.incrementRetries(mail);
         DeliveryRetriesHelper.incrementRetries(mail);
-        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
 
         UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
         when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
@@ -300,4 +303,160 @@ public class MailDelivrerTest {
         assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.PERMANENT_FAILURE);
     }
 
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldWork() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class)))
+            .thenReturn(ExecutionResult.success());
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(1)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.SUCCESS);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldAbortWhenServerError() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class)))
+            .thenThrow(new MessagingException("500 : Horrible way to manage Server Return code"));
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(1)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.PERMANENT_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldAbortWithTemporaryWhenMessagingExceptionCauseUnknown() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class)))
+            .thenThrow(new MessagingException("400 : Horrible way to manage Server Return code"));
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(1)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.TEMPORARY_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldTryTwiceOnIOException() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), eq(HOST_ADDRESS_1)))
+            .thenThrow(new MessagingException("400 : Horrible way to manage Server Return code", new IOException()));
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), eq(HOST_ADDRESS_2)))
+            .thenReturn(ExecutionResult.success());
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(2)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.SUCCESS);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldAbortWhenServerErrorSFE() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class)))
+            .thenThrow(new SMTPSenderFailedException(new InternetAddress(MailAddressFixture.ANY_AT_JAMES.toString()), "command", 505, "Big failure"));
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(1)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.PERMANENT_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldAttemptDeliveryOnlyOnceIfNoMoreValidUnsent() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class)))
+            .thenThrow(new SendFailedException());
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(1)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.TEMPORARY_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldAttemptDeliveryOnBothMXIfStillRecipients() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class)))
+            .thenThrow(sfe);
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(2)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.TEMPORARY_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldWorkIfOnlyMX2Valid() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+
+        UnmodifiableIterator<HostAddress> dnsEntries = ImmutableList.of(
+            HOST_ADDRESS_1,
+            HOST_ADDRESS_2).iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(dnsEntries);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), eq(HOST_ADDRESS_1)))
+            .thenThrow(sfe);
+        when(mailDelivrerToHost.tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), eq(HOST_ADDRESS_2)))
+            .thenReturn(ExecutionResult.success());
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        verify(mailDelivrerToHost, times(2)).tryDeliveryToHost(any(Mail.class), any(InternetAddress[].class), any(HostAddress.class));
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.SUCCESS);
+    }
+
 }


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


[15/50] [abbrv] james-project git commit: JAMES-1877 Make mail lifeCycle readable in DeliveryRunnable

Posted by ro...@apache.org.
JAMES-1877 Make mail lifeCycle readable in DeliveryRunnable


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

Branch: refs/heads/master
Commit: cb0cb77cdcbd605b8e09d289a2b78f7eff1fcb28
Parents: 2e60b2d
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 16:12:39 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:49 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/DeliveryRunnable.java          | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/cb0cb77c/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 75fb3e2..b8be45d 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -101,10 +101,8 @@ public class DeliveryRunnable implements Runnable {
                         if (configuration.isDebug()) {
                             logger.debug(Thread.currentThread().getName() + " will process mail " + mail.getName());
                         }
-
                         attemptDelivery(session, mail);
-
-                        // Clear the object handle to make sure it recycles this object.
+                        LifecycleUtil.dispose(mail);
                         mail = null;
                         queueItem.done(true);
                     } catch (Exception e) {
@@ -131,10 +129,7 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private void attemptDelivery(Session session, Mail mail) throws MailQueue.MailQueueException {
-        if (deliver(mail, session)) {
-            // Message was successfully delivered/fully failed... delete it
-            LifecycleUtil.dispose(mail);
-        } else {
+        if (!deliver(mail, session)) {
             // Something happened that will delay delivery. Store it back in the retry repository.
             long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
 
@@ -143,7 +138,6 @@ public class DeliveryRunnable implements Runnable {
                 mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, MailPrioritySupport.LOW_PRIORITY);
             }
             queue.enQueue(mail, delay, TimeUnit.MILLISECONDS);
-            LifecycleUtil.dispose(mail);
         }
     }
 


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


[12/50] [abbrv] james-project git commit: JAMES-1877 Extract a message composer

Posted by ro...@apache.org.
JAMES-1877 Extract a message composer


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

Branch: refs/heads/master
Commit: 80039f140dff5e26ae4bfc605faf27a6702433cd
Parents: 63bb307
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 14:11:45 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:48 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/MessageComposer.java | 212 +++++++++++++++++++
 1 file changed, 212 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/80039f14/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
new file mode 100644
index 0000000..a57b496
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
@@ -0,0 +1,212 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+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.Arrays;
+
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+
+public class MessageComposer {
+
+    private final RemoteDeliveryConfiguration configuration;
+
+    public MessageComposer(RemoteDeliveryConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    public String composeForBounce(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();
+        return sout.toString();
+    }
+
+    /**
+     * 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
+     */
+    public String fromException(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 for getting the error message from the (nested) exception.
+     *
+     * @param me MessagingException
+     * @return error message
+     */
+    public String getErrorMsg(Exception ex) {
+        if (ex instanceof MessagingException) {
+            return getNestedExceptionMessage((MessagingException) ex);
+        } else {
+            return ex.getMessage();
+        }
+    }
+
+    public String getNestedExceptionMessage(MessagingException me) {
+        if (me.getNextException() == null) {
+            return me.getMessage().trim();
+        } else {
+            Exception ex1 = me.getNextException();
+            return ex1.getMessage().trim();
+        }
+    }
+
+    public String composeFailLogMessage(Mail mail, Exception ex, boolean permanent) {
+        StringWriter sout = new StringWriter();
+        PrintWriter out = new PrintWriter(sout, true);
+        out.print(permanentAsString(permanent) + " exception delivering mail (" + mail.getName() + ")" + retrieveExceptionLog(ex) + ": " );
+        if (configuration.isDebug()) {
+            ex.printStackTrace(out);
+        }
+        return sout.toString();
+    }
+
+    private String permanentAsString(boolean permanent) {
+        if (permanent) {
+            return "Permanent";
+        }
+        return "Temporary";
+    }
+
+    private String retrieveExceptionLog(Exception ex) {
+        String exceptionLog = fromException(ex);
+        if (exceptionLog != null) {
+            return ". " + exceptionLog;
+        }
+        return "";
+    }
+
+    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");
+    }
+
+}


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


[26/50] [abbrv] james-project git commit: JAMES-1877 Introduce a helper for working with MessagingException

Posted by ro...@apache.org.
JAMES-1877 Introduce a helper for working with MessagingException


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

Branch: refs/heads/master
Commit: a0ca1bfa700afb4e5e14aedb2fcca29c146d0489
Parents: 2a4936d
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 10:06:14 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:51 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 135 +++++-----------
 .../EnhancedMessagingException.java             | 161 +++++++++++++++++++
 2 files changed, 200 insertions(+), 96 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/a0ca1bfa/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index c45e736..d62f6f6 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -51,6 +51,7 @@ import org.apache.mailet.MailAddress;
 import org.apache.mailet.MailetContext;
 import org.slf4j.Logger;
 
+import com.google.common.base.Optional;
 import com.sun.mail.smtp.SMTPTransport;
 
 @SuppressWarnings("deprecation")
@@ -334,6 +335,7 @@ public class DeliveryRunnable implements Runnable {
         Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());
 
         ExecutionResult deleteMessage = ExecutionResult.temporaryFailure();
+        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
 
             /*
              * If you send a message that has multiple invalid addresses, you'll
@@ -357,32 +359,12 @@ public class DeliveryRunnable implements Runnable {
              * 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 = ExecutionResult.onFailure(returnCode >= 500 && returnCode <= 599, sfe);
+        if (enhancedMessagingException.hasReturnCode()) {
+            if (enhancedMessagingException.isServerError()) {
+                deleteMessage = ExecutionResult.permanentFailure(sfe);
             } 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 = ExecutionResult.onFailure(returnCode >= 500 && returnCode <= 599, sfe);
-                    }
-                }
+                deleteMessage = ExecutionResult.temporaryFailure(sfe);
             }
-        } 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
@@ -432,9 +414,8 @@ public class DeliveryRunnable implements Runnable {
                 if (configuration.isDebug())
                     logger.debug("Unsent recipients: " + recipients);
 
-                if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
-                    int returnCode = (Integer) invokeGetter(sfe, "getReturnCode");
-                    boolean isPermanent = returnCode >= 500 && returnCode <= 599;
+                if (enhancedMessagingException.hasReturnCode()) {
+                    boolean isPermanent = enhancedMessagingException.isServerError();
                     deleteMessage = ExecutionResult.onFailure(isPermanent, sfe);
                     logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
                 } else {
@@ -458,20 +439,13 @@ public class DeliveryRunnable implements Runnable {
             }
         }
 
-                /*
-                 * 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) {
-            }
+        /*
+        * SMTPSendFailedException introduced in JavaMail 1.3.2, and
+        * provides detailed protocol reply code for the operation
+        */
+        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+        if (enhancedMessagingException.isServerError()) {
+            throw sfe;
         }
 
         if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
@@ -585,65 +559,34 @@ public class DeliveryRunnable implements Runnable {
         return configuration.getDelayTimes().get(retry_count - 1);
     }
 
-
-    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 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");
-                    logger.debug("SMTP SEND FAILED:");
-                    logger.debug(sfe.toString());
-                    logger.debug("  Command: " + command);
-                    logger.debug("  RetCode: " + returnCode);
-                    logger.debug("  Response: " + sfe.getMessage());
-                } catch (IllegalStateException ise) {
-                    // Error invoking the getAddress method
-                    logger.debug("Send failed: " + me.toString());
-                } catch (ClassCastException cce) {
-                    // The getAddress method returned something different than
-                    // InternetAddress
-                    logger.debug("Send failed: " + me.toString());
-                }
+            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+            if (enhancedMessagingException.hasReturnCode()) {
+                logger.debug("SMTP SEND FAILED:");
+                logger.debug(sfe.toString());
+                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
+                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
+                logger.debug("  Response: " + sfe.getMessage());
             } else {
-                logger.debug("Send failed: " + me.toString());
+                logger.debug("Send failed: " + sfe.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");
-                        logger.debug("ADDRESS " + action + ":");
-                        logger.debug(me.toString());
-                        logger.debug("  Address: " + address);
-                        logger.debug("  Command: " + command);
-                        logger.debug("  RetCode: " + returnCode);
-                        logger.debug("  Response: " + me.getMessage());
-                    } catch (IllegalStateException ise) {
-                        // Error invoking the getAddress method
-                    } catch (ClassCastException cce) {
-                        // A method returned something different than expected
-                    }
-                }
+            logLevels(sfe);
+        }
+    }
+
+    private void logLevels(MessagingException me) {
+        Exception ne;
+        while ((ne = me.getNextException()) != null && ne instanceof MessagingException) {
+            me = (MessagingException) ne;
+            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me);
+            if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
+                logger.debug("ADDRESS " + enhancedMessagingException.computeAction() + ":");
+                logger.debug(me.toString());
+                logger.debug("  Address: " + enhancedMessagingException.computeAddress());
+                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
+                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
+                logger.debug("  Response: " + me.getMessage());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/a0ca1bfa/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
new file mode 100644
index 0000000..44b40bd
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/EnhancedMessagingException.java
@@ -0,0 +1,161 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+public class EnhancedMessagingException {
+
+    private final MessagingException messagingException;
+    private final Optional<Integer> returnCode;
+    private final Optional<Integer> nestedReturnCode;
+
+    public EnhancedMessagingException(MessagingException messagingException) {
+        this.messagingException = messagingException;
+        this.returnCode = computeReturnCode();
+        this.nestedReturnCode = computeNestedReturnCode();
+    }
+
+    public boolean hasReturnCode() {
+        return returnCode.isPresent();
+    }
+
+    public boolean hasNestedReturnCode() {
+        return nestedReturnCode.isPresent();
+    }
+
+    public boolean isServerError() {
+        return isServerError(returnCode) || isServerError(nestedReturnCode);
+    }
+
+    private boolean isServerError(Optional<Integer> returnCode) {
+        return (returnCode.isPresent()
+            && returnCode.get() >= 500
+            && returnCode.get() <= 599)
+            || messageIndicatesServerException();
+    }
+
+    private boolean messageIndicatesServerException() {
+        return Optional.fromNullable(messagingException.getMessage())
+            .transform(startWith5())
+            .or(false);
+    }
+
+    private Function<String, Boolean> startWith5() {
+        return new Function<String, Boolean>() {
+            @Override
+            public Boolean apply(String input) {
+                return input.startsWith("5");
+            }
+        };
+    }
+
+    private Optional<Integer> computeReturnCode() {
+        if (messagingException.getClass().getName().endsWith(".SMTPSendFailedException")
+            || messagingException.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
+            try {
+                return Optional.of ((Integer) invokeGetter(messagingException, "getReturnCode"));
+            } catch (ClassCastException cce) {
+            } catch (IllegalArgumentException iae) {
+            } catch (IllegalStateException ise) {
+            }
+        }
+        return Optional.absent();
+    }
+
+    public Optional<String> computeCommand() {
+        if (hasReturnCode()) {
+            try {
+                return Optional.of((String) invokeGetter(messagingException, "getCommand"));
+            } catch (ClassCastException cce) {
+            } catch (IllegalArgumentException iae) {
+            } catch (IllegalStateException ise) {
+            }
+        }
+        return Optional.absent();
+    }
+
+    public Optional<InternetAddress> computeAddress() {
+        if (hasReturnCode()) {
+            try {
+                return Optional.of((InternetAddress) invokeGetter(messagingException, "getAddress"));
+            } catch (ClassCastException cce) {
+            } catch (IllegalArgumentException iae) {
+            } catch (IllegalStateException ise) {
+            }
+        }
+        return Optional.absent();
+    }
+
+    public String computeAction() {
+        return messagingException.getClass().getName().endsWith(".SMTPAddressFailedException") ? "FAILED" : "SUCCEEDED";
+    }
+
+    public Optional<Integer> getReturnCode() {
+        return returnCode;
+    }
+
+    private Optional<Integer> computeNestedReturnCode() {
+        EnhancedMessagingException currentMessagingException = this;
+        while (true) {
+            Optional<Integer> returnCode = currentMessagingException.computeReturnCode();
+            if (returnCode.isPresent()) {
+                return returnCode;
+            }
+            if (currentMessagingException.hasNestedMessagingException()) {
+                currentMessagingException = currentMessagingException.getNestedMessagingException();
+            } else {
+                return Optional.absent();
+            }
+        }
+    }
+
+    private boolean hasNestedMessagingException() {
+        return messagingException.getNextException() != null
+            && messagingException.getNextException() instanceof MessagingException;
+    }
+
+    private EnhancedMessagingException getNestedMessagingException() {
+        Preconditions.checkState(hasNestedMessagingException());
+        return new EnhancedMessagingException((MessagingException) messagingException.getNextException());
+    }
+
+    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");
+    }
+}


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


[48/50] [abbrv] james-project git commit: JAMES-1877 Remove wildcard and eclipse warnings

Posted by ro...@apache.org.
JAMES-1877 Remove wildcard and eclipse warnings


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

Branch: refs/heads/master
Commit: 4221b357cfe2a02172a4d4c54b5a20b946efc6ba
Parents: c06d312
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 11:50:00 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:32 2017 +0700

----------------------------------------------------------------------
 .../src/test/java/org/apache/james/util/streams/IteratorsTest.java | 2 +-
 .../transport/mailets/remoteDelivery/DeliveryRunnableTest.java     | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/4221b357/server/container/util-java8/src/test/java/org/apache/james/util/streams/IteratorsTest.java
----------------------------------------------------------------------
diff --git a/server/container/util-java8/src/test/java/org/apache/james/util/streams/IteratorsTest.java b/server/container/util-java8/src/test/java/org/apache/james/util/streams/IteratorsTest.java
index cad060d..4c63f1a 100644
--- a/server/container/util-java8/src/test/java/org/apache/james/util/streams/IteratorsTest.java
+++ b/server/container/util-java8/src/test/java/org/apache/james/util/streams/IteratorsTest.java
@@ -22,7 +22,7 @@ package org.apache.james.util.streams;
 import static java.util.stream.Collectors.toList;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.stream.*;
+import java.util.stream.Stream;
 
 import org.junit.Test;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/4221b357/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
index 1bfec0d..f8dfd40 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
@@ -19,7 +19,6 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
-import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;


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


[02/50] [abbrv] james-project git commit: JAMES-1877 Extract HeloName provider and test it

Posted by ro...@apache.org.
JAMES-1877 Extract HeloName provider and test it


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

Branch: refs/heads/master
Commit: 1cb969bdda7db0ba8f1c8b52adeb27186bb566e7
Parents: e82996e
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Nov 29 10:27:13 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 11:37:10 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 30 ++------
 .../remoteDelivery/HeloNameProvider.java        | 53 +++++++++++++
 .../remoteDelivery/HeloNameProviderTest.java    | 79 ++++++++++++++++++++
 3 files changed, 140 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1cb969bd/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 0e7e48d..c4113b5 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
@@ -53,12 +53,10 @@ import javax.mail.internet.MimeMultipart;
 import javax.mail.internet.MimePart;
 import javax.mail.internet.ParseException;
 
-import com.sun.mail.smtp.SMTPTransport;
 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.domainlist.api.DomainListException;
 import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
@@ -67,6 +65,7 @@ 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.HeloNameProvider;
 import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
 import org.apache.james.transport.util.Patterns;
 import org.apache.james.util.TimeConverter;
@@ -77,6 +76,8 @@ 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.
@@ -252,8 +253,6 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
 
     private MailQueue queue;
 
-    private String heloName;
-
     private Logger logger;
 
     private boolean usePriority;
@@ -266,6 +265,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
 
     private MetricFactory metricFactory;
     private Metric outgoingMailsMetric;
+    private HeloNameProvider heloNameProvider;
 
     @Inject
     public void setDomainList(DomainList domainList) {
@@ -444,7 +444,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
             dnsProblemRetry = Integer.parseInt(dnsRetry);
         }
 
-        heloName = getInitParameter("heloName");
+        heloNameProvider = new HeloNameProvider(getInitParameter("heloName"), domainList);
 
         String prio = getInitParameter("usePriority");
         if (prio != null) {
@@ -751,7 +751,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
         props.put("mail.smtp.connectiontimeout", connectionTimeout + "");
         props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
 
-        props.put("mail.smtp.localhost", getHeloName());
+        props.put("mail.smtp.localhost", heloNameProvider.getHeloName());
 
         // handle starttls
         props.put("mail.smtp.starttls.enable", String.valueOf(startTLS));
@@ -970,7 +970,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
                     SMTPTransport transport = null;
                     try {
                         transport =  (SMTPTransport) session.getTransport(outgoingMailServer);
-                        transport.setLocalHost( props.getProperty("mail.smtp.localhost", heloName) );
+                        transport.setLocalHost( props.getProperty("mail.smtp.localhost", heloNameProvider.getHeloName()) );
                         try {
                             if (authUser != null) {
                                 transport.connect(outgoingMailServer.getHostName(), authUser, authPass);
@@ -1555,7 +1555,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
         PrintWriter out = new PrintWriter(sout, true);
         String machine;
         try {
-            machine = getHeloName();
+            machine = heloNameProvider.getHeloName();
 
         } catch (Exception e) {
             machine = "[address unknown]";
@@ -1631,18 +1631,4 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
         return new MXHostAddressIterator(gateways, dnsServer, false, logger);
     }
 
-    protected String getHeloName() {
-        if (heloName == null) {
-            // TODO: Maybe we should better just lookup the hostname via dns
-            try {
-                return domainList.getDefaultDomain();
-            } catch (DomainListException e) {
-                log("Unable to access DomainList", e);
-                return "localhost";
-            }
-        } else {
-            return heloName;
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1cb969bd/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java
new file mode 100644
index 0000000..b91c68e
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProvider.java
@@ -0,0 +1,53 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.domainlist.api.DomainListException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HeloNameProvider {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(HeloNameProvider.class);
+    public static final String LOCALHOST = "localhost";
+
+    private final String heloName;
+    private final DomainList domainList;
+
+    public HeloNameProvider(String heloName, DomainList domainList) {
+        this.heloName = heloName;
+        this.domainList = domainList;
+    }
+
+    public String getHeloName() {
+        if (heloName == null) {
+            // TODO: Maybe we should better just lookup the hostname via dns
+            try {
+                return domainList.getDefaultDomain();
+            } catch (DomainListException e) {
+                LOGGER.warn("Unable to access DomainList", e);
+                return LOCALHOST;
+            }
+        } else {
+            return heloName;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1cb969bd/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProviderTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProviderTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProviderTest.java
new file mode 100644
index 0000000..987b99b
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/HeloNameProviderTest.java
@@ -0,0 +1,79 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.mockito.Mockito.mock;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.domainlist.api.DomainListException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class HeloNameProviderTest {
+
+    public static final String DOMAIN = "domain";
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    private DomainList domainList;
+
+    @Before
+    public void setUp() {
+        domainList = mock(DomainList.class);
+    }
+
+    @Test
+    public void getHeloNameShouldReturnNonNullProvidedHeloName() {
+        HeloNameProvider heloNameProvider = new HeloNameProvider(DOMAIN, domainList);
+
+        assertThat(heloNameProvider.getHeloName()).isEqualTo(DOMAIN);
+    }
+
+    @Test
+    public void getHeloNameShouldReturnDomainListDefaultDomainOnNullHeloName() throws DomainListException {
+        when(domainList.getDefaultDomain()).thenReturn(DOMAIN);
+        HeloNameProvider heloNameProvider = new HeloNameProvider(null, domainList);
+
+        assertThat(heloNameProvider.getHeloName()).isEqualTo(DOMAIN);
+    }
+
+    @Test
+    public void getHeloNameShouldReturnLocalhostOnDomainListException() throws DomainListException {
+        when(domainList.getDefaultDomain()).thenThrow(new DomainListException("any message"));
+        HeloNameProvider heloNameProvider = new HeloNameProvider(null, domainList);
+
+        assertThat(heloNameProvider.getHeloName()).isEqualTo(HeloNameProvider.LOCALHOST);
+    }
+
+    @Test
+    public void getHeloNameShouldPropagateRuntimeExceptions() throws DomainListException {
+        when(domainList.getDefaultDomain()).thenThrow(new RuntimeException());
+        HeloNameProvider heloNameProvider = new HeloNameProvider(null, domainList);
+
+        expectedException.expect(RuntimeException.class);
+        heloNameProvider.getHeloName();
+    }
+
+}


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


[23/50] [abbrv] james-project git commit: JAMES-1877 Remove unrelated exception

Posted by ro...@apache.org.
JAMES-1877 Remove unrelated exception

No attempts to dequeue the e-mail was done, no need to throw an exception that will be caught 10 lines under


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

Branch: refs/heads/master
Commit: f5fc4757e0965b3205d4e519378d5d12b8bce2c8
Parents: cb0cb77
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 16:14:10 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:50 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/remoteDelivery/DeliveryRunnable.java    | 1 -
 1 file changed, 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/f5fc4757/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index b8be45d..f6763ef 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -113,7 +113,6 @@ public class DeliveryRunnable implements Runnable {
                         logger.error("Exception caught in RemoteDelivery.run()", e);
                         LifecycleUtil.dispose(mail);
                         queueItem.done(false);
-                        throw new MailQueue.MailQueueException("Unable to perform dequeue", e);
                     }
 
                 } catch (Throwable e) {


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


[44/50] [abbrv] james-project git commit: JAMES-1877 MailDelivrerToHost interface simplification

Posted by ro...@apache.org.
JAMES-1877 MailDelivrerToHost interface simplification


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

Branch: refs/heads/master
Commit: 97e23aee67370202e5d3406becc377f9148a31a3
Parents: d004e6e
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 11:07:46 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:27 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/remoteDelivery/MailDelivrer.java  | 7 +++----
 .../transport/mailets/remoteDelivery/MailDelivrerToHost.java  | 4 ++--
 2 files changed, 5 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/97e23aee/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index 1dfc81b..f42a0fc 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -28,7 +28,6 @@ import javax.mail.Address;
 import javax.mail.MessagingException;
 import javax.mail.SendFailedException;
 import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
 
 import org.apache.james.dnsservice.api.DNSService;
 import org.apache.james.dnsservice.api.TemporaryResolutionException;
@@ -119,7 +118,7 @@ public class MailDelivrer {
             if (!targetServers.hasNext()) {
                 return handleNoTargetServer(mail, host);
             }
-            return doDeliver(mail, mail.getMessage(), InternetAddressConverter.convert(mail.getRecipients()), targetServers);
+            return doDeliver(mail, InternetAddressConverter.convert(mail.getRecipients()), targetServers);
         } catch (TemporaryResolutionException e) {
             return logAndReturn(mail, ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
                 "up mail server for host: " + host + ".  I cannot determine where to send this message.")));
@@ -132,12 +131,12 @@ public class MailDelivrer {
     }
 
     @SuppressWarnings("deprecation")
-    private ExecutionResult doDeliver(Mail mail, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
+    private ExecutionResult doDeliver(Mail mail, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
         MessagingException lastError = null;
 
         while (targetServers.hasNext()) {
             try {
-                if (mailDelivrerToHost.tryDeliveryToHost(mail, message, addr, targetServers.next())) {
+                if (mailDelivrerToHost.tryDeliveryToHost(mail, addr, targetServers.next())) {
                     return ExecutionResult.success();
                 }
             } catch (SendFailedException sfe) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/97e23aee/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
index 46bd8f9..a9f5758 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
@@ -50,7 +50,7 @@ public class MailDelivrerToHost {
         this.logger = logger;
     }
 
-    public boolean tryDeliveryToHost(Mail mail, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
+    public boolean tryDeliveryToHost(Mail mail, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
         Properties props = session.getProperties();
         if (mail.getSender() == null) {
             props.put("mail.smtp.from", "<>");
@@ -74,7 +74,7 @@ public class MailDelivrerToHost {
             transport = (SMTPTransport) session.getTransport(outgoingMailServer);
             transport.setLocalHost( props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName()) );
             connect(outgoingMailServer, transport);
-            transport.sendMessage(adaptToTransport(message, transport), addr);
+            transport.sendMessage(adaptToTransport(mail.getMessage(), transport), addr);
             logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
                 " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
             return true;


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


[32/50] [abbrv] james-project git commit: JAMES-1877 Improve Bouncer :

Posted by ro...@apache.org.
JAMES-1877 Improve Bouncer :

 - Stop failing on empty exception error message
 - Remove extra space in message
 - Move methods of MessageComposer used only by Bouncer


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

Branch: refs/heads/master
Commit: 6a2fe8a883e37afaebcd832dcee1082314b65d2d
Parents: 9b59d55
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 13:57:11 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:52 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/Bouncer.java         | 69 +++++++++++++-------
 .../mailets/remoteDelivery/MessageComposer.java | 23 -------
 .../mailets/remoteDelivery/BouncerTest.java     | 62 +++++++++++++-----
 3 files changed, 93 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/6a2fe8a8/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
index da90b08..fa60c23 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
@@ -37,28 +37,26 @@ public class Bouncer {
 
     public static final String DELIVERY_ERROR = "delivery-error";
     private final RemoteDeliveryConfiguration configuration;
-    private final MessageComposer messageComposer;
     private final MailetContext mailetContext;
     private final Logger logger;
 
     public Bouncer(RemoteDeliveryConfiguration configuration, MailetContext mailetContext, Logger logger) {
         this.configuration = configuration;
-        this.messageComposer = new MessageComposer(configuration);
         this.mailetContext = mailetContext;
         this.logger = logger;
     }
 
     public void bounce(Mail mail, Exception ex) {
         if (mail.getSender() == null) {
-            logger.debug("Null Sender: no bounce will be generated for " + mail.getName());
+            logger.debug("Null Sender: no bounce will be generated for {}", mail.getName());
         } else {
             if (configuration.getBounceProcessor() != null) {
-                mail.setAttribute(DELIVERY_ERROR, messageComposer.getErrorMsg(ex));
+                mail.setAttribute(DELIVERY_ERROR, getErrorMsg(ex));
                 mail.setState(configuration.getBounceProcessor());
                 try {
                     mailetContext.sendMail(mail);
                 } catch (MessagingException e) {
-                    logger.debug("Exception re-inserting failed mail: ", e);
+                    logger.warn("Exception re-inserting failed mail: ", e);
                 }
             } else {
                 bounceWithMailetContext(mail, ex);
@@ -72,26 +70,18 @@ public class Bouncer {
         try {
             mailetContext.bounce(mail, explanationText(mail, ex));
         } catch (MessagingException me) {
-            logger.debug("Encountered unexpected messaging exception while bouncing message: " + me.getMessage());
+            logger.warn("Encountered unexpected messaging exception while bouncing message: {}", me.getMessage());
         } catch (Exception e) {
-            logger.debug("Encountered unexpected exception while bouncing message: " + e.getMessage());
+            logger.warn("Encountered unexpected exception while bouncing message: {}", e.getMessage());
         }
     }
 
     public String explanationText(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("Hi. This is the James mail server at " + resolveMachineName() + ".");
         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("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();
@@ -100,25 +90,58 @@ public class Bouncer {
         }
         if (ex instanceof MessagingException) {
             if (((MessagingException) ex).getNextException() == null) {
-                out.println(ex.getMessage().trim());
+                out.println(sanitizeExceptionMessage(ex));
             } else {
                 Exception ex1 = ((MessagingException) ex).getNextException();
                 if (ex1 instanceof SendFailedException) {
-                    out.println("Remote mail server told me: " + ex1.getMessage().trim());
+                    out.println("Remote mail server told me: " + sanitizeExceptionMessage(ex1));
                 } else if (ex1 instanceof UnknownHostException) {
-                    out.println("Unknown host: " + ex1.getMessage().trim());
+                    out.println("Unknown host: " + sanitizeExceptionMessage(ex1));
                     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());
+                    out.println(sanitizeExceptionMessage(ex1));
                 } else if (ex1 instanceof SocketException) {
-                    out.println("Socket exception: " + ex1.getMessage().trim());
+                    out.println("Socket exception: " + sanitizeExceptionMessage(ex1));
                 } else {
-                    out.println(ex1.getMessage().trim());
+                    out.println(sanitizeExceptionMessage(ex1));
                 }
             }
         }
         out.println();
         return sout.toString();
     }
+
+    private String sanitizeExceptionMessage(Exception e) {
+        if (e.getMessage() == null) {
+            return "null";
+        } else {
+            return e.getMessage().trim();
+        }
+    }
+
+    private String resolveMachineName() {
+        try {
+            return configuration.getHeloNameProvider().getHeloName();
+        } catch (Exception e) {
+            return "[address unknown]";
+        }
+    }
+
+    public String getErrorMsg(Exception ex) {
+        if (ex instanceof MessagingException) {
+            return getNestedExceptionMessage((MessagingException) ex);
+        } else {
+            return sanitizeExceptionMessage(ex);
+        }
+    }
+
+    public String getNestedExceptionMessage(MessagingException me) {
+        if (me.getNextException() == null) {
+            return sanitizeExceptionMessage(me);
+        } else {
+            Exception ex1 = me.getNextException();
+            return sanitizeExceptionMessage(ex1);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/6a2fe8a8/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
index 3e6d861..74852e9 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
@@ -99,29 +99,6 @@ public class MessageComposer {
         return null;
     }
 
-    /**
-     * Utility method for getting the error message from the (nested) exception.
-     *
-     * @param me MessagingException
-     * @return error message
-     */
-    public String getErrorMsg(Exception ex) {
-        if (ex instanceof MessagingException) {
-            return getNestedExceptionMessage((MessagingException) ex);
-        } else {
-            return ex.getMessage();
-        }
-    }
-
-    public String getNestedExceptionMessage(MessagingException me) {
-        if (me.getNextException() == null) {
-            return me.getMessage().trim();
-        } else {
-            Exception ex1 = me.getNextException();
-            return ex1.getMessage().trim();
-        }
-    }
-
     public String composeFailLogMessage(Mail mail, ExecutionResult executionResult) {
         StringWriter sout = new StringWriter();
         PrintWriter out = new PrintWriter(sout, true);

http://git-wip-us.apache.org/repos/asf/james-project/blob/6a2fe8a8/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
index de683c0..cc600ad 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/BouncerTest.java
@@ -74,7 +74,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n\n\n",
             Optional.<MailAddress>absent());
@@ -101,7 +101,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n" +
                 "\n" +
@@ -130,7 +130,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n" +
                 "\n" +
@@ -159,7 +159,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n" +
                 "\n" +
@@ -189,7 +189,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n" +
                 "\n" +
@@ -218,7 +218,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n" +
                 "\n" +
@@ -247,7 +247,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n" +
                 "\n" +
@@ -277,7 +277,7 @@ public class BouncerTest {
     }
 
     @Test
-    public void bounceShouldSupportExceptionWithoutMessagesByDefaultByDefault() throws Exception {
+    public void bounceShouldSupportExceptionWithoutMessagesByDefault() throws Exception {
         RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
             FakeMailetConfig.builder()
                 .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
@@ -294,7 +294,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n\n\n",
             Optional.<MailAddress>absent());
@@ -303,7 +303,7 @@ public class BouncerTest {
     }
 
     @Test
-    public void bounceShouldNotSupportMessagingExceptionWithoutMessagesByDefaultByDefault() throws Exception {
+    public void bounceShouldNotSupportMessagingExceptionWithoutMessagesByDefault() throws Exception {
         RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
             FakeMailetConfig.builder()
                 .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
@@ -317,8 +317,15 @@ public class BouncerTest {
             .build();
         testee.bounce(mail, new MessagingException());
 
+        FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
+            "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
+                "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
+                "I include the list of recipients and the reason why I was unable to deliver\n" +
+                "your message.\n\nnull\n\n",
+            Optional.<MailAddress>absent());
         assertThat(mailetContext.getSentMails()).isEmpty();
-        assertThat(mailetContext.getBouncedMails()).isEmpty();
+        assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
     }
 
     @Test
@@ -367,7 +374,7 @@ public class BouncerTest {
     }
 
     @Test
-    public void bounceShouldDisplayAddressByDefaultByDefault() throws Exception {
+    public void bounceShouldDisplayAddressByDefault() throws Exception {
         RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
             FakeMailetConfig.builder()
                 .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
@@ -385,7 +392,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n\n" +
                 MailAddressFixture.ANY_AT_JAMES2.asString() + "\n\n",
@@ -395,7 +402,7 @@ public class BouncerTest {
     }
 
     @Test
-    public void bounceShouldDisplayAddressesByDefaultByDefault() throws Exception {
+    public void bounceShouldDisplayAddressesByDefault() throws Exception {
         RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
             FakeMailetConfig.builder()
                 .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
@@ -413,7 +420,7 @@ public class BouncerTest {
         FakeMailContext.BouncedMail expected = new FakeMailContext.BouncedMail(FakeMailContext.fromMail(mail),
             "Hi. This is the James mail server at " + HELLO_NAME + ".\n" +
                 "I'm afraid I wasn't able to deliver your message to the following addresses.\n" +
-                "This is a permanent error; I've given up. Sorry it didn't work out.  Below\n" +
+                "This is a permanent error; I've given up. Sorry it didn't work out. Below\n" +
                 "I include the list of recipients and the reason why I was unable to deliver\n" +
                 "your message.\n\n" +
                 MailAddressFixture.ANY_AT_JAMES2.asString() + "\n" +
@@ -422,4 +429,29 @@ public class BouncerTest {
         assertThat(mailetContext.getSentMails()).isEmpty();
         assertThat(mailetContext.getBouncedMails()).containsOnly(expected);
     }
+
+    @Test
+    public void bounceShouldWorkWhenProcessorSpecifiedAndNoExceptionMessage() throws Exception {
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(
+            FakeMailetConfig.builder()
+                .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+                .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME)
+                .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR)
+                .build(),
+            mock(DomainList.class));
+        Bouncer testee = new Bouncer(configuration, mailetContext, LOGGER);
+
+        Mail mail = FakeMail.builder().state(Mail.DEFAULT)
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .build();
+        testee.bounce(mail, new MessagingException());
+
+        FakeMailContext.SentMail expected = FakeMailContext.sentMailBuilder()
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .attribute(DELIVERY_ERROR, "null")
+            .state(BOUNCE_PROCESSOR)
+            .build();
+        assertThat(mailetContext.getSentMails()).containsOnly(expected);
+        assertThat(mailetContext.getBouncedMails()).isEmpty();
+    }
 }


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


[22/50] [abbrv] james-project git commit: JAMES-1877 Centralize decision making in DeliveryRunnable

Posted by ro...@apache.org.
JAMES-1877 Centralize decision making in DeliveryRunnable

All parts generates ExecutionResults.
All boucing / success / enqueuing decision should be centralized


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

Branch: refs/heads/master
Commit: bc52f3375231187e3f5093f43ee995c2dce365ce
Parents: c8a3dd9
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 18:14:25 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:50 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 130 +++++++++----------
 1 file changed, 62 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/bc52f337/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 45169a1..a3e08ef 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -186,22 +186,44 @@ public class DeliveryRunnable implements Runnable {
                 outgoingMailsMetric.increment();
                 break;
             case TEMPORARY_FAILURE:
-                // TODO move the incrementing of retries here instead of in fail message
-                // Something happened that will delay delivery. Store it back in the retry repository.
-                long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
-
-                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);
+                handleTemporaryFailure(mail, executionResult);
                 break;
             case PERMANENT_FAILURE:
-                // TODO move boucing logic here
+                bounce(mail, executionResult.getException().orNull());
                 break;
         }
     }
 
+    private void handleTemporaryFailure(Mail mail, ExecutionResult executionResult) throws MailQueue.MailQueueException {
+        if (!mail.getState().equals(Mail.ERROR)) {
+            mail.setState(Mail.ERROR);
+            DeliveryRetriesHelper.initRetries(mail);
+            mail.setLastUpdated(new Date());
+        }
+        int retries = DeliveryRetriesHelper.retrieveRetries(mail);
+
+        if (retries < configuration.getMaxRetries()) {
+            reAttemptDelivery(mail, retries);
+        } else {
+            logger.debug("Bouncing message " + mail.getName() + " after " + retries + " retries");
+            bounce(mail, new Exception("Too many retries failure. Bouncing after " + retries + " retries.", executionResult.getException().orNull()));
+        }
+    }
+
+    private void reAttemptDelivery(Mail mail, int retries) throws MailQueue.MailQueueException {
+        logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
+        DeliveryRetriesHelper.incrementRetries(mail);
+        mail.setLastUpdated(new Date());
+        // Something happened that will delay delivery. Store it back in the retry repository.
+        long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
+
+        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);
+    }
+
     /**
      * 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
@@ -215,14 +237,7 @@ public class DeliveryRunnable implements Runnable {
      */
     private ExecutionResult deliver(Mail mail, Session session) {
         try {
-            return Optional.fromNullable(tryDeliver(mail, session))
-                .or(failMessage(mail, new MessagingException("No mail server(s) available at this time."), false));
-            /*
-             * 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 tryDeliver(mail, session);
         } catch (SendFailedException sfe) {
             return handleSenderFailedException(mail, sfe);
         } catch (MessagingException ex) {
@@ -234,11 +249,15 @@ public class DeliveryRunnable implements Runnable {
 
             // 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)));
+
+            boolean isPermanent = '5' == ex.getMessage().charAt(0);
+            logger.debug(messageComposer.composeFailLogMessage(mail, ex, isPermanent));
+            return onFailure(isPermanent, ex);
         } catch (Exception ex) {
             logger.error("Generic exception = permanent failure: " + ex.getMessage(), ex);
             // Generic exception = permanent failure
-            return failMessage(mail, ex, PERMANENT_FAILURE);
+            logger.debug(messageComposer.composeFailLogMessage(mail, ex, PERMANENT_FAILURE));
+            return permanentFailure(ex);
         }
     }
 
@@ -301,7 +320,6 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
-        boolean success = false;
         Properties props = session.getProperties();
         if (mail.getSender() == null) {
             props.put("mail.smtp.from", "<>");
@@ -325,16 +343,15 @@ public class DeliveryRunnable implements Runnable {
             transport = (SMTPTransport) session.getTransport(outgoingMailServer);
             transport.setLocalHost( props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName()) );
             if (!connect(outgoingMailServer, transport)) {
-                success = false;
+                return false;
             }
             transport.sendMessage(adaptToTransport(message, transport), addr);
-            success = true;
             logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
                 " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
+            return true;
         } finally {
             closeTransport(mail, outgoingMailServer, transport);
         }
-        return success;
     }
 
     private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
@@ -440,7 +457,8 @@ public class DeliveryRunnable implements Runnable {
 
                 if (configuration.isDebug())
                     logger.debug("Invalid recipients: " + recipients);
-                deleteMessage = failMessage(mail, sfe, true);
+                logger.debug(messageComposer.composeFailLogMessage(mail, sfe, true));
+                deleteMessage = permanentFailure(sfe);
             }
         }
 
@@ -462,11 +480,15 @@ public class DeliveryRunnable implements Runnable {
                 mail.setRecipients(recipients);
                 if (configuration.isDebug())
                     logger.debug("Unsent recipients: " + recipients);
+
                 if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
                     int returnCode = (Integer) invokeGetter(sfe, "getReturnCode");
-                    deleteMessage = failMessage(mail, sfe, returnCode >= 500 && returnCode <= 599);
+                    boolean isPermanent = returnCode >= 500 && returnCode <= 599;
+                    logger.debug(messageComposer.composeFailLogMessage(mail, sfe, isPermanent));
+                    deleteMessage = onFailure(isPermanent, sfe);
                 } else {
-                    deleteMessage = failMessage(mail, sfe, false);
+                    logger.debug(messageComposer.composeFailLogMessage(mail, sfe, false));
+                    deleteMessage = temporaryFailure(sfe);
                 }
             }
         }
@@ -593,11 +615,9 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
-        logger.info("Temporary problem looking up mail server for host: " + host);
-        // temporary problems
-        return failMessage(mail,
-            new MessagingException("Temporary problem looking up mail server for host: " + host + ".  I cannot determine where to send this message."),
-            false);
+        MessagingException messagingException = new MessagingException("Temporary problem looking up mail server for host: " + host + ".  I cannot determine where to send this message.");
+        logger.debug(messageComposer.composeFailLogMessage(mail, messagingException, false));
+        return temporaryFailure(messagingException);
     }
 
     private ExecutionResult handleNoTargetServer(Mail mail, String host) {
@@ -607,9 +627,13 @@ public class DeliveryRunnable implements Runnable {
         int retry = DeliveryRetriesHelper.retrieveRetries(mail);
         if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
             // The domain has no dns entry.. Return a permanent error
-            return failMessage(mail, new MessagingException(exceptionBuffer), true);
+            MessagingException messagingException = new MessagingException(exceptionBuffer);
+            logger.debug(messageComposer.composeFailLogMessage(mail, messagingException, true));
+            return permanentFailure(messagingException);
         } else {
-            return failMessage(mail, new MessagingException(exceptionBuffer), false);
+            MessagingException messagingException = new MessagingException(exceptionBuffer);
+            logger.debug(messageComposer.composeFailLogMessage(mail, messagingException, false));
+            return temporaryFailure(messagingException);
         }
     }
 
@@ -687,41 +711,13 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    /**
-     * @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 ExecutionResult failMessage(Mail mail, Exception ex, boolean permanent) {
-        logger.debug(messageComposer.composeFailLogMessage(mail, ex, permanent));
-        if (!permanent) {
-            if (!mail.getState().equals(Mail.ERROR)) {
-                mail.setState(Mail.ERROR);
-                DeliveryRetriesHelper.initRetries(mail);
-                mail.setLastUpdated(new Date());
-            }
-
-            int retries = DeliveryRetriesHelper.retrieveRetries(mail);
-
-            if (retries < configuration.getMaxRetries()) {
-                logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
-                DeliveryRetriesHelper.incrementRetries(mail);
-                mail.setLastUpdated(new Date());
-                return temporaryFailure();
-            } else {
-                logger.debug("Bouncing message " + mail.getName() + " after " + retries + " retries");
-            }
-        }
-
+    private void bounce(Mail mail, Exception ex) {
         if (mail.getSender() == null) {
             logger.debug("Null Sender: no bounce will be generated for " + mail.getName());
-            return permanentFailure(ex);
         }
 
         if (configuration.getBounceProcessor() != null) {
-            // do the new DSN bounce
-            // setting attributes for DSN mailet
+            // do the new DSN bounce setting attributes for DSN mailet
             mail.setAttribute("delivery-error", messageComposer.getErrorMsg(ex));
             mail.setState(configuration.getBounceProcessor());
             // re-insert the mail into the spool for getting it passed to the dsn-processor
@@ -732,14 +728,12 @@ public class DeliveryRunnable implements Runnable {
                 logger.debug("Exception re-inserting failed mail: ", e);
             }
         } else {
-            // do an old style bounce
-            bounce(mail, ex);
+            bounceWithMailetContext(mail, ex);
         }
-        return permanentFailure(ex);
     }
 
 
-    private void bounce(Mail mail, Exception ex) {
+    private void bounceWithMailetContext(Mail mail, Exception ex) {
         logger.debug("Sending failure message " + mail.getName());
         try {
             mailetContext.bounce(mail, messageComposer.composeForBounce(mail, ex));


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


[28/50] [abbrv] james-project git commit: JAMES-1877 Provide tests for DeliveryRunnable decision making

Posted by ro...@apache.org.
JAMES-1877 Provide tests for DeliveryRunnable decision making


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

Branch: refs/heads/master
Commit: fc1b1d3e7054bf1c8ede4c3fb5d0727f677cf13f
Parents: 9790170
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 14:35:46 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:51 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        |  33 ++-
 .../remoteDelivery/DeliveryRunnableTest.java    | 249 +++++++++++++++++++
 2 files changed, 276 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/fc1b1d3e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index d46a9f5..bc01245 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -31,8 +31,19 @@ import org.apache.mailet.Mail;
 import org.apache.mailet.MailetContext;
 import org.slf4j.Logger;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Supplier;
+
 public class DeliveryRunnable implements Runnable {
 
+    public static final Supplier<Date> CURRENT_DATE_SUPPLIER = new Supplier<Date>() {
+        @Override
+        public Date get() {
+            return new Date();
+        }
+    };
+    public static final AtomicBoolean DEFAULT_NOT_STARTED = new AtomicBoolean(false);
+
     private final MailQueue queue;
     private final RemoteDeliveryConfiguration configuration;
     private final Metric outgoingMailsMetric;
@@ -40,17 +51,26 @@ public class DeliveryRunnable implements Runnable {
     private final Bouncer bouncer;
     private final MailDelivrer mailDelivrer;
     private final VolatileIsDestroyed volatileIsDestroyed;
+    private final Supplier<Date> dateSupplier;
 
     public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric,
                             Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
+        this(queue, configuration, outgoingMailsMetric, logger, new Bouncer(configuration, mailetContext, logger),
+            new MailDelivrer(configuration, new MailDelivrerToHost(configuration, mailetContext, logger), dnsServer, logger),
+            volatileIsDestroyed, CURRENT_DATE_SUPPLIER);
+    }
+
+    @VisibleForTesting
+    DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, Metric outgoingMailsMetric, Logger logger, Bouncer bouncer,
+                     MailDelivrer mailDelivrer, VolatileIsDestroyed volatileIsDestroyed, Supplier<Date> dateSupplier) {
         this.queue = queue;
         this.configuration = configuration;
         this.outgoingMailsMetric = outgoingMailsMetric;
         this.logger = logger;
+        this.bouncer = bouncer;
+        this.mailDelivrer = mailDelivrer;
         this.volatileIsDestroyed = volatileIsDestroyed;
-        this.bouncer = new Bouncer(configuration, mailetContext, logger);
-        MailDelivrerToHost mailDelivrerToHost = new MailDelivrerToHost(configuration, mailetContext, logger);
-        this.mailDelivrer = new MailDelivrer(configuration, mailDelivrerToHost, dnsServer, logger);
+        this.dateSupplier = dateSupplier;
     }
 
     @Override
@@ -98,7 +118,8 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private void attemptDelivery(Mail mail) throws MailQueue.MailQueueException {
+    @VisibleForTesting
+    void attemptDelivery(Mail mail) throws MailQueue.MailQueueException {
         ExecutionResult executionResult = mailDelivrer.deliver(mail);
         switch (executionResult.getExecutionState()) {
             case SUCCESS:
@@ -117,7 +138,7 @@ public class DeliveryRunnable implements Runnable {
         if (!mail.getState().equals(Mail.ERROR)) {
             mail.setState(Mail.ERROR);
             DeliveryRetriesHelper.initRetries(mail);
-            mail.setLastUpdated(new Date());
+            mail.setLastUpdated(dateSupplier.get());
         }
         int retries = DeliveryRetriesHelper.retrieveRetries(mail);
 
@@ -132,7 +153,7 @@ public class DeliveryRunnable implements Runnable {
     private void reAttemptDelivery(Mail mail, int retries) throws MailQueue.MailQueueException {
         logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
         DeliveryRetriesHelper.incrementRetries(mail);
-        mail.setLastUpdated(new Date());
+        mail.setLastUpdated(dateSupplier.get());
         // Something happened that will delay delivery. Store it back in the retry repository.
         long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/fc1b1d3e/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
new file mode 100644
index 0000000..1bfec0d
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnableTest.java
@@ -0,0 +1,249 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.metrics.api.Metric;
+import org.apache.james.queue.api.MailQueue;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.test.FakeMail;
+import org.apache.mailet.base.test.FakeMailetConfig;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Supplier;
+
+public class DeliveryRunnableTest {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryRunnableTest.class);
+    public static final Date FIXED_DATE = new Date(1159599194961L);
+    public static final Supplier<Date> FIXED_DATE_SUPPLIER = new Supplier<Date>() {
+        @Override
+        public Date get() {
+            return FIXED_DATE;
+        }
+    };
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    private DeliveryRunnable testee;
+    private Metric outgoingMailsMetric;
+    private Bouncer bouncer;
+    private MailDelivrer mailDelivrer;
+    private MailQueue mailQueue;
+
+    @Before
+    public void setUp() {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.DEBUG, "true")
+            .setProperty(RemoteDeliveryConfiguration.DELAY_TIME, "1000,2000,3000,4000,5000")
+            .build();
+
+        RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class));
+        outgoingMailsMetric = mock(Metric.class);
+        bouncer = mock(Bouncer.class);
+        mailDelivrer = mock(MailDelivrer.class);
+        mailQueue = mock(MailQueue.class);
+        testee = new DeliveryRunnable(mailQueue, configuration, outgoingMailsMetric, LOGGER, bouncer, mailDelivrer, DeliveryRunnable.DEFAULT_NOT_STARTED, FIXED_DATE_SUPPLIER);
+    }
+
+    @Test
+    public void deliverySuccessShouldIncrementMetric() throws Exception {
+        FakeMail fakeMail = FakeMail.defaultFakeMail();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.success());
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(outgoingMailsMetric).increment();
+        verifyNoMoreInteractions(outgoingMailsMetric);
+    }
+
+    @Test
+    public void deliveryPermanentFailureShouldBounceTheMail() throws Exception {
+        FakeMail fakeMail = FakeMail.defaultFakeMail();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.permanentFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(bouncer).bounce(fakeMail, exception);
+        verifyNoMoreInteractions(bouncer);
+    }
+
+    @Test
+    public void deliveryPermanentFailureShouldNotIncrementDeliveryMetric() throws Exception {
+        FakeMail fakeMail = FakeMail.defaultFakeMail();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.permanentFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verifyNoMoreInteractions(outgoingMailsMetric);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldNotIncrementDeliveryMetric() throws Exception {
+        FakeMail fakeMail = FakeMail.builder().state(Mail.DEFAULT).build();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verifyNoMoreInteractions(outgoingMailsMetric);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldFailOnMailsWithoutState() throws Exception {
+        FakeMail fakeMail = FakeMail.defaultFakeMail();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        expectedException.expect(NullPointerException.class);
+
+        testee.attemptDelivery(fakeMail);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldRetryDelivery() throws Exception {
+        FakeMail fakeMail = FakeMail.builder().state(Mail.DEFAULT).build();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(mailQueue).enQueue(FakeMail.builder()
+                .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 1)
+                .state(Mail.ERROR)
+                .lastUpdated(FIXED_DATE)
+                .build(),
+            1000,
+            TimeUnit.MILLISECONDS);
+        verifyNoMoreInteractions(mailQueue);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldRetryDeliveryWithRightDelay() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+            .state(Mail.ERROR)
+            .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 2)
+            .build();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(mailQueue).enQueue(FakeMail.builder()
+                .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 3)
+                .state(Mail.ERROR)
+                .lastUpdated(FIXED_DATE)
+                .build(),
+            3000,
+            TimeUnit.MILLISECONDS);
+        verifyNoMoreInteractions(mailQueue);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldRetryDeliveryOnMaximumRetryNumber() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+            .state(Mail.ERROR)
+            .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 4)
+            .build();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(mailQueue).enQueue(FakeMail.builder()
+                .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5)
+                .state(Mail.ERROR)
+                .lastUpdated(FIXED_DATE)
+                .build(),
+            5000,
+            TimeUnit.MILLISECONDS);
+        verifyNoMoreInteractions(mailQueue);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldNotRetryDeliveryOverMaximumRetryNumber() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+            .state(Mail.ERROR)
+            .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5)
+            .build();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verifyNoMoreInteractions(mailQueue);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldBounceWhenRetryExceeded() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+            .state(Mail.ERROR)
+            .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5)
+            .build();
+        Exception exception = new Exception("");
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(bouncer).bounce(eq(fakeMail), any(Exception.class));
+        verifyNoMoreInteractions(bouncer);
+    }
+
+    @Test
+    public void deliveryTemporaryFailureShouldResetDeliveryCountOnNonErrorState() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+            .state(Mail.DEFAULT)
+            .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 5)
+            .build();
+        Exception exception = new Exception();
+        when(mailDelivrer.deliver(fakeMail)).thenReturn(ExecutionResult.temporaryFailure(exception));
+
+        testee.attemptDelivery(fakeMail);
+
+        verify(mailQueue).enQueue(FakeMail.builder()
+                .attribute(DeliveryRetriesHelper.DELIVERY_RETRY_COUNT, 1)
+                .state(Mail.ERROR)
+                .lastUpdated(FIXED_DATE)
+                .build(),
+            1000,
+            TimeUnit.MILLISECONDS);
+        verifyNoMoreInteractions(mailQueue);
+    }
+}


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


[29/50] [abbrv] james-project git commit: JAMES-1877 Extract delivery to single server to MailDelivrer

Posted by ro...@apache.org.
JAMES-1877 Extract delivery to single server to MailDelivrer


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

Branch: refs/heads/master
Commit: f567a5a5606aceb53b4bf02beb9384f0d62b31a7
Parents: a0ca1bf
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 10:20:51 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:51 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 126 ++--------------
 .../remoteDelivery/MailDelivrerToHost.java      | 142 +++++++++++++++++++
 2 files changed, 155 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/f567a5a5/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index d62f6f6..37ae531 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -20,20 +20,16 @@
 package org.apache.james.transport.mailets.remoteDelivery;
 
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
-import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
 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.ParseException;
@@ -51,24 +47,21 @@ import org.apache.mailet.MailAddress;
 import org.apache.mailet.MailetContext;
 import org.slf4j.Logger;
 
-import com.google.common.base.Optional;
-import com.sun.mail.smtp.SMTPTransport;
-
 @SuppressWarnings("deprecation")
 public class DeliveryRunnable implements Runnable {
 
-    public static final String BIT_MIME_8 = "8BITMIME";
     private final MailQueue queue;
     private final RemoteDeliveryConfiguration configuration;
     private final DNSService dnsServer;
     private final Metric outgoingMailsMetric;
     private final Logger logger;
     private final Bouncer bouncer;
+    private final MailDelivrerToHost mailDelivrerToHost;
     private final VolatileIsDestroyed volatileIsDestroyed;
     private final MessageComposer messageComposer;
-    private final Converter7Bit converter7Bit;
 
-    public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric, Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
+    public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric,
+                            Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
         this.queue = queue;
         this.configuration = configuration;
         this.dnsServer = dnsServer;
@@ -76,8 +69,8 @@ public class DeliveryRunnable implements Runnable {
         this.logger = logger;
         this.volatileIsDestroyed = volatileIsDestroyed;
         this.messageComposer = new MessageComposer(configuration);
-        this.converter7Bit = new Converter7Bit(mailetContext);
         this.bouncer = new Bouncer(configuration, messageComposer, mailetContext, logger);
+        this.mailDelivrerToHost = new MailDelivrerToHost(configuration, mailetContext, logger);
     }
 
     /**
@@ -86,7 +79,6 @@ public class DeliveryRunnable implements Runnable {
      */
     @Override
     public void run() {
-        final Session session = obtainSession(configuration.createFinalJavaxProperties());
         try {
             while (!Thread.interrupted() && !volatileIsDestroyed.isDestroyed()) {
                 try {
@@ -100,7 +92,7 @@ public class DeliveryRunnable implements Runnable {
                         if (configuration.isDebug()) {
                             logger.debug(Thread.currentThread().getName() + " will process mail " + mail.getName());
                         }
-                        attemptDelivery(session, mail);
+                        attemptDelivery(mail);
                         LifecycleUtil.dispose(mail);
                         mail = null;
                         queueItem.done(true);
@@ -126,8 +118,8 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private void attemptDelivery(Session session, Mail mail) throws MailQueue.MailQueueException {
-        ExecutionResult executionResult = deliver(mail, session);
+    private void attemptDelivery(Mail mail) throws MailQueue.MailQueueException {
+        ExecutionResult executionResult = deliver(mail);
         switch (executionResult.getExecutionState()) {
             case SUCCESS:
                 outgoingMailsMetric.increment();
@@ -182,9 +174,9 @@ public class DeliveryRunnable implements Runnable {
      * @return boolean Whether the delivery was successful and the message can
      *         be deleted
      */
-    private ExecutionResult deliver(Mail mail, Session session) {
+    private ExecutionResult deliver(Mail mail) {
         try {
-            return tryDeliver(mail, session);
+            return tryDeliver(mail);
         } catch (SendFailedException sfe) {
             return handleSenderFailedException(mail, sfe);
         } catch (MessagingException ex) {
@@ -210,7 +202,7 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private ExecutionResult tryDeliver(Mail mail, Session session) throws MessagingException {
+    private ExecutionResult tryDeliver(Mail mail) throws MessagingException {
         if (mail.getRecipients().isEmpty()) {
             logger.info("No recipients specified... not sure how this could have happened.");
             return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
@@ -239,15 +231,15 @@ public class DeliveryRunnable implements Runnable {
             targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
         }
 
-        return doDeliver(mail, session, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
+        return doDeliver(mail, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
     }
 
-    private ExecutionResult doDeliver(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
+    private ExecutionResult doDeliver(Mail mail, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
         MessagingException lastError = null;
 
         while (targetServers.hasNext()) {
             try {
-                if (tryDeliveryToHost(mail, session, message, addr, targetServers.next())) {
+                if (mailDelivrerToHost.tryDeliveryToHost(mail, message, addr, targetServers.next())) {
                     return ExecutionResult.success();
                 }
             } catch (SendFailedException sfe) {
@@ -273,39 +265,6 @@ public class DeliveryRunnable implements Runnable {
         return ExecutionResult.temporaryFailure();
     }
 
-    private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
-        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);
-        }
-        logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
-            + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
-
-        // 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()) );
-            connect(outgoingMailServer, transport);
-            transport.sendMessage(adaptToTransport(message, transport), addr);
-            logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
-                " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
-            return true;
-        } finally {
-            closeTransport(mail, outgoingMailServer, transport);
-        }
-    }
-
     private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
         MessagingException lastError;// MessagingException are horribly difficult to figure out what actually happened.
         logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
@@ -468,61 +427,6 @@ public class DeliveryRunnable implements Runnable {
         return addr;
     }
 
-    private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException {
-        // if the transport is a SMTPTransport (from sun) some
-        // performance enhancement can be done.
-        if (transport.getClass().getName().endsWith(".SMTPTransport")) {
-            // 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 (!transport.supportsExtension(BIT_MIME_8)) {
-                try {
-                    converter7Bit.convertTo7Bit(message);
-                } catch (IOException e) {
-                    // An error has occured during the 7bit conversion.
-                    // The error is logged and the message is sent anyway.
-
-                    logger.error("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 {
-                converter7Bit.convertTo7Bit(message);
-            } catch (IOException e) {
-                logger.error("Error during the conversion to 7 bit.", e);
-            }
-        }
-        return message;
-    }
-
-    private void closeTransport(Mail mail, HostAddress outgoingMailServer, SMTPTransport transport) {
-        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) {
-                logger.error("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;
-        }
-    }
-
-    private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException {
-        if (configuration.getAuthUser() != null) {
-            transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
-        } else {
-            transport.connect();
-        }
-    }
-
     private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
         ExecutionResult executionResult = ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
             "up mail server for host: " + host + ".  I cannot determine where to send this message."));
@@ -548,10 +452,6 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    protected Session obtainSession(Properties props) {
-        return Session.getInstance(props);
-    }
-
     private long getNextDelay(int retry_count) {
         if (retry_count > configuration.getDelayTimes().size()) {
             return Delay.DEFAULT_DELAY_TIME;

http://git-wip-us.apache.org/repos/asf/james-project/blob/f567a5a5/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
new file mode 100644
index 0000000..46bd8f9
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerToHost.java
@@ -0,0 +1,142 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.mailet.HostAddress;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetContext;
+import org.slf4j.Logger;
+
+import com.sun.mail.smtp.SMTPTransport;
+
+@SuppressWarnings("deprecation")
+public class MailDelivrerToHost {
+    public static final String BIT_MIME_8 = "8BITMIME";
+
+    private final RemoteDeliveryConfiguration configuration;
+    private final Converter7Bit converter7Bit;
+    private final Session session;
+    private final Logger logger;
+
+    public MailDelivrerToHost(RemoteDeliveryConfiguration remoteDeliveryConfiguration, MailetContext mailetContext, Logger logger) {
+        this.configuration = remoteDeliveryConfiguration;
+        this.converter7Bit = new Converter7Bit(mailetContext);
+        this.session = Session.getInstance(configuration.createFinalJavaxProperties());
+        this.logger = logger;
+    }
+
+    public boolean tryDeliveryToHost(Mail mail, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
+        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);
+        }
+        logger.debug("Attempting delivery of " + mail.getName() + " to host " + outgoingMailServer.getHostName()
+            + " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from"));
+
+        // 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()) );
+            connect(outgoingMailServer, transport);
+            transport.sendMessage(adaptToTransport(message, transport), addr);
+            logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
+                " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
+            return true;
+        } finally {
+            closeTransport(mail, outgoingMailServer, transport);
+        }
+    }
+
+    private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException {
+        if (configuration.getAuthUser() != null) {
+            transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
+        } else {
+            transport.connect();
+        }
+    }
+
+    private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException {
+        // if the transport is a SMTPTransport (from sun) some
+        // performance enhancement can be done.
+        if (transport.getClass().getName().endsWith(".SMTPTransport")) {
+            // 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 (!transport.supportsExtension(BIT_MIME_8)) {
+                try {
+                    converter7Bit.convertTo7Bit(message);
+                } catch (IOException e) {
+                    // An error has occured during the 7bit conversion.
+                    // The error is logged and the message is sent anyway.
+
+                    logger.error("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 {
+                converter7Bit.convertTo7Bit(message);
+            } catch (IOException e) {
+                logger.error("Error during the conversion to 7 bit.", e);
+            }
+        }
+        return message;
+    }
+
+    private void closeTransport(Mail mail, HostAddress outgoingMailServer, SMTPTransport transport) {
+        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) {
+                logger.error("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;
+        }
+    }
+
+
+}


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


[47/50] [abbrv] james-project git commit: JAMES-1877 Provide tests for SFEHelperTest

Posted by ro...@apache.org.
JAMES-1877 Provide tests for SFEHelperTest


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

Branch: refs/heads/master
Commit: d584c13e5fbe8e5f554a352593f85ddba0d2f6ec
Parents: 4221b35
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 8 15:20:17 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:32 2017 +0700

----------------------------------------------------------------------
 ...ddressesArrayToMailAddressListConverter.java | 66 ++++++++++++++++++++
 .../mailets/remoteDelivery/MailDelivrer.java    |  4 +-
 .../mailets/remoteDelivery/SFEHelper.java       | 66 --------------------
 ...ssesArrayToMailAddressListConverterTest.java | 59 +++++++++++++++++
 4 files changed, 127 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/d584c13e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
new file mode 100644
index 0000000..d468947
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.mail.Address;
+import javax.mail.internet.AddressException;
+
+import org.apache.mailet.MailAddress;
+import org.slf4j.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+public class AddressesArrayToMailAddressListConverter {
+
+    public static List<MailAddress> getAddressesAsMailAddress(Address[] addresses, final Logger logger) {
+        if (addresses == null) {
+            return ImmutableList.of();
+        }
+        return FluentIterable.from(Arrays.asList(addresses)).transform(new Function<Address, Optional<MailAddress>>() {
+            @Override
+            public Optional<MailAddress> apply(Address input) {
+                try {
+                    return Optional.of(new MailAddress(input.toString()));
+                } catch (AddressException e) {
+                    logger.debug("Can't parse unsent address: " + e.getMessage());
+                    return Optional.absent();
+                }
+            }
+        }).filter(new Predicate<Optional<MailAddress>>() {
+            @Override
+            public boolean apply(Optional<MailAddress> input) {
+                return input.isPresent();
+            }
+        }).transform(new Function<Optional<MailAddress>, MailAddress>() {
+            @Override
+            public MailAddress apply(Optional<MailAddress> input) {
+                return input.get();
+            }
+        }).toList();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d584c13e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index 1036734..df38243 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -168,8 +168,8 @@ public class MailDelivrer {
     ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {
         logSendFailedException(sfe);
         EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
-        List<MailAddress> invalidAddresses = SFEHelper.getAddressesAsMailAddress(sfe.getInvalidAddresses(), logger);
-        List<MailAddress> validUnsentAddresses = SFEHelper.getAddressesAsMailAddress(sfe.getValidUnsentAddresses(), logger);
+        List<MailAddress> invalidAddresses = AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(sfe.getInvalidAddresses(), logger);
+        List<MailAddress> validUnsentAddresses = AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(sfe.getValidUnsentAddresses(), logger);
         if (configuration.isDebug()) {
             logger.debug("Mail {} has initially recipients: {}", mail.getName(), mail.getRecipients());
             if (!invalidAddresses.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/d584c13e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java
deleted file mode 100644
index a9650d3..0000000
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/SFEHelper.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.transport.mailets.remoteDelivery;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.mail.Address;
-import javax.mail.internet.AddressException;
-
-import org.apache.mailet.MailAddress;
-import org.slf4j.Logger;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-
-public class SFEHelper {
-
-    public static List<MailAddress> getAddressesAsMailAddress(Address[] addresses, final Logger logger) {
-        if (addresses == null) {
-            return ImmutableList.of();
-        }
-        return FluentIterable.from(Arrays.asList(addresses)).transform(new Function<Address, Optional<MailAddress>>() {
-            @Override
-            public Optional<MailAddress> apply(Address input) {
-                try {
-                    return Optional.of(new MailAddress(input.toString()));
-                } catch (AddressException e) {
-                    logger.debug("Can't parse unsent address: " + e.getMessage());
-                    return Optional.absent();
-                }
-            }
-        }).filter(new Predicate<Optional<MailAddress>>() {
-            @Override
-            public boolean apply(Optional<MailAddress> input) {
-                return input.isPresent();
-            }
-        }).transform(new Function<Optional<MailAddress>, MailAddress>() {
-            @Override
-            public MailAddress apply(Optional<MailAddress> input) {
-                return input.get();
-            }
-        }).toList();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d584c13e/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverterTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverterTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverterTest.java
new file mode 100644
index 0000000..dd05c7c
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverterTest.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.mail.Address;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.mailet.base.MailAddressFixture;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AddressesArrayToMailAddressListConverterTest {
+    private static final Logger LOGGER = LoggerFactory.getLogger(AddressesArrayToMailAddressListConverterTest.class);
+
+    @Test
+    public void getAddressesAsMailAddressShouldReturnEmptyOnNull() {
+        assertThat(AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(null, LOGGER)).isEmpty();
+    }
+
+    @Test
+    public void getAddressesAsMailAddressShouldReturnEmptyOnEmpty() {
+        assertThat(AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(new Address[]{}, LOGGER)).isEmpty();
+    }
+
+    @Test
+    public void getAddressesAsMailAddressShouldWorkWithSingleValue() throws Exception {
+        assertThat(AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(new Address[]{
+            new InternetAddress(MailAddressFixture.ANY_AT_JAMES.toString())}, LOGGER))
+            .containsOnly(MailAddressFixture.ANY_AT_JAMES);
+    }
+
+    @Test
+    public void getAddressesAsMailAddressShouldWorkWithTwoValues() throws Exception {
+        assertThat(AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(new Address[]{
+            new InternetAddress(MailAddressFixture.ANY_AT_JAMES.toString()),
+            new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.toString())}, LOGGER))
+            .containsOnly(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES);
+    }
+}


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


[08/50] [abbrv] james-project git commit: JAMES-1877 Extract DeliveryRunnable from RemoteDelivery

Posted by ro...@apache.org.
http://git-wip-us.apache.org/repos/asf/james-project/blob/4a5a4ba6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
new file mode 100644
index 0000000..5a0bdf1
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -0,0 +1,963 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+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.Iterator;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+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.lifecycle.api.LifecycleUtil;
+import org.apache.james.metrics.api.Metric;
+import org.apache.james.queue.api.MailPrioritySupport;
+import org.apache.james.queue.api.MailQueue;
+import org.apache.mailet.HostAddress;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.apache.mailet.MailetContext;
+import org.slf4j.Logger;
+
+import com.sun.mail.smtp.SMTPTransport;
+
+@SuppressWarnings("deprecation")
+public class DeliveryRunnable implements Runnable {
+
+    private final MailQueue queue;
+    private final RemoteDeliveryConfiguration configuration;
+    private final DNSService dnsServer;
+    private final Metric outgoingMailsMetric;
+    private final Logger logger;
+    private final MailetContext mailetContext;
+    private final VolatileIsDestroyed volatileIsDestroyed;
+
+    public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric, Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
+        this.queue = queue;
+        this.configuration = configuration;
+        this.dnsServer = dnsServer;
+        this.outgoingMailsMetric = outgoingMailsMetric;
+        this.logger = logger;
+        this.mailetContext = mailetContext;
+        this.volatileIsDestroyed = volatileIsDestroyed;
+    }
+
+    /**
+     * 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() && !volatileIsDestroyed.isDestroyed()) {
+                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.
+                    MailQueue.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;
+                            logger.debug(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!
+                        logger.error("Exception caught in RemoteDelivery.run()", e);
+                        LifecycleUtil.dispose(mail);
+                        // workRepository.remove(key);
+                        queueItem.done(false);
+                        throw new MailQueue.MailQueueException("Unable to perform dequeue", e);
+                    }
+
+                } catch (Throwable e) {
+                    if (!volatileIsDestroyed.isDestroyed()) {
+                        logger.error("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()) {
+                logger.debug("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) {
+                logger.info("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().isEmpty()) {
+                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) {
+                    logger.info("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()) {
+                    logger.info("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));
+                    logger.debug(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()) {
+                                logger.debug(me.getMessage(), me.getCause());
+                            } else {
+                                logger.info(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.
+
+                                    logger.error("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) {
+                                logger.error("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) {
+                                logger.error("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());
+                    logger.debug(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);
+                            logger.debug(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())
+                            logger.debug("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();
+                    logger.debug(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())
+                logger.debug("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.
+                            logger.debug("Can't parse invalid address: " + pe.getMessage());
+                        }
+                    }
+                    // Set the recipients for the mail
+                    mail.setRecipients(recipients);
+
+                    if (configuration.isDebug())
+                        logger.debug("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.
+                            logger.debug("Can't parse unsent address: " + pe.getMessage());
+                        }
+                    }
+                    // Set the recipients for the mail
+                    mail.setRecipients(recipients);
+                    if (configuration.isDebug())
+                        logger.debug("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) {
+            logger.error("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);
+    }
+
+    /**
+     * Returns the javamail Session object.
+     *
+     * @param props
+     * @return the java mail session
+     */
+    protected Session obtainSession(Properties props) {
+        return Session.getInstance(props);
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * 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");
+                    logger.debug("SMTP SEND FAILED:");
+                    logger.debug(sfe.toString());
+                    logger.debug("  Command: " + command);
+                    logger.debug("  RetCode: " + returnCode);
+                    logger.debug("  Response: " + sfe.getMessage());
+                } catch (IllegalStateException ise) {
+                    // Error invoking the getAddress method
+                    logger.debug("Send failed: " + me.toString());
+                } catch (ClassCastException cce) {
+                    // The getAddress method returned something different than
+                    // InternetAddress
+                    logger.debug("Send failed: " + me.toString());
+                }
+            } else {
+                logger.debug("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");
+                        logger.debug("ADDRESS " + action + ":");
+                        logger.debug(me.toString());
+                        logger.debug("  Address: " + address);
+                        logger.debug("  Command: " + command);
+                        logger.debug("  RetCode: " + returnCode);
+                        logger.debug("  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 " + mailetContext.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);
+        logger.debug(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");
+                logger.debug(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");
+                logger.debug(logBuffer.toString());
+            }
+        }
+
+        if (mail.getSender() == null) {
+            logger.debug("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 = mailetContext;
+            try {
+                mc.sendMail(mail);
+            } catch (MessagingException e) {
+                // we shouldn't get an exception, because the mail was already
+                // processed
+                logger.debug("Exception re-inserting failed mail: ", e);
+            }
+        } else {
+            // do an old style bounce
+            bounce(mail, ex);
+        }
+        return true;
+    }
+
+
+    /**
+     * 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 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();
+
+        logger.debug("Sending failure message " + mail.getName());
+        try {
+            mailetContext.bounce(mail, sout.toString());
+        } catch (MessagingException me) {
+            logger.debug("Encountered unexpected messaging exception while bouncing message: " + me.getMessage());
+        } catch (Exception e) {
+            logger.debug("Encountered unexpected exception while bouncing message: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4a5a4ba6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java
new file mode 100644
index 0000000..19d4b36
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyed.java
@@ -0,0 +1,36 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+public class VolatileIsDestroyed {
+    private volatile boolean isDestroyed;
+
+    public VolatileIsDestroyed() {
+        this.isDestroyed = false;
+    }
+
+    public boolean isDestroyed() {
+        return isDestroyed;
+    }
+
+    public void markAsDestroyed() {
+        isDestroyed = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4a5a4ba6/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java
new file mode 100644
index 0000000..dd10c8c
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/VolatileIsDestroyedTest.java
@@ -0,0 +1,41 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class VolatileIsDestroyedTest {
+
+    @Test
+    public void isDestroyedShouldBeFalseByDefault() {
+        assertThat(new VolatileIsDestroyed().isDestroyed()).isFalse();
+    }
+
+    @Test
+    public void isDestroyedShouldBeTrueWhenMarkedAsDestroyed() {
+        VolatileIsDestroyed volatileIsDestroyed = new VolatileIsDestroyed();
+
+        volatileIsDestroyed.markAsDestroyed();
+
+        assertThat(volatileIsDestroyed.isDestroyed()).isTrue();
+    }
+}


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


[19/50] [abbrv] james-project git commit: JAMES-1877 Extract 7 bit conversion

Posted by ro...@apache.org.
JAMES-1877 Extract 7 bit conversion


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

Branch: refs/heads/master
Commit: a62e460e668c72bd2a3e71624a8446c9e456a1aa
Parents: 3b02d3f
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 15:50:50 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:49 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/Converter7Bit.java   | 65 ++++++++++++++++++++
 .../remoteDelivery/DeliveryRunnable.java        | 39 ++----------
 2 files changed, 69 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/a62e460e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java
new file mode 100644
index 0000000..ab798e6
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.io.IOException;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimePart;
+
+import org.apache.mailet.MailetContext;
+
+public class Converter7Bit {
+
+    private final MailetContext mailetContext;
+
+    public Converter7Bit(MailetContext mailetContext) {
+        this.mailetContext = mailetContext;
+    }
+
+    public MimePart 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 " + mailetContext.getServerInfo());
+        }
+        return part;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/a62e460e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 9c31696..d9db12c 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -36,8 +36,6 @@ 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;
@@ -69,6 +67,7 @@ public class DeliveryRunnable implements Runnable {
     private final MailetContext mailetContext;
     private final VolatileIsDestroyed volatileIsDestroyed;
     private final MessageComposer messageComposer;
+    private final Converter7Bit converter7Bit;
 
     public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric, Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
         this.queue = queue;
@@ -79,6 +78,7 @@ public class DeliveryRunnable implements Runnable {
         this.mailetContext = mailetContext;
         this.volatileIsDestroyed = volatileIsDestroyed;
         this.messageComposer = new MessageComposer(configuration);
+        this.converter7Bit = new Converter7Bit(mailetContext);
     }
 
     /**
@@ -486,7 +486,7 @@ public class DeliveryRunnable implements Runnable {
             // Temporarily disabled. See JAMES-638
             if (!transport.supportsExtension(BIT_MIME_8)) {
                 try {
-                    convertTo7Bit(message);
+                    converter7Bit.convertTo7Bit(message);
                 } catch (IOException e) {
                     // An error has occured during the 7bit conversion.
                     // The error is logged and the message is sent anyway.
@@ -498,7 +498,7 @@ public class DeliveryRunnable implements Runnable {
             // 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);
+                converter7Bit.convertTo7Bit(message);
             } catch (IOException e) {
                 logger.error("Error during the conversion to 7 bit.", e);
             }
@@ -647,37 +647,6 @@ public class DeliveryRunnable implements Runnable {
     }
 
     /**
-     * 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 " + mailetContext.getServerInfo());
-        }
-    }
-
-    /**
      * Insert the method's description here.
      *
      * @param mail      org.apache.james.core.MailImpl


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


[38/50] [abbrv] james-project git commit: JAMES-1877 Bounce on invalid addresses before discarding them

Posted by ro...@apache.org.
JAMES-1877 Bounce on invalid addresses before discarding them


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

Branch: refs/heads/master
Commit: 9646571062ae917dd678c4c15c90012a648c2ab8
Parents: 1c6d1d0
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 10:13:38 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:17 2017 +0700

----------------------------------------------------------------------
 .../org/apache/mailet/base/test/FakeMail.java   | 18 +++++++---
 .../james/transport/mailets/RemoteDelivery.java | 38 ++++++++++----------
 .../remoteDelivery/DeliveryRunnable.java        |  6 ++--
 .../mailets/remoteDelivery/MailDelivrer.java    |  8 ++++-
 .../remoteDelivery/MailDelivrerTest.java        | 24 ++++++++++++-
 .../remoteDelivery/RemoteDeliveryTest.java      |  5 ++-
 6 files changed, 67 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/96465710/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
----------------------------------------------------------------------
diff --git a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
index 661882a..59a3b89 100644
--- a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
+++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMail.java
@@ -58,6 +58,19 @@ public class FakeMail implements Mail {
                     new ByteArrayInputStream(text.getBytes(javaEncodingCharset))))
                 .build();
     }
+
+    public static FakeMail fromMail(Mail mail) throws MessagingException {
+        return new FakeMail(mail.getMessage(),
+            Lists.newArrayList(mail.getRecipients()),
+            mail.getName(),
+            mail.getSender(),
+            mail.getState(),
+            mail.getErrorMessage(),
+            mail.getLastUpdated(),
+            attributes(mail),
+            mail.getMessageSize(),
+            mail.getRemoteAddr());
+    }
     
     public static Builder builder() {
         return new Builder();
@@ -212,11 +225,6 @@ public class FakeMail implements Mail {
         this.remoteAddr = remoteAddr;
     }
 
-    public FakeMail(Mail mail) throws MessagingException {
-        this(mail.getMessage(), Lists.newArrayList(mail.getRecipients()), mail.getName(), mail.getSender(), mail.getState(), mail.getErrorMessage(),
-            mail.getLastUpdated(), attributes(mail), mail.getMessageSize(), mail.getRemoteAddr());
-    }
-
     @Override
     public String getName() {
         return name;

http://git-wip-us.apache.org/repos/asf/james-project/blob/96465710/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 8a40298..3a1f17d 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
@@ -23,7 +23,8 @@ import java.net.UnknownHostException;
 import java.util.Collection;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import javax.inject.Inject;
 import javax.mail.MessagingException;
@@ -36,6 +37,7 @@ 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.MailQueueFactory;
+import org.apache.james.transport.mailets.remoteDelivery.Bouncer;
 import org.apache.james.transport.mailets.remoteDelivery.DeliveryRunnable;
 import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliveryConfiguration;
 import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
@@ -119,34 +121,37 @@ import com.google.common.collect.HashMultimap;
  */
 public class RemoteDelivery extends GenericMailet {
 
+    public enum THREAD_STATE {
+        START_THREADS,
+        DO_NOT_START_THREADS
+    }
+
     private static final String OUTGOING_MAILS = "outgoingMails";
-    private static final boolean DEFAULT_START_THREADS = true;
     public static final String NAME_JUNCTION = "-to-";
 
     private final DNSService dnsServer;
     private final DomainList domainList;
     private final MailQueueFactory queueFactory;
     private final Metric outgoingMailsMetric;
-    private final Collection<Thread> workersThreads;
     private final VolatileIsDestroyed volatileIsDestroyed;
-    private final boolean startThreads;
+    private final THREAD_STATE startThreads;
 
     private MailQueue queue;
     private Logger logger;
     private RemoteDeliveryConfiguration configuration;
+    private ExecutorService executor;
 
     @Inject
     public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory) {
-        this(dnsServer, domainList, queueFactory, metricFactory, DEFAULT_START_THREADS);
+        this(dnsServer, domainList, queueFactory, metricFactory, THREAD_STATE.START_THREADS);
     }
 
-    public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory, boolean startThreads) {
+    public RemoteDelivery(DNSService dnsServer, DomainList domainList, MailQueueFactory queueFactory, MetricFactory metricFactory, THREAD_STATE startThreads) {
         this.dnsServer = dnsServer;
         this.domainList = domainList;
         this.queueFactory = queueFactory;
         this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
         this.volatileIsDestroyed = new VolatileIsDestroyed();
-        this.workersThreads = new Vector<Thread>();
         this.startThreads = startThreads;
     }
 
@@ -160,25 +165,23 @@ public class RemoteDelivery extends GenericMailet {
         } catch (UnknownHostException e) {
             log("Invalid bind setting (" + configuration.getBindAddress() + "): " + e.toString());
         }
-        if (startThreads) {
+        if (startThreads == THREAD_STATE.START_THREADS) {
             initDeliveryThreads();
         }
     }
 
     private void initDeliveryThreads() {
+        executor = Executors.newFixedThreadPool(configuration.getWorkersThreadCount());
         for (int a = 0; a < configuration.getWorkersThreadCount(); a++) {
-            String threadName = "Remote delivery thread (" + a + ")";
-            Thread t = new Thread(
+            executor.execute(
                 new DeliveryRunnable(queue,
                     configuration,
                     dnsServer,
                     outgoingMailsMetric,
                     logger,
                     getMailetContext(),
-                    volatileIsDestroyed),
-                threadName);
-            t.start();
-            workersThreads.add(t);
+                    new Bouncer(configuration, getMailetContext(), logger),
+                    volatileIsDestroyed));
         }
     }
 
@@ -255,12 +258,9 @@ public class RemoteDelivery extends GenericMailet {
      */
     @Override
     public synchronized void destroy() {
-        if (startThreads) {
+        if (startThreads == THREAD_STATE.START_THREADS) {
             volatileIsDestroyed.markAsDestroyed();
-            // Wake up all threads from waiting for an accept
-            for (Thread t : workersThreads) {
-                t.interrupt();
-            }
+            executor.shutdown();
             notifyAll();
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/96465710/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index bc01245..7af269b 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -54,9 +54,9 @@ public class DeliveryRunnable implements Runnable {
     private final Supplier<Date> dateSupplier;
 
     public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric,
-                            Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
-        this(queue, configuration, outgoingMailsMetric, logger, new Bouncer(configuration, mailetContext, logger),
-            new MailDelivrer(configuration, new MailDelivrerToHost(configuration, mailetContext, logger), dnsServer, logger),
+                            Logger logger, MailetContext mailetContext, Bouncer bouncer, VolatileIsDestroyed volatileIsDestroyed) {
+        this(queue, configuration, outgoingMailsMetric, logger, bouncer,
+            new MailDelivrer(configuration, new MailDelivrerToHost(configuration, mailetContext, logger), dnsServer, bouncer, logger),
             volatileIsDestroyed, CURRENT_DATE_SUPPLIER);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/96465710/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index c89a2a0..bb03b62 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -45,11 +45,13 @@ public class MailDelivrer {
     private final MailDelivrerToHost mailDelivrerToHost;
     private final DnsHelper dnsHelper;
     private final MessageComposer messageComposer;
+    private final Bouncer bouncer;
     private final Logger logger;
 
-    public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Logger logger) {
+    public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Bouncer bouncer, Logger logger) {
         this.configuration = configuration;
         this.mailDelivrerToHost = mailDelivrerToHost;
+        this.bouncer = bouncer;
         this.dnsHelper = new DnsHelper(dnsServer, configuration, logger);
         this.messageComposer = new MessageComposer(configuration);
         this.logger = logger;
@@ -191,6 +193,10 @@ public class MailDelivrer {
             }
         }
         if (!validUnsentAddresses.isEmpty()) {
+            if (!invalidAddresses.isEmpty()) {
+                mail.setRecipients(invalidAddresses);
+                bouncer.bounce(mail, sfe);
+            }
             mail.setRecipients(validUnsentAddresses);
             if (enhancedMessagingException.hasReturnCode()) {
                 boolean isPermanent = enhancedMessagingException.isServerError();

http://git-wip-us.apache.org/repos/asf/james-project/blob/96465710/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
index 9c5fe69..c96e9a1 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
@@ -21,6 +21,8 @@ package org.apache.james.transport.mailets.remoteDelivery;
 
 import static org.mockito.Mockito.mock;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import javax.mail.Address;
 import javax.mail.SendFailedException;
@@ -42,10 +44,12 @@ public class MailDelivrerTest {
     private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrerTest.class);
 
     private MailDelivrer testee;
+    private Bouncer bouncer;
 
     @Before
     public void setUp() {
-        testee = new MailDelivrer(mock(RemoteDeliveryConfiguration.class), mock(MailDelivrerToHost.class), mock(DNSService.class), LOGGER);
+        bouncer = mock(Bouncer.class);
+        testee = new MailDelivrer(mock(RemoteDeliveryConfiguration.class), mock(MailDelivrerToHost.class), mock(DNSService.class), bouncer, LOGGER);
     }
 
     @Test
@@ -180,4 +184,22 @@ public class MailDelivrerTest {
 
         assertThat(mail.getRecipients()).containsOnly(MailAddressFixture.OTHER_AT_JAMES);
     }
+
+    @Test
+    public void handleSenderFailedExceptionShouldBounceInvalidAddressesOnBothInvalidAndValidUnsent() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        Address[] validSent = {};
+        Address[] validUnsent = {new InternetAddress(MailAddressFixture.OTHER_AT_JAMES.asString())};
+        Address[] invalid = {new InternetAddress(MailAddressFixture.ANY_AT_JAMES.asString())};
+        SendFailedException sfe = new SendFailedException("Message",
+            new Exception(),
+            validSent,
+            validUnsent,
+            invalid);
+        testee.handleSenderFailedException(mail, sfe);
+
+        verify(bouncer).bounce(mail, sfe);
+        verifyNoMoreInteractions(bouncer);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/96465710/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
index 2012a2e..6e944e3 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryTest.java
@@ -66,7 +66,7 @@ public class RemoteDeliveryTest {
         @Override
         public void enQueue(Mail mail) throws MailQueueException {
             try {
-                enqueuedMail.add(new FakeMail(mail));
+                enqueuedMail.add(FakeMail.fromMail(mail));
             } catch (MessagingException e) {
                 throw Throwables.propagate(e);
             }
@@ -82,7 +82,6 @@ public class RemoteDeliveryTest {
         }
     }
 
-    public static final boolean DONT_START_THREADS = false;
     private RemoteDelivery remoteDelivery;
     private FakeMailQueue mailQueue;
 
@@ -91,7 +90,7 @@ public class RemoteDeliveryTest {
         MailQueueFactory queueFactory = mock(MailQueueFactory.class);
         mailQueue = new FakeMailQueue();
         when(queueFactory.getQueue(RemoteDeliveryConfiguration.OUTGOING)).thenReturn(mailQueue);
-        remoteDelivery = new RemoteDelivery(mock(DNSService.class), mock(DomainList.class), queueFactory, mock(MetricFactory.class), DONT_START_THREADS);
+        remoteDelivery = new RemoteDelivery(mock(DNSService.class), mock(DomainList.class), queueFactory, mock(MetricFactory.class), RemoteDelivery.THREAD_STATE.DO_NOT_START_THREADS);
     }
 
     @Test


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


[13/50] [abbrv] james-project git commit: JAMES-1877 Inspection is not needed to see if 8 bit is supported

Posted by ro...@apache.org.
JAMES-1877 Inspection is not needed to see if 8 bit is supported


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

Branch: refs/heads/master
Commit: 63bb307449633acc0bfb2a0c9a7ac908ccead9e0
Parents: b839f8b
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 14:11:15 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:48 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/DeliveryRunnable.java | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/63bb3074/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index cb189d0..ea35f7c 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -59,6 +59,7 @@ import com.sun.mail.smtp.SMTPTransport;
 public class DeliveryRunnable implements Runnable {
 
     public static final boolean PERMANENT_FAILURE = true;
+    public static final String BIT_MIME_8 = "8BITMIME";
     private final MailQueue queue;
     private final RemoteDeliveryConfiguration configuration;
     private final DNSService dnsServer;
@@ -407,7 +408,7 @@ public class DeliveryRunnable implements Runnable {
                         // that conversion, but it is required to be a rfc-compliant smtp server.
 
                         // Temporarily disabled. See JAMES-638
-                        if (!isSupports8bitmime(transport)) {
+                        if (!transport.supportsExtension(BIT_MIME_8)) {
                             try {
                                 convertTo7Bit(message);
                             } catch (IOException e) {
@@ -513,22 +514,6 @@ public class DeliveryRunnable implements Runnable {
         return null;
     }
 
-    private boolean isSupports8bitmime(SMTPTransport transport) {
-        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.
-        }
-        return supports8bitmime;
-    }
-
     private boolean handleTemporaryResolutionException(Mail mail, String host) {
         logger.info("Temporary problem looking up mail server for host: " + host);
         // temporary problems


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


[25/50] [abbrv] james-project git commit: JAMES-1877 Simplify MessageComposer API with ExecutionResult

Posted by ro...@apache.org.
JAMES-1877 Simplify MessageComposer API with ExecutionResult


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

Branch: refs/heads/master
Commit: 942cdfc66265af6a966b5814b5366fda26433e7e
Parents: 26c6d9c
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 18:45:36 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:50 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 108 +++++--------------
 .../mailets/remoteDelivery/ExecutionResult.java |  76 +++++++++++++
 .../mailets/remoteDelivery/MessageComposer.java |   8 +-
 3 files changed, 110 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/942cdfc6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 39b38d0..f497767 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -51,65 +51,11 @@ import org.apache.mailet.MailAddress;
 import org.apache.mailet.MailetContext;
 import org.slf4j.Logger;
 
-import com.google.common.base.Optional;
 import com.sun.mail.smtp.SMTPTransport;
 
 @SuppressWarnings("deprecation")
 public class DeliveryRunnable implements Runnable {
 
-    private static ExecutionResult success() {
-        return new ExecutionResult(ExecutionState.SUCCESS, Optional.<Exception>absent());
-    }
-
-    private static ExecutionResult temporaryFailure(Exception e) {
-        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.of(e));
-    }
-
-    private static ExecutionResult permanentFailure(Exception e) {
-        return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.of(e));
-    }
-
-    private static ExecutionResult temporaryFailure() {
-        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.<Exception>absent());
-    }
-
-    private static ExecutionResult permanentFailure() {
-        return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.<Exception>absent());
-    }
-
-    private static class ExecutionResult {
-        private final ExecutionState executionState;
-        private final Optional<Exception> exception;
-
-        public ExecutionResult(ExecutionState executionState, Optional<Exception> exception) {
-            this.executionState = executionState;
-            this.exception = exception;
-        }
-
-        public ExecutionState getExecutionState() {
-            return executionState;
-        }
-
-        public Optional<Exception> getException() {
-            return exception;
-        }
-    }
-
-    private enum ExecutionState {
-        SUCCESS,
-        PERMANENT_FAILURE,
-        TEMPORARY_FAILURE
-    }
-
-    private static ExecutionResult onFailure(boolean permanent, Exception exeption) {
-        if (permanent) {
-            return permanentFailure(exeption);
-        } else {
-            return temporaryFailure(exeption);
-        }
-    }
-
-    public static final boolean PERMANENT_FAILURE = true;
     public static final String BIT_MIME_8 = "8BITMIME";
     private final MailQueue queue;
     private final RemoteDeliveryConfiguration configuration;
@@ -251,20 +197,22 @@ public class DeliveryRunnable implements Runnable {
             // or mailbox is full or domain is setup wrong). We fail permanently if this was a 5xx error
 
             boolean isPermanent = '5' == ex.getMessage().charAt(0);
-            logger.debug(messageComposer.composeFailLogMessage(mail, ex, isPermanent));
-            return onFailure(isPermanent, ex);
+            ExecutionResult executionResult = ExecutionResult.onFailure(isPermanent, ex);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
         } catch (Exception ex) {
             logger.error("Generic exception = permanent failure: " + ex.getMessage(), ex);
             // Generic exception = permanent failure
-            logger.debug(messageComposer.composeFailLogMessage(mail, ex, PERMANENT_FAILURE));
-            return permanentFailure(ex);
+            ExecutionResult executionResult = ExecutionResult.permanentFailure(ex);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
         }
     }
 
     private ExecutionResult tryDeliver(Mail mail, Session session) throws MessagingException {
         if (mail.getRecipients().isEmpty()) {
             logger.info("No recipients specified... not sure how this could have happened.");
-            return permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
+            return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
         }
         if (configuration.isDebug()) {
             logger.debug("Attempting to deliver " + mail.getName());
@@ -299,7 +247,7 @@ public class DeliveryRunnable implements Runnable {
         while (targetServers.hasNext()) {
             try {
                 if (tryDeliveryToHost(mail, session, message, addr, targetServers.next())) {
-                    return success();
+                    return ExecutionResult.success();
                 }
             } catch (SendFailedException sfe) {
                 lastError = handleSendFailException(mail, sfe);
@@ -316,7 +264,7 @@ public class DeliveryRunnable implements Runnable {
         if (lastError != null) {
             throw lastError;
         }
-        return temporaryFailure();
+        return ExecutionResult.temporaryFailure();
     }
 
     private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
@@ -382,7 +330,7 @@ public class DeliveryRunnable implements Runnable {
         // Copy the recipients as direct modification may not be possible
         Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());
 
-        ExecutionResult deleteMessage = temporaryFailure();
+        ExecutionResult deleteMessage = ExecutionResult.temporaryFailure();
 
             /*
              * If you send a message that has multiple invalid addresses, you'll
@@ -411,7 +359,7 @@ public class DeliveryRunnable implements Runnable {
                 int returnCode = (Integer) invokeGetter(sfe, "getReturnCode");
                 // If we got an SMTPSendFailedException, use its RetCode to
                 // determine default permanent/temporary failure
-                deleteMessage = onFailure(returnCode >= 500 && returnCode <= 599, sfe);
+                deleteMessage = ExecutionResult.onFailure(returnCode >= 500 && returnCode <= 599, sfe);
             } else {
                 // Sometimes we'll get a normal SendFailedException with
                 // nested SMTPAddressFailedException, so use the latter
@@ -422,7 +370,7 @@ public class DeliveryRunnable implements Runnable {
                     me = (MessagingException) ne;
                     if (me.getClass().getName().endsWith(".SMTPAddressFailedException")) {
                         int returnCode = (Integer) invokeGetter(me, "getReturnCode");
-                        deleteMessage = onFailure(returnCode >= 500 && returnCode <= 599, sfe);
+                        deleteMessage = ExecutionResult.onFailure(returnCode >= 500 && returnCode <= 599, sfe);
                     }
                 }
             }
@@ -457,8 +405,8 @@ public class DeliveryRunnable implements Runnable {
 
                 if (configuration.isDebug())
                     logger.debug("Invalid recipients: " + recipients);
-                logger.debug(messageComposer.composeFailLogMessage(mail, sfe, true));
-                deleteMessage = permanentFailure(sfe);
+                deleteMessage = ExecutionResult.permanentFailure(sfe);
+                logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
             }
         }
 
@@ -484,11 +432,11 @@ public class DeliveryRunnable implements Runnable {
                 if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) {
                     int returnCode = (Integer) invokeGetter(sfe, "getReturnCode");
                     boolean isPermanent = returnCode >= 500 && returnCode <= 599;
-                    logger.debug(messageComposer.composeFailLogMessage(mail, sfe, isPermanent));
-                    deleteMessage = onFailure(isPermanent, sfe);
+                    deleteMessage = ExecutionResult.onFailure(isPermanent, sfe);
+                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
                 } else {
-                    logger.debug(messageComposer.composeFailLogMessage(mail, sfe, false));
-                    deleteMessage = temporaryFailure(sfe);
+                    deleteMessage = ExecutionResult.temporaryFailure(sfe);
+                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
                 }
             }
         }
@@ -615,25 +563,27 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
-        MessagingException messagingException = new MessagingException("Temporary problem looking up mail server for host: " + host + ".  I cannot determine where to send this message.");
-        logger.debug(messageComposer.composeFailLogMessage(mail, messagingException, false));
-        return temporaryFailure(messagingException);
+        ExecutionResult executionResult = ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
+            "up mail server for host: " + host + ".  I cannot determine where to send this message."));
+        logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+        return executionResult;
     }
 
     private ExecutionResult handleNoTargetServer(Mail mail, String host) {
         logger.info("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.";
 
+        MessagingException messagingException = new MessagingException(exceptionBuffer);
         int retry = DeliveryRetriesHelper.retrieveRetries(mail);
         if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
             // The domain has no dns entry.. Return a permanent error
-            MessagingException messagingException = new MessagingException(exceptionBuffer);
-            logger.debug(messageComposer.composeFailLogMessage(mail, messagingException, true));
-            return permanentFailure(messagingException);
+            ExecutionResult executionResult = ExecutionResult.permanentFailure(messagingException);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
         } else {
-            MessagingException messagingException = new MessagingException(exceptionBuffer);
-            logger.debug(messageComposer.composeFailLogMessage(mail, messagingException, false));
-            return temporaryFailure(messagingException);
+            ExecutionResult executionResult = ExecutionResult.temporaryFailure(messagingException);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/942cdfc6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
new file mode 100644
index 0000000..f984fd1
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/ExecutionResult.java
@@ -0,0 +1,76 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import com.google.common.base.Optional;
+
+public class ExecutionResult {
+
+    public enum ExecutionState {
+        SUCCESS,
+        PERMANENT_FAILURE,
+        TEMPORARY_FAILURE
+    }
+
+    public static ExecutionResult success() {
+        return new ExecutionResult(ExecutionState.SUCCESS, Optional.<Exception>absent());
+    }
+
+    public static ExecutionResult temporaryFailure(Exception e) {
+        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.of(e));
+    }
+
+    public static ExecutionResult permanentFailure(Exception e) {
+        return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.of(e));
+    }
+
+    public static ExecutionResult temporaryFailure() {
+        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.<Exception>absent());
+    }
+
+    public static ExecutionResult onFailure(boolean permanent, Exception exeption) {
+        if (permanent) {
+            return permanentFailure(exeption);
+        } else {
+            return temporaryFailure(exeption);
+        }
+    }
+
+    private final ExecutionState executionState;
+    private final Optional<Exception> exception;
+
+    public ExecutionResult(ExecutionState executionState, Optional<Exception> exception) {
+        this.executionState = executionState;
+        this.exception = exception;
+    }
+
+    public ExecutionState getExecutionState() {
+        return executionState;
+    }
+
+    public Optional<Exception> getException() {
+        return exception;
+    }
+
+    public boolean isPermanent() {
+        return executionState == ExecutionState.PERMANENT_FAILURE;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/942cdfc6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
index a323ba9..3e6d861 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MessageComposer.java
@@ -122,12 +122,14 @@ public class MessageComposer {
         }
     }
 
-    public String composeFailLogMessage(Mail mail, Exception ex, boolean permanent) {
+    public String composeFailLogMessage(Mail mail, ExecutionResult executionResult) {
         StringWriter sout = new StringWriter();
         PrintWriter out = new PrintWriter(sout, true);
-        out.print(permanentAsString(permanent) + " exception delivering mail (" + mail.getName() + ")" + retrieveExceptionLog(ex) + ": " );
+        out.print(permanentAsString(executionResult.isPermanent()) + " exception delivering mail (" + mail.getName()
+            + ")" + retrieveExceptionLog(executionResult.getException().orNull()) + ": " );
         if (configuration.isDebug()) {
-            ex.printStackTrace(out);
+            if (executionResult.getException().isPresent())
+                executionResult.getException().get().printStackTrace(out);
         }
         return sout.toString();
     }


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


[31/50] [abbrv] james-project git commit: JAMES-1877 Create mailDelivrer

Posted by ro...@apache.org.
JAMES-1877 Create mailDelivrer

Its responsibility :
 - Resolve MXs addresses for a set of domains
 - Try host to host delivery
 - Report execution


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

Branch: refs/heads/master
Commit: 9790170cd133537194f82b16b8e89f5f16cdb504
Parents: 1467986
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 10:40:19 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:51 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/Bouncer.java         |   4 +-
 .../remoteDelivery/DeliveryRunnable.java        | 434 ++-----------------
 .../mailets/remoteDelivery/MailDelivrer.java    | 399 +++++++++++++++++
 3 files changed, 440 insertions(+), 397 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9790170c/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
index feebf84..da90b08 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
@@ -41,9 +41,9 @@ public class Bouncer {
     private final MailetContext mailetContext;
     private final Logger logger;
 
-    public Bouncer(RemoteDeliveryConfiguration configuration, MessageComposer messageComposer, MailetContext mailetContext, Logger logger) {
+    public Bouncer(RemoteDeliveryConfiguration configuration, MailetContext mailetContext, Logger logger) {
         this.configuration = configuration;
-        this.messageComposer = messageComposer;
+        this.messageComposer = new MessageComposer(configuration);
         this.mailetContext = mailetContext;
         this.logger = logger;
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9790170c/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 6317209..d46a9f5 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -19,98 +19,45 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Date;
-import java.util.Iterator;
 import java.util.concurrent.TimeUnit;
 
-import javax.mail.Address;
-import javax.mail.MessagingException;
-import javax.mail.SendFailedException;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-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.lifecycle.api.LifecycleUtil;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.queue.api.MailPrioritySupport;
 import org.apache.james.queue.api.MailQueue;
-import org.apache.mailet.HostAddress;
 import org.apache.mailet.Mail;
-import org.apache.mailet.MailAddress;
 import org.apache.mailet.MailetContext;
 import org.slf4j.Logger;
 
-@SuppressWarnings("deprecation")
 public class DeliveryRunnable implements Runnable {
 
     private final MailQueue queue;
     private final RemoteDeliveryConfiguration configuration;
-    private final DNSService dnsServer;
     private final Metric outgoingMailsMetric;
     private final Logger logger;
     private final Bouncer bouncer;
-    private final MailDelivrerToHost mailDelivrerToHost;
+    private final MailDelivrer mailDelivrer;
     private final VolatileIsDestroyed volatileIsDestroyed;
-    private final MessageComposer messageComposer;
 
     public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration configuration, DNSService dnsServer, Metric outgoingMailsMetric,
                             Logger logger, MailetContext mailetContext, VolatileIsDestroyed volatileIsDestroyed) {
         this.queue = queue;
         this.configuration = configuration;
-        this.dnsServer = dnsServer;
         this.outgoingMailsMetric = outgoingMailsMetric;
         this.logger = logger;
         this.volatileIsDestroyed = volatileIsDestroyed;
-        this.messageComposer = new MessageComposer(configuration);
-        this.bouncer = new Bouncer(configuration, messageComposer, mailetContext, logger);
-        this.mailDelivrerToHost = new MailDelivrerToHost(configuration, mailetContext, logger);
+        this.bouncer = new Bouncer(configuration, mailetContext, logger);
+        MailDelivrerToHost mailDelivrerToHost = new MailDelivrerToHost(configuration, mailetContext, logger);
+        this.mailDelivrer = new MailDelivrer(configuration, mailDelivrerToHost, dnsServer, logger);
     }
 
-    /**
-     * Handles checking the outgoing spool for new mail and delivering them if
-     * there are any
-     */
     @Override
     public void run() {
         try {
             while (!Thread.interrupted() && !volatileIsDestroyed.isDestroyed()) {
-                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.
-                    MailQueue.MailQueueItem queueItem = queue.deQueue();
-                    Mail mail = queueItem.getMail();
-
-                    try {
-                        if (configuration.isDebug()) {
-                            logger.debug(Thread.currentThread().getName() + " will process mail " + mail.getName());
-                        }
-                        attemptDelivery(mail);
-                        LifecycleUtil.dispose(mail);
-                        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!
-                        logger.error("Exception caught in RemoteDelivery.run()", e);
-                        LifecycleUtil.dispose(mail);
-                        queueItem.done(false);
-                    }
-
-                } catch (Throwable e) {
-                    if (!volatileIsDestroyed.isDestroyed()) {
-                        logger.error("Exception caught in RemoteDelivery.run()", e);
-                    }
-                }
+                runStep();
             }
         } finally {
             // Restore the thread state to non-interrupted.
@@ -118,8 +65,41 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
+    private void runStep() {
+        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.
+            MailQueue.MailQueueItem queueItem = queue.deQueue();
+            Mail mail = queueItem.getMail();
+
+            try {
+                if (configuration.isDebug()) {
+                    logger.debug(Thread.currentThread().getName() + " will process mail " + mail.getName());
+                }
+                attemptDelivery(mail);
+                LifecycleUtil.dispose(mail);
+                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!
+                logger.error("Exception caught in RemoteDelivery.run()", e);
+                LifecycleUtil.dispose(mail);
+                queueItem.done(false);
+            }
+
+        } catch (Throwable e) {
+            if (!volatileIsDestroyed.isDestroyed()) {
+                logger.error("Exception caught in RemoteDelivery.run()", e);
+            }
+        }
+    }
+
     private void attemptDelivery(Mail mail) throws MailQueue.MailQueueException {
-        ExecutionResult executionResult = deliver(mail);
+        ExecutionResult executionResult = mailDelivrer.deliver(mail);
         switch (executionResult.getExecutionState()) {
             case SUCCESS:
                 outgoingMailsMetric.increment();
@@ -163,346 +143,10 @@ public class DeliveryRunnable implements Runnable {
         queue.enQueue(mail, delay, TimeUnit.MILLISECONDS);
     }
 
-    /**
-     * 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 ExecutionResult deliver(Mail mail) {
-        try {
-            return tryDeliver(mail);
-        } catch (SendFailedException sfe) {
-            return handleSenderFailedException(mail, sfe);
-        } 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
-
-            boolean isPermanent = '5' == ex.getMessage().charAt(0);
-            ExecutionResult executionResult = ExecutionResult.onFailure(isPermanent, ex);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
-        } catch (Exception ex) {
-            logger.error("Generic exception = permanent failure: " + ex.getMessage(), ex);
-            // Generic exception = permanent failure
-            ExecutionResult executionResult = ExecutionResult.permanentFailure(ex);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
-        }
-    }
-
-    private ExecutionResult tryDeliver(Mail mail) throws MessagingException {
-        if (mail.getRecipients().isEmpty()) {
-            logger.info("No recipients specified... not sure how this could have happened.");
-            return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
-        }
-        if (configuration.isDebug()) {
-            logger.debug("Attempting to deliver " + mail.getName());
-        }
-
-        // Figure out which servers to try to send to. This collection
-        // will hold all the possible target servers
-        Iterator<HostAddress> targetServers;
-        if (configuration.getGatewayServer().isEmpty()) {
-            MailAddress rcpt = mail.getRecipients().iterator().next();
-            String host = rcpt.getDomain();
-
-            // Lookup the possible targets
-            try {
-                targetServers = new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
-            } catch (TemporaryResolutionException e) {
-                return handleTemporaryResolutionException(mail, host);
-            }
-            if (!targetServers.hasNext()) {
-                return handleNoTargetServer(mail, host);
-            }
-        } else {
-            targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
-        }
-
-        return doDeliver(mail, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
-    }
-
-    private ExecutionResult doDeliver(Mail mail, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
-        MessagingException lastError = null;
-
-        while (targetServers.hasNext()) {
-            try {
-                if (mailDelivrerToHost.tryDeliveryToHost(mail, message, addr, targetServers.next())) {
-                    return ExecutionResult.success();
-                }
-            } catch (SendFailedException sfe) {
-                lastError = handleSendFailException(mail, sfe);
-            } catch (MessagingException me) {
-                lastError = handleMessagingException(mail, me);
-                if (configuration.isDebug()) {
-                    logger.debug(me.getMessage(), me.getCause());
-                } else {
-                    logger.info(me.getMessage());
-                }
-            }
-        }
-        // 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;
-        }
-        return ExecutionResult.temporaryFailure();
-    }
-
-    private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
-        logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
-        if ((me.getNextException() != null) && (me.getNextException() instanceof 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.
-            return me;
-        } else {
-            // 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;
-        }
-    }
-
-    private ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {
-        logSendFailedException(sfe);
-
-        // Copy the recipients as direct modification may not be possible
-        Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());
-
-        ExecutionResult deleteMessage = ExecutionResult.temporaryFailure();
-        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
-
-            /*
-             * 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
-             */
-        if (enhancedMessagingException.hasReturnCode()) {
-            if (enhancedMessagingException.isServerError()) {
-                deleteMessage = ExecutionResult.permanentFailure(sfe);
-            } else {
-                deleteMessage = ExecutionResult.temporaryFailure(sfe);
-            }
-        }
-
-        // log the original set of intended recipients
-        if (configuration.isDebug())
-            logger.debug("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.
-                        logger.debug("Can't parse invalid address: " + pe.getMessage());
-                    }
-                }
-                // Set the recipients for the mail
-                mail.setRecipients(recipients);
-
-                if (configuration.isDebug())
-                    logger.debug("Invalid recipients: " + recipients);
-                deleteMessage = ExecutionResult.permanentFailure(sfe);
-                logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
-            }
-        }
-
-        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.
-                        logger.debug("Can't parse unsent address: " + pe.getMessage());
-                    }
-                }
-                // Set the recipients for the mail
-                mail.setRecipients(recipients);
-                if (configuration.isDebug())
-                    logger.debug("Unsent recipients: " + recipients);
-
-                if (enhancedMessagingException.hasReturnCode()) {
-                    boolean isPermanent = enhancedMessagingException.isServerError();
-                    deleteMessage = ExecutionResult.onFailure(isPermanent, sfe);
-                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
-                } else {
-                    deleteMessage = ExecutionResult.temporaryFailure(sfe);
-                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
-                }
-            }
-        }
-
-
-        return deleteMessage;
-    }
-
-    private MessagingException handleSendFailException(Mail mail, SendFailedException sfe) throws SendFailedException {
-        logSendFailedException(sfe);
-
-        if (sfe.getValidSentAddresses() != null) {
-            Address[] validSent = sfe.getValidSentAddresses();
-            if (validSent.length > 0) {
-                logger.debug( "Mail (" + mail.getName() + ") sent successfully for " + Arrays.asList(validSent));
-            }
-        }
-
-        /*
-        * SMTPSendFailedException introduced in JavaMail 1.3.2, and
-        * provides detailed protocol reply code for the operation
-        */
-        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
-        if (enhancedMessagingException.isServerError()) {
-            throw sfe;
-        }
-
-        if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
-            if (configuration.isDebug())
-                logger.debug("Send failed, " + sfe.getValidUnsentAddresses().length + " valid addresses remain, continuing with any other servers");
-            return sfe;
-        } else {
-            // There are no valid addresses left to send, so rethrow
-            throw sfe;
-        }
-    }
-
-    private InternetAddress[] convertToInetAddr(Collection<MailAddress> recipients) {
-        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();
-        }
-        return addr;
-    }
-
-    private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
-        ExecutionResult executionResult = ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
-            "up mail server for host: " + host + ".  I cannot determine where to send this message."));
-        logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-        return executionResult;
-    }
-
-    private ExecutionResult handleNoTargetServer(Mail mail, String host) {
-        logger.info("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.";
-
-        MessagingException messagingException = new MessagingException(exceptionBuffer);
-        int retry = DeliveryRetriesHelper.retrieveRetries(mail);
-        if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
-            // The domain has no dns entry.. Return a permanent error
-            ExecutionResult executionResult = ExecutionResult.permanentFailure(messagingException);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
-        } else {
-            ExecutionResult executionResult = ExecutionResult.temporaryFailure(messagingException);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
-        }
-    }
-
     private long getNextDelay(int retry_count) {
         if (retry_count > configuration.getDelayTimes().size()) {
             return Delay.DEFAULT_DELAY_TIME;
         }
         return configuration.getDelayTimes().get(retry_count - 1);
     }
-
-    private void logSendFailedException(SendFailedException sfe) {
-        if (configuration.isDebug()) {
-            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
-            if (enhancedMessagingException.hasReturnCode()) {
-                logger.debug("SMTP SEND FAILED:");
-                logger.debug(sfe.toString());
-                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
-                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
-                logger.debug("  Response: " + sfe.getMessage());
-            } else {
-                logger.debug("Send failed: " + sfe.toString());
-            }
-            logLevels(sfe);
-        }
-    }
-
-    private void logLevels(MessagingException me) {
-        Exception ne;
-        while ((ne = me.getNextException()) != null && ne instanceof MessagingException) {
-            me = (MessagingException) ne;
-            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me);
-            if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
-                logger.debug("ADDRESS " + enhancedMessagingException.computeAction() + ":");
-                logger.debug(me.toString());
-                logger.debug("  Address: " + enhancedMessagingException.computeAddress());
-                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
-                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
-                logger.debug("  Response: " + me.getMessage());
-            }
-        }
-    }
-
-    /**
-     * 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) {
-        return new MXHostAddressIterator(gatewayServers.iterator(), dnsServer, false, logger);
-    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9790170c/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
new file mode 100644
index 0000000..bcfe330
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -0,0 +1,399 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.mail.Address;
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+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.mailet.HostAddress;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailAddress;
+import org.slf4j.Logger;
+
+import com.google.common.base.Optional;
+
+public class MailDelivrer {
+
+    private final RemoteDeliveryConfiguration configuration;
+    private final MailDelivrerToHost mailDelivrerToHost;
+    private final DNSService dnsServer;
+    private final MessageComposer messageComposer;
+    private final Logger logger;
+
+    public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Logger logger) {
+        this.configuration = configuration;
+        this.mailDelivrerToHost = mailDelivrerToHost;
+        this.dnsServer = dnsServer;
+        this.messageComposer = new MessageComposer(configuration);
+        this.logger = logger;
+    }
+
+    /**
+     * 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
+     */
+    public ExecutionResult deliver(Mail mail) {
+        try {
+            return tryDeliver(mail);
+        } catch (SendFailedException sfe) {
+            return handleSenderFailedException(mail, sfe);
+        } 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
+
+            boolean isPermanent = '5' == ex.getMessage().charAt(0);
+            ExecutionResult executionResult = ExecutionResult.onFailure(isPermanent, ex);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
+        } catch (Exception ex) {
+            logger.error("Generic exception = permanent failure: " + ex.getMessage(), ex);
+            // Generic exception = permanent failure
+            ExecutionResult executionResult = ExecutionResult.permanentFailure(ex);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private ExecutionResult tryDeliver(Mail mail) throws MessagingException {
+        if (mail.getRecipients().isEmpty()) {
+            logger.info("No recipients specified... not sure how this could have happened.");
+            return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
+        }
+        if (configuration.isDebug()) {
+            logger.debug("Attempting to deliver " + mail.getName());
+        }
+
+        // Figure out which servers to try to send to. This collection
+        // will hold all the possible target servers
+        Iterator<HostAddress> targetServers;
+        if (configuration.getGatewayServer().isEmpty()) {
+            MailAddress rcpt = mail.getRecipients().iterator().next();
+            String host = rcpt.getDomain();
+
+            // Lookup the possible targets
+            try {
+                targetServers = new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
+            } catch (TemporaryResolutionException e) {
+                return handleTemporaryResolutionException(mail, host);
+            }
+            if (!targetServers.hasNext()) {
+                return handleNoTargetServer(mail, host);
+            }
+        } else {
+            targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
+        }
+
+        return doDeliver(mail, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
+    }
+
+    @SuppressWarnings("deprecation")
+    private ExecutionResult doDeliver(Mail mail, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
+        MessagingException lastError = null;
+
+        while (targetServers.hasNext()) {
+            try {
+                if (mailDelivrerToHost.tryDeliveryToHost(mail, message, addr, targetServers.next())) {
+                    return ExecutionResult.success();
+                }
+            } catch (SendFailedException sfe) {
+                lastError = handleSendFailException(mail, sfe);
+            } catch (MessagingException me) {
+                lastError = handleMessagingException(mail, me);
+                if (configuration.isDebug()) {
+                    logger.debug(me.getMessage(), me.getCause());
+                } else {
+                    logger.info(me.getMessage());
+                }
+            }
+        }
+        // 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;
+        }
+        return ExecutionResult.temporaryFailure();
+    }
+
+    private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
+        logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
+        if ((me.getNextException() != null) && (me.getNextException() instanceof 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.
+            return me;
+        } else {
+            // 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;
+        }
+    }
+
+    private ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {
+        logSendFailedException(sfe);
+
+        // Copy the recipients as direct modification may not be possible
+        Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());
+
+        ExecutionResult deleteMessage = ExecutionResult.temporaryFailure();
+        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+
+            /*
+             * 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
+             */
+        if (enhancedMessagingException.hasReturnCode() || enhancedMessagingException.hasNestedReturnCode()) {
+            if (enhancedMessagingException.isServerError()) {
+                deleteMessage = ExecutionResult.permanentFailure(sfe);
+            } else {
+                deleteMessage = ExecutionResult.temporaryFailure(sfe);
+            }
+        }
+
+        // log the original set of intended recipients
+        if (configuration.isDebug())
+            logger.debug("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.
+                        logger.debug("Can't parse invalid address: " + pe.getMessage());
+                    }
+                }
+                // Set the recipients for the mail
+                mail.setRecipients(recipients);
+
+                if (configuration.isDebug())
+                    logger.debug("Invalid recipients: " + recipients);
+                deleteMessage = ExecutionResult.permanentFailure(sfe);
+                logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
+            }
+        }
+
+        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.
+                        logger.debug("Can't parse unsent address: " + pe.getMessage());
+                    }
+                }
+                // Set the recipients for the mail
+                mail.setRecipients(recipients);
+                if (configuration.isDebug())
+                    logger.debug("Unsent recipients: " + recipients);
+
+                if (enhancedMessagingException.hasReturnCode()) {
+                    boolean isPermanent = enhancedMessagingException.isServerError();
+                    deleteMessage = ExecutionResult.onFailure(isPermanent, sfe);
+                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
+                } else {
+                    deleteMessage = ExecutionResult.temporaryFailure(sfe);
+                    logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
+                }
+            }
+        }
+
+
+        return deleteMessage;
+    }
+
+    private MessagingException handleSendFailException(Mail mail, SendFailedException sfe) throws SendFailedException {
+        logSendFailedException(sfe);
+
+        if (sfe.getValidSentAddresses() != null) {
+            Address[] validSent = sfe.getValidSentAddresses();
+            if (validSent.length > 0) {
+                logger.debug( "Mail (" + mail.getName() + ") sent successfully for " + Arrays.asList(validSent));
+            }
+        }
+
+        /*
+        * SMTPSendFailedException introduced in JavaMail 1.3.2, and
+        * provides detailed protocol reply code for the operation
+        */
+        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+        if (enhancedMessagingException.isServerError()) {
+            throw sfe;
+        }
+
+        if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
+            if (configuration.isDebug())
+                logger.debug("Send failed, " + sfe.getValidUnsentAddresses().length + " valid addresses remain, continuing with any other servers");
+            return sfe;
+        } else {
+            // There are no valid addresses left to send, so rethrow
+            throw sfe;
+        }
+    }
+
+    private InternetAddress[] convertToInetAddr(Collection<MailAddress> recipients) {
+        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();
+        }
+        return addr;
+    }
+
+    private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
+        ExecutionResult executionResult = ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
+            "up mail server for host: " + host + ".  I cannot determine where to send this message."));
+        logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+        return executionResult;
+    }
+
+    private ExecutionResult handleNoTargetServer(Mail mail, String host) {
+        logger.info("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.";
+
+        MessagingException messagingException = new MessagingException(exceptionBuffer);
+        int retry = DeliveryRetriesHelper.retrieveRetries(mail);
+        if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
+            // The domain has no dns entry.. Return a permanent error
+            ExecutionResult executionResult = ExecutionResult.permanentFailure(messagingException);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
+        } else {
+            ExecutionResult executionResult = ExecutionResult.temporaryFailure(messagingException);
+            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+            return executionResult;
+        }
+    }
+
+    /**
+     * 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
+     */
+    @SuppressWarnings("deprecation")
+    private Iterator<HostAddress> getGatewaySMTPHostAddresses(Collection<String> gatewayServers) {
+        return new MXHostAddressIterator(gatewayServers.iterator(), dnsServer, false, logger);
+    }
+
+    private void logSendFailedException(SendFailedException sfe) {
+        if (configuration.isDebug()) {
+            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+            if (enhancedMessagingException.hasReturnCode()) {
+                logger.debug("SMTP SEND FAILED:");
+                logger.debug(sfe.toString());
+                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
+                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
+                logger.debug("  Response: " + sfe.getMessage());
+            } else {
+                logger.debug("Send failed: " + sfe.toString());
+            }
+            logLevels(sfe);
+        }
+    }
+
+    private void logLevels(MessagingException me) {
+        Exception ne;
+        while ((ne = me.getNextException()) != null && ne instanceof MessagingException) {
+            me = (MessagingException) ne;
+            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me);
+            if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
+                logger.debug("ADDRESS " + enhancedMessagingException.computeAction() + ":");
+                logger.debug(me.toString());
+                logger.debug("  Address: " + enhancedMessagingException.computeAddress());
+                logger.debug("  Command: " + enhancedMessagingException.computeCommand());
+                logger.debug("  RetCode: " + enhancedMessagingException.getReturnCode());
+                logger.debug("  Response: " + me.getMessage());
+            }
+        }
+    }
+}


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


[30/50] [abbrv] james-project git commit: JAMES-1877 Simplify handleMessagingException of DeliveryRunnable

Posted by ro...@apache.org.
JAMES-1877 Simplify handleMessagingException of DeliveryRunnable


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

Branch: refs/heads/master
Commit: 1467986ea395c04603b02ac09c692cc99745daca
Parents: f567a5a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 10:26:14 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:51 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/remoteDelivery/DeliveryRunnable.java | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1467986e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 37ae531..6317209 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -266,14 +266,13 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
-        MessagingException lastError;// MessagingException are horribly difficult to figure out what actually happened.
         logger.debug("Exception delivering message (" + mail.getName() + ") - " + me.getMessage());
         if ((me.getNextException() != null) && (me.getNextException() instanceof 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;
+            return me;
         } else {
             // This was not a connection or I/O error particular to one
             // SMTP server of an MX set. Instead, it is almost certainly
@@ -284,7 +283,6 @@ public class DeliveryRunnable implements Runnable {
             // severity.
             throw me;
         }
-        return lastError;
     }
 
     private ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {


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


[24/50] [abbrv] james-project git commit: JAMES-1877 Adding an enumeration for keeping track of the status of Delivery

Posted by ro...@apache.org.
JAMES-1877 Adding an enumeration for keeping track of the status of Delivery


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

Branch: refs/heads/master
Commit: c8a3dd97832007bf7403b8670b5ce499efc94ff1
Parents: f5fc475
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 1 17:30:55 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:50 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 115 ++++++++++++++-----
 1 file changed, 88 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c8a3dd97/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index f6763ef..45169a1 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -57,6 +57,58 @@ import com.sun.mail.smtp.SMTPTransport;
 @SuppressWarnings("deprecation")
 public class DeliveryRunnable implements Runnable {
 
+    private static ExecutionResult success() {
+        return new ExecutionResult(ExecutionState.SUCCESS, Optional.<Exception>absent());
+    }
+
+    private static ExecutionResult temporaryFailure(Exception e) {
+        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.of(e));
+    }
+
+    private static ExecutionResult permanentFailure(Exception e) {
+        return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.of(e));
+    }
+
+    private static ExecutionResult temporaryFailure() {
+        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.<Exception>absent());
+    }
+
+    private static ExecutionResult permanentFailure() {
+        return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.<Exception>absent());
+    }
+
+    private static class ExecutionResult {
+        private final ExecutionState executionState;
+        private final Optional<Exception> exception;
+
+        public ExecutionResult(ExecutionState executionState, Optional<Exception> exception) {
+            this.executionState = executionState;
+            this.exception = exception;
+        }
+
+        public ExecutionState getExecutionState() {
+            return executionState;
+        }
+
+        public Optional<Exception> getException() {
+            return exception;
+        }
+    }
+
+    private enum ExecutionState {
+        SUCCESS,
+        PERMANENT_FAILURE,
+        TEMPORARY_FAILURE
+    }
+
+    private static ExecutionResult onFailure(boolean permanent, Exception exeption) {
+        if (permanent) {
+            return permanentFailure(exeption);
+        } else {
+            return temporaryFailure(exeption);
+        }
+    }
+
     public static final boolean PERMANENT_FAILURE = true;
     public static final String BIT_MIME_8 = "8BITMIME";
     private final MailQueue queue;
@@ -128,15 +180,25 @@ public class DeliveryRunnable implements Runnable {
     }
 
     private void attemptDelivery(Session session, Mail mail) throws MailQueue.MailQueueException {
-        if (!deliver(mail, session)) {
-            // Something happened that will delay delivery. Store it back in the retry repository.
-            long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
-
-            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);
+        ExecutionResult executionResult = deliver(mail, session);
+        switch (executionResult.getExecutionState()) {
+            case SUCCESS:
+                outgoingMailsMetric.increment();
+                break;
+            case TEMPORARY_FAILURE:
+                // TODO move the incrementing of retries here instead of in fail message
+                // Something happened that will delay delivery. Store it back in the retry repository.
+                long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
+
+                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);
+                break;
+            case PERMANENT_FAILURE:
+                // TODO move boucing logic here
+                break;
         }
     }
 
@@ -151,7 +213,7 @@ public class DeliveryRunnable implements Runnable {
      * @return boolean Whether the delivery was successful and the message can
      *         be deleted
      */
-    private boolean deliver(Mail mail, Session session) {
+    private ExecutionResult deliver(Mail mail, Session session) {
         try {
             return Optional.fromNullable(tryDeliver(mail, session))
                 .or(failMessage(mail, new MessagingException("No mail server(s) available at this time."), false));
@@ -180,10 +242,10 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private Boolean tryDeliver(Mail mail, Session session) throws MessagingException {
+    private ExecutionResult tryDeliver(Mail mail, Session session) throws MessagingException {
         if (mail.getRecipients().isEmpty()) {
             logger.info("No recipients specified... not sure how this could have happened.");
-            return true;
+            return permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
         }
         if (configuration.isDebug()) {
             logger.debug("Attempting to deliver " + mail.getName());
@@ -212,20 +274,20 @@ public class DeliveryRunnable implements Runnable {
         return doDeliver(mail, session, mail.getMessage(), convertToInetAddr(mail.getRecipients()), targetServers);
     }
 
-    private Boolean doDeliver(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
+    private ExecutionResult doDeliver(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
         MessagingException lastError = null;
 
         while (targetServers.hasNext()) {
             try {
                 if (tryDeliveryToHost(mail, session, message, addr, targetServers.next())) {
-                    return true;
+                    return success();
                 }
             } catch (SendFailedException sfe) {
                 lastError = handleSendFailException(mail, sfe);
             } catch (MessagingException me) {
                 lastError = handleMessagingException(mail, 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
@@ -235,7 +297,7 @@ public class DeliveryRunnable implements Runnable {
         if (lastError != null) {
             throw lastError;
         }
-        return null;
+        return temporaryFailure();
     }
 
     private boolean tryDeliveryToHost(Mail mail, Session session, MimeMessage message, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
@@ -269,7 +331,6 @@ public class DeliveryRunnable implements Runnable {
             success = true;
             logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
                 " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
-            outgoingMailsMetric.increment();
         } finally {
             closeTransport(mail, outgoingMailServer, transport);
         }
@@ -298,13 +359,13 @@ public class DeliveryRunnable implements Runnable {
         return lastError;
     }
 
-    private boolean handleSenderFailedException(Mail mail, SendFailedException sfe) {
+    private ExecutionResult handleSenderFailedException(Mail mail, 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;
+        ExecutionResult deleteMessage = temporaryFailure();
 
             /*
              * If you send a message that has multiple invalid addresses, you'll
@@ -333,7 +394,7 @@ public class DeliveryRunnable implements Runnable {
                 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);
+                deleteMessage = onFailure(returnCode >= 500 && returnCode <= 599, sfe);
             } else {
                 // Sometimes we'll get a normal SendFailedException with
                 // nested SMTPAddressFailedException, so use the latter
@@ -344,7 +405,7 @@ public class DeliveryRunnable implements Runnable {
                     me = (MessagingException) ne;
                     if (me.getClass().getName().endsWith(".SMTPAddressFailedException")) {
                         int returnCode = (Integer) invokeGetter(me, "getReturnCode");
-                        deleteMessage = (returnCode >= 500 && returnCode <= 599);
+                        deleteMessage = onFailure(returnCode >= 500 && returnCode <= 599, sfe);
                     }
                 }
             }
@@ -531,7 +592,7 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private boolean handleTemporaryResolutionException(Mail mail, String host) {
+    private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
         logger.info("Temporary problem looking up mail server for host: " + host);
         // temporary problems
         return failMessage(mail,
@@ -539,7 +600,7 @@ public class DeliveryRunnable implements Runnable {
             false);
     }
 
-    private boolean handleNoTargetServer(Mail mail, String host) {
+    private ExecutionResult handleNoTargetServer(Mail mail, String host) {
         logger.info("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.";
 
@@ -632,7 +693,7 @@ public class DeliveryRunnable implements Runnable {
      * @param permanent
      * @return boolean Whether the message failed fully and can be deleted
      */
-    private boolean failMessage(Mail mail, Exception ex, boolean permanent) {
+    private ExecutionResult failMessage(Mail mail, Exception ex, boolean permanent) {
         logger.debug(messageComposer.composeFailLogMessage(mail, ex, permanent));
         if (!permanent) {
             if (!mail.getState().equals(Mail.ERROR)) {
@@ -647,7 +708,7 @@ public class DeliveryRunnable implements Runnable {
                 logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
                 DeliveryRetriesHelper.incrementRetries(mail);
                 mail.setLastUpdated(new Date());
-                return false;
+                return temporaryFailure();
             } else {
                 logger.debug("Bouncing message " + mail.getName() + " after " + retries + " retries");
             }
@@ -655,7 +716,7 @@ public class DeliveryRunnable implements Runnable {
 
         if (mail.getSender() == null) {
             logger.debug("Null Sender: no bounce will be generated for " + mail.getName());
-            return true;
+            return permanentFailure(ex);
         }
 
         if (configuration.getBounceProcessor() != null) {
@@ -674,7 +735,7 @@ public class DeliveryRunnable implements Runnable {
             // do an old style bounce
             bounce(mail, ex);
         }
-        return true;
+        return permanentFailure(ex);
     }
 
 


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


[46/50] [abbrv] james-project git commit: JAMES-1877 Provide tests for DNS error handling

Posted by ro...@apache.org.
JAMES-1877 Provide tests for DNS error handling


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

Branch: refs/heads/master
Commit: 9898e18e475c7a672ba8e88c8fa4c1504d3c2531
Parents: 70ad233
Author: Benoit Tellier <bt...@linagora.com>
Authored: Wed Dec 7 10:53:01 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:27 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/MailDelivrer.java    |  33 +++---
 .../remoteDelivery/MailDelivrerTest.java        | 109 ++++++++++++++++++-
 2 files changed, 119 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9898e18e/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index e6f1ec3..27f66aa 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -49,11 +49,16 @@ public class MailDelivrer {
     private final Logger logger;
 
     public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Bouncer bouncer, Logger logger) {
+        this(configuration, mailDelivrerToHost, new DnsHelper(dnsServer, configuration, logger), bouncer, logger);
+    }
+
+    @VisibleForTesting
+    MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DnsHelper dnsHelper, Bouncer bouncer, Logger logger) {
         this.configuration = configuration;
         this.mailDelivrerToHost = mailDelivrerToHost;
-        this.bouncer = bouncer;
-        this.dnsHelper = new DnsHelper(dnsServer, configuration, logger);
+        this.dnsHelper = dnsHelper;
         this.messageComposer = new MessageComposer(configuration);
+        this.bouncer = bouncer;
         this.logger = logger;
     }
 
@@ -116,7 +121,8 @@ public class MailDelivrer {
             }
             return doDeliver(mail, mail.getMessage(), InternetAddressConverter.convert(mail.getRecipients()), targetServers);
         } catch (TemporaryResolutionException e) {
-            return handleTemporaryResolutionException(mail, host);
+            return logAndReturn(mail, ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
+                "up mail server for host: " + host + ".  I cannot determine where to send this message.")));
         }
     }
 
@@ -252,28 +258,15 @@ public class MailDelivrer {
         }
     }
 
-    private ExecutionResult handleTemporaryResolutionException(Mail mail, String host) {
-        ExecutionResult executionResult = ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
-            "up mail server for host: " + host + ".  I cannot determine where to send this message."));
-        logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-        return executionResult;
-    }
-
     private ExecutionResult handleNoTargetServer(Mail mail, String host) {
         logger.info("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.";
-
-        MessagingException messagingException = new MessagingException(exceptionBuffer);
+        MessagingException messagingException = new MessagingException("There are no DNS entries for the hostname " + host + ".  I cannot determine where to send this message.");
         int retry = DeliveryRetriesHelper.retrieveRetries(mail);
+        System.out.println("retyry " + retry);
         if (retry == 0 || retry > configuration.getDnsProblemRetry()) {
-            // The domain has no dns entry.. Return a permanent error
-            ExecutionResult executionResult = ExecutionResult.permanentFailure(messagingException);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
+            return logAndReturn(mail, ExecutionResult.permanentFailure(messagingException));
         } else {
-            ExecutionResult executionResult = ExecutionResult.temporaryFailure(messagingException);
-            logger.debug(messageComposer.composeFailLogMessage(mail, executionResult));
-            return executionResult;
+            return logAndReturn(mail, ExecutionResult.temporaryFailure(messagingException));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/9898e18e/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
index 477d945..27de817 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrerTest.java
@@ -19,24 +19,31 @@
 
 package org.apache.james.transport.mailets.remoteDelivery;
 
-import static org.mockito.Mockito.mock;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 import javax.mail.Address;
 import javax.mail.SendFailedException;
 import javax.mail.internet.InternetAddress;
 
-import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.dnsservice.api.TemporaryResolutionException;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.mailet.HostAddress;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.MailAddressFixture;
 import org.apache.mailet.base.test.FakeMail;
+import org.apache.mailet.base.test.FakeMailetConfig;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.UnmodifiableIterator;
 import com.sun.mail.smtp.SMTPSenderFailedException;
 
 public class MailDelivrerTest {
@@ -44,11 +51,13 @@ public class MailDelivrerTest {
 
     private MailDelivrer testee;
     private Bouncer bouncer;
+    private DnsHelper dnsHelper;
 
     @Before
     public void setUp() {
         bouncer = mock(Bouncer.class);
-        testee = new MailDelivrer(mock(RemoteDeliveryConfiguration.class), mock(MailDelivrerToHost.class), mock(DNSService.class), bouncer, LOGGER);
+        dnsHelper = mock(DnsHelper.class);
+        testee = new MailDelivrer(mock(RemoteDeliveryConfiguration.class), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
     }
 
     @Test
@@ -200,4 +209,98 @@ public class MailDelivrerTest {
         verify(bouncer).bounce(mail, sfe);
         verifyNoMoreInteractions(bouncer);
     }
+
+    @Test
+    public void deliverShouldReturnTemporaryFailureOnTemporaryResolutionException() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenThrow(new TemporaryResolutionException());
+
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.TEMPORARY_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    @Ignore("Fails if first delivery attempt")
+    public void deliverShouldReturnTemporaryErrorWhenFirstDNSProblem() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
+            .build();
+        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
+
+        UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
+
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.TEMPORARY_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldReturnTemporaryErrorWhenToleratedDNSProblem() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
+            .build();
+        DeliveryRetriesHelper.incrementRetries(mail);
+        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
+
+        UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
+
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.TEMPORARY_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    @Ignore("One more failure is tolerated than specified by the configuration")
+    public void deliverShouldReturnPermanentErrorWhenLimitDNSProblemReached() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
+            .build();
+        DeliveryRetriesHelper.incrementRetries(mail);
+        DeliveryRetriesHelper.incrementRetries(mail);
+        DeliveryRetriesHelper.incrementRetries(mail);
+        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
+
+        UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
+
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.PERMANENT_FAILURE);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deliverShouldReturnPermanentErrorWhenLimitDNSProblemExceeded() throws Exception {
+        Mail mail = FakeMail.builder().recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.OTHER_AT_JAMES).build();
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .setProperty(RemoteDeliveryConfiguration.DELIVERY_THREADS, "1")
+            .setProperty(RemoteDeliveryConfiguration.MAX_DNS_PROBLEM_RETRIES, "3")
+            .build();
+        DeliveryRetriesHelper.incrementRetries(mail);
+        DeliveryRetriesHelper.incrementRetries(mail);
+        DeliveryRetriesHelper.incrementRetries(mail);
+        DeliveryRetriesHelper.incrementRetries(mail);
+        testee = new MailDelivrer(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)), mock(MailDelivrerToHost.class), dnsHelper, bouncer, LOGGER);
+
+        UnmodifiableIterator<HostAddress> empty = ImmutableList.<HostAddress>of().iterator();
+        when(dnsHelper.retrieveHostAddressIterator(MailAddressFixture.JAMES_APACHE_ORG)).thenReturn(empty);
+
+        ExecutionResult executionResult = testee.deliver(mail);
+
+        assertThat(executionResult.getExecutionState()).isEqualTo(ExecutionResult.ExecutionState.PERMANENT_FAILURE);
+    }
+
 }


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


[04/50] [abbrv] james-project git commit: JAMES-1877 Extract Delay

Posted by ro...@apache.org.
JAMES-1877 Extract Delay


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

Branch: refs/heads/master
Commit: 1193b6b4a542a0cd61bd2e7b74567b3a92207554
Parents: 03fc53a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Nov 29 10:56:27 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 14:35:31 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 118 +------------
 .../transport/mailets/remoteDelivery/Delay.java | 166 +++++++++++++++++++
 .../mailets/remoteDelivery/DelayTest.java       |  96 +++++++++++
 3 files changed, 264 insertions(+), 116 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1193b6b4/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 89a4987..b23f79d 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
@@ -39,8 +39,6 @@ import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.inject.Inject;
 import javax.mail.Address;
@@ -65,10 +63,9 @@ 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.HeloNameProvider;
 import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
-import org.apache.james.transport.util.Patterns;
-import org.apache.james.util.TimeConverter;
 import org.apache.mailet.HostAddress;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
@@ -151,17 +148,6 @@ import com.sun.mail.smtp.SMTPTransport;
 @SuppressWarnings("deprecation")
 public class RemoteDelivery extends GenericMailet implements Runnable {
 
-    /**
-     * Default Delay Time (Default is 6*60*60*1000 Milliseconds (6 hours)).
-     */
-    private static final long DEFAULT_DELAY_TIME = 21600000;
-
-    /**
-     * Pattern to match [attempts*]delay[units].
-     */
-    private static final String PATTERN_STRING = "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";
-
-    private static final Pattern PATTERN = Patterns.compilePatternUncheckedException(PATTERN_STRING);
     private static final String OUTGOING_MAILS = "outgoingMails";
 
     private final DNSService dnsServer;
@@ -502,111 +488,11 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
      */
     private long getNextDelay(int retry_count) {
         if (retry_count > delayTimes.length) {
-            return DEFAULT_DELAY_TIME;
+            return Delay.DEFAULT_DELAY_TIME;
         }
         return delayTimes[retry_count - 1];
     }
 
-    /**
-     * This class is used to hold a delay time and its corresponding number of
-     * retries.
-     */
-    private final static class Delay {
-        private int attempts = 1;
-
-        private long delayTime = DEFAULT_DELAY_TIME;
-
-        /**
-         * <p>
-         * This constructor expects Strings of the form
-         * "[attempt\*]delaytime[unit]".
-         * </p>
-         * <p>
-         * The optional attempt is the number of tries this delay should be used
-         * (default = 1). The unit, if present, must be one of
-         * (msec,sec,minute,hour,day). The default value of unit is 'msec'.
-         * </p>
-         * <p>
-         * The constructor multiplies the delaytime by the relevant multiplier
-         * for the unit, so the delayTime instance variable is always in msec.
-         * </p>
-         *
-         * @param initString the string to initialize this Delay object from
-         */
-        public Delay(String initString) throws MessagingException {
-            // Default unit value to 'msec'.
-            String unit = "msec";
-
-            Matcher res = PATTERN.matcher(initString);
-            if (res.matches()) {
-                // The capturing groups will now hold:
-                // at 1: attempts * (if present)
-                // at 2: delaytime
-                // at 3: unit (if present)
-                if (res.group(1) != null && !res.group(1).equals("")) {
-                    // We have an attempt *
-                    String attemptMatch = res.group(1);
-
-                    // Strip the * and whitespace.
-                    attemptMatch = attemptMatch.substring(0, attemptMatch.length() - 1).trim();
-                    attempts = Integer.parseInt(attemptMatch);
-                }
-
-                delayTime = Long.parseLong(res.group(2));
-
-                if (!res.group(3).equals("")) {
-                    // We have a value for 'unit'.
-                    unit = res.group(3).toLowerCase(Locale.US);
-                }
-            } else {
-                throw new MessagingException(initString + " does not match " + PATTERN_STRING);
-            }
-
-            // calculate delayTime.
-            try {
-                delayTime = TimeConverter.getMilliSeconds(delayTime, unit);
-            } catch (NumberFormatException e) {
-                throw new MessagingException(e.getMessage());
-            }
-        }
-
-        /**
-         * This constructor makes a default Delay object with attempts = 1 and
-         * delayTime = DEFAULT_DELAY_TIME.
-         */
-        public Delay() {
-        }
-
-        /**
-         * @return the delayTime for this Delay
-         */
-        public long getDelayTime() {
-            return delayTime;
-        }
-
-        /**
-         * @return the number attempts this Delay should be used.
-         */
-        public int getAttempts() {
-            return attempts;
-        }
-
-        /**
-         * Set the number attempts this Delay should be used.
-         */
-        public void setAttempts(int value) {
-            attempts = value;
-        }
-
-        /**
-         * Pretty prints this Delay
-         */
-        @Override
-        public String toString() {
-            return getAttempts() + "*" + getDelayTime() + "msecs";
-        }
-    }
-
     @Override
     public String getMailetInfo() {
         return "RemoteDelivery Mailet";

http://git-wip-us.apache.org/repos/asf/james-project/blob/1193b6b4/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
new file mode 100644
index 0000000..c066a63
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Delay.java
@@ -0,0 +1,166 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.transport.util.Patterns;
+import org.apache.james.util.TimeConverter;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+
+/**
+ * This class is used to hold a delay time and its corresponding number of
+ * retries.
+ */
+public class Delay {
+
+    /**
+     * Default Delay Time (Default is 6*60*60*1000 Milliseconds (6 hours)).
+     */
+    public static final long DEFAULT_DELAY_TIME = 21600000;
+    public static final int DEFAULT_ATTEMPTS = 1;
+    /**
+     * Pattern to match [attempts*]delay[units].
+     */
+    private static final String PATTERN_STRING = "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";
+    private static final Pattern PATTERN = Patterns.compilePatternUncheckedException(PATTERN_STRING);
+
+    private int attempts = DEFAULT_ATTEMPTS;
+
+    private long delayTime = DEFAULT_DELAY_TIME;
+
+    /**
+     * <p>
+     * This constructor expects Strings of the form
+     * "[attempt\*]delaytime[unit]".
+     * </p>
+     * <p>
+     * The optional attempt is the number of tries this delay should be used
+     * (default = 1). The unit, if present, must be one of
+     * (msec,sec,minute,hour,day). The default value of unit is 'msec'.
+     * </p>
+     * <p>
+     * The constructor multiplies the delaytime by the relevant multiplier
+     * for the unit, so the delayTime instance variable is always in msec.
+     * </p>
+     *
+     * @param initString the string to initialize this Delay object from
+     */
+    public Delay(String initString) throws MessagingException {
+        // Default unit value to 'msec'.
+        String unit = "msec";
+
+        Matcher res = PATTERN.matcher(initString);
+        if (res.matches()) {
+            // The capturing groups will now hold:
+            // at 1: attempts * (if present)
+            // at 2: delaytime
+            // at 3: unit (if present)
+            if (res.group(1) != null && !res.group(1).equals("")) {
+                // We have an attempt *
+                String attemptMatch = res.group(1);
+
+                // Strip the * and whitespace.
+                attemptMatch = attemptMatch.substring(0, attemptMatch.length() - 1).trim();
+                attempts = Integer.parseInt(attemptMatch);
+            }
+
+            delayTime = Long.parseLong(res.group(2));
+
+            if (!res.group(3).equals("")) {
+                // We have a value for 'unit'.
+                unit = res.group(3).toLowerCase(Locale.US);
+            }
+        } else {
+            throw new MessagingException(initString + " does not match " + PATTERN_STRING);
+        }
+
+        // calculate delayTime.
+        try {
+            delayTime = TimeConverter.getMilliSeconds(delayTime, unit);
+        } catch (NumberFormatException e) {
+            throw new MessagingException(e.getMessage());
+        }
+    }
+
+    /**
+     * This constructor makes a default Delay object with attempts = 1 and
+     * delayTime = DEFAULT_DELAY_TIME.
+     */
+    public Delay() {
+    }
+
+    @VisibleForTesting
+    Delay(int attempts, long delayTime) {
+        this.attempts = attempts;
+        this.delayTime = delayTime;
+    }
+
+    /**
+     * @return the delayTime for this Delay
+     */
+    public long getDelayTime() {
+        return delayTime;
+    }
+
+    /**
+     * @return the number attempts this Delay should be used.
+     */
+    public int getAttempts() {
+        return attempts;
+    }
+
+    /**
+     * Set the number attempts this Delay should be used.
+     */
+    public void setAttempts(int value) {
+        attempts = value;
+    }
+
+    /**
+     * Pretty prints this Delay
+     */
+    @Override
+    public String toString() {
+        return getAttempts() + "*" + getDelayTime() + "msecs";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof Delay) {
+            Delay that = (Delay) o;
+
+            return Objects.equal(this.attempts, that.attempts)
+                && Objects.equal(this.delayTime, that.delayTime);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(attempts, delayTime);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1193b6b4/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
new file mode 100644
index 0000000..92ce97b
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
@@ -0,0 +1,96 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.mail.MessagingException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class DelayTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void defaultConstructorShouldConstructDefaultDelay() {
+        assertThat(new Delay())
+            .isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, Delay.DEFAULT_DELAY_TIME));
+    }
+
+    @Test
+    public void stringConstructorShouldWorkForNumbers() throws Exception {
+        assertThat(new Delay("36")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 36));
+    }
+
+    @Test
+    public void stringConstructorShouldWorkForZero() throws Exception {
+        assertThat(new Delay("0")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 0));
+    }
+
+    @Test
+    public void stringConstructorShouldDefaultToZeroForNegatives() throws Exception {
+        assertThat(new Delay("0")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 0));
+    }
+
+    @Test
+    public void stringConstructorShouldWorkForNumberAndSecond() throws Exception {
+        assertThat(new Delay("1s")).isEqualTo(new Delay(Delay.DEFAULT_ATTEMPTS, 1000));
+    }
+
+    @Test
+    public void stringConstructorShouldWorkForNumberAndAttempts() throws Exception {
+        assertThat(new Delay("2*36")).isEqualTo(new Delay(2, 36));
+    }
+
+    @Test
+    public void stringConstructorShouldWorkForNumberAndZeroAttempts() throws Exception {
+        assertThat(new Delay("0*36")).isEqualTo(new Delay(0, 36));
+    }
+
+    @Test
+    public void stringConstructorShouldThrowOnNegativeAttempts() throws Exception {
+        expectedException.expect(MessagingException.class);
+
+        new Delay("-1*36");
+    }
+
+    @Test
+    public void stringConstructorShouldWorkForNumberAttemptsAndUnit() throws Exception {
+        assertThat(new Delay("2*36s")).isEqualTo(new Delay(2, 36000));
+    }
+
+    @Test
+    public void stringConstructorShouldThrowOnInvalidInput() throws Exception {
+        expectedException.expect(MessagingException.class);
+
+        new Delay("invalid");
+    }
+
+    @Test
+    public void stringConstructorShouldThrowOnInvalidUnit() throws Exception {
+        expectedException.expect(MessagingException.class);
+
+        new Delay("36invalid");
+    }
+}


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


[05/50] [abbrv] james-project git commit: JAMES-1877 Extract delays and max retries calculations from RemoteDelivery

Posted by ro...@apache.org.
JAMES-1877 Extract delays and max retries calculations 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/8a8af97a
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/8a8af97a
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/8a8af97a

Branch: refs/heads/master
Commit: 8a8af97a5f569fb01f24b72bfbb8e481e4c26f26
Parents: 1193b6b
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Nov 29 12:02:07 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 14:35:32 2017 +0700

----------------------------------------------------------------------
 .../james/transport/mailets/RemoteDelivery.java | 107 +-----------
 .../remoteDelivery/DelaysAndMaxRetry.java       | 166 +++++++++++++++++++
 .../mailets/remoteDelivery/DelayTest.java       |  21 +++
 .../remoteDelivery/DelaysAndMaxRetryTest.java   | 136 +++++++++++++++
 4 files changed, 329 insertions(+), 101 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/8a8af97a/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 b23f79d..9ffd164 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
@@ -64,6 +64,7 @@ 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.DelaysAndMaxRetry;
 import org.apache.james.transport.mailets.remoteDelivery.HeloNameProvider;
 import org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
 import org.apache.mailet.HostAddress;
@@ -170,7 +171,7 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     /**
      * Maximum no. of retries (Defaults to 5).
      */
-    private int maxRetries = 5;
+    private int maxRetries;
 
     /**
      * Default number of ms to timeout on smtp delivery
@@ -264,61 +265,11 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
 
         logger = getMailetContext().getLogger();
 
-        // Create list of Delay Times.
-        ArrayList<Delay> delayTimesList = new ArrayList<Delay>();
         try {
-            if (getInitParameter("delayTime") != null) {
-
-                // parses delayTimes specified in config file.
-                String delayTimesParm = getInitParameter("delayTime");
-
-                // Split on commas
-                StringTokenizer st = new StringTokenizer(delayTimesParm, ",");
-                while (st.hasMoreTokens()) {
-                    String delayTime = st.nextToken();
-                    delayTimesList.add(new Delay(delayTime));
-                }
-            } else {
-                // Use default delayTime.
-                delayTimesList.add(new Delay());
-            }
-        } catch (Exception e) {
-            log("Invalid delayTime setting: " + getInitParameter("delayTime"));
-        }
-
-        try {
-            // Get No. of Max Retries.
-            if (getInitParameter("maxRetries") != null) {
-                maxRetries = Integer.parseInt(getInitParameter("maxRetries"));
-            }
-
-            // Check consistency of 'maxRetries' with delayTimesList attempts.
-            int totalAttempts = calcTotalAttempts(delayTimesList);
-
-            // If inconsistency found, fix it.
-            if (totalAttempts > maxRetries) {
-                log("Total number of delayTime attempts exceeds maxRetries specified. " + " Increasing maxRetries from " + maxRetries + " to " + totalAttempts);
-                maxRetries = totalAttempts;
-            } else {
-                int extra = maxRetries - totalAttempts;
-                if (extra != 0) {
-                    log("maxRetries is larger than total number of attempts specified.  " + "Increasing last delayTime with " + extra + " attempts ");
-
-                    // Add extra attempts to the last delayTime.
-                    if (delayTimesList.size() != 0) {
-                        // Get the last delayTime.
-                        Delay delay = delayTimesList.get(delayTimesList.size() - 1);
-
-                        // Increase no. of attempts.
-                        delay.setAttempts(delay.getAttempts() + extra);
-                        log("Delay of " + delay.getDelayTime() + " msecs is now attempted: " + delay.getAttempts() + " times");
-                    } else {
-                        throw new MessagingException("No delaytimes, cannot continue");
-                    }
-                }
-            }
-            delayTimes = expandDelays(delayTimesList);
-
+            int intendedMaxRetries = Integer.parseInt(getInitParameter("maxRetries", "5"));
+            DelaysAndMaxRetry delaysAndMaxRetry = DelaysAndMaxRetry.from(intendedMaxRetries, getInitParameter("delayTime"));
+            maxRetries = delaysAndMaxRetry.getMaxRetries();
+            delayTimes = delaysAndMaxRetry.getExpendedDelays();
         } catch (Exception e) {
             log("Invalid maxRetries setting: " + getInitParameter("maxRetries"));
         }
@@ -435,52 +386,6 @@ public class RemoteDelivery extends GenericMailet implements Runnable {
     }
 
     /**
-     * Calculates Total no. of attempts for the specified delayList.
-     *
-     * @param delayList list of 'Delay' objects
-     * @return total no. of retry attempts
-     */
-    private int calcTotalAttempts(ArrayList<Delay> delayList) {
-        int sum = 0;
-        for (Delay delay : delayList) {
-            sum += delay.getAttempts();
-        }
-        return sum;
-    }
-
-    /**
-     * <p>
-     * This method expands an ArrayList containing Delay objects into an array
-     * holding the only delaytime in the order.
-     * </p>
-     * <p/>
-     * So if the list has 2 Delay objects the first having attempts=2 and
-     * delaytime 4000 the second having attempts=1 and delaytime=300000 will be
-     * expanded into this array:
-     * <p/>
-     * <pre>
-     * long[0] = 4000
-     * long[1] = 4000
-     * long[2] = 300000
-     * </pre>
-     *
-     * @param list the list to expand
-     * @return the expanded list
-     */
-    private long[] expandDelays(ArrayList<Delay> list) {
-        long[] delays = new long[calcTotalAttempts(list)];
-        Iterator<Delay> i = list.iterator();
-        int idx = 0;
-        while (i.hasNext()) {
-            Delay delay = i.next();
-            for (int j = 0; j < delay.getAttempts(); j++) {
-                delays[idx++] = delay.getDelayTime();
-            }
-        }
-        return delays;
-    }
-
-    /**
      * This method returns, given a retry-count, the next delay time to use.
      *
      * @param retry_count the current retry_count.

http://git-wip-us.apache.org/repos/asf/james-project/blob/8a8af97a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
new file mode 100644
index 0000000..10b6cca
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetry.java
@@ -0,0 +1,166 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.mail.MessagingException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+public class DelaysAndMaxRetry {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DelaysAndMaxRetry.class);
+
+    public static DelaysAndMaxRetry from(int intendedMaxRetries, String delaysAsString) throws MessagingException {
+        // Create list of Delay Times.
+        ArrayList<Delay> delayTimesList = createDelayList(delaysAsString);
+
+        // Check consistency of 'maxRetries' with delayTimesList attempts.
+        int totalAttempts = calcTotalAttempts(delayTimesList);
+
+        // If inconsistency found, fix it.
+        if (totalAttempts > intendedMaxRetries) {
+            LOGGER.warn("Total number of delayTime attempts exceeds maxRetries specified. " + " Increasing maxRetries from " + intendedMaxRetries + " to " + totalAttempts);
+            return new DelaysAndMaxRetry(totalAttempts, delayTimesList);
+        } else {
+            int extra = intendedMaxRetries - totalAttempts;
+            if (extra != 0) {
+                LOGGER.warn("maxRetries is larger than total number of attempts specified.  " + "Increasing last delayTime with " + extra + " attempts ");
+
+                // Add extra attempts to the last delayTime.
+                if (delayTimesList.size() != 0) {
+                    // Get the last delayTime.
+                    Delay delay = delayTimesList.get(delayTimesList.size() - 1);
+
+                    // Increase no. of attempts.
+                    delay.setAttempts(delay.getAttempts() + extra);
+                    LOGGER.warn("Delay of " + delay.getDelayTime() + " msecs is now attempted: " + delay.getAttempts() + " times");
+                } else {
+                    throw new MessagingException("No delaytimes, cannot continue");
+                }
+            }
+            return new DelaysAndMaxRetry(intendedMaxRetries, delayTimesList);
+        }
+    }
+
+    private static ArrayList<Delay> createDelayList(String delaysAsString) {
+        ArrayList<Delay> delayTimesList = new ArrayList<Delay>();
+        try {
+            if (delaysAsString != null) {
+
+                // Split on commas
+                StringTokenizer st = new StringTokenizer(delaysAsString, ",");
+                while (st.hasMoreTokens()) {
+                    String delayTime = st.nextToken();
+                    delayTimesList.add(new Delay(delayTime));
+                }
+            } else {
+                // Use default delayTime.
+                delayTimesList.add(new Delay());
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Invalid delayTime setting: " + delaysAsString);
+        }
+        return delayTimesList;
+    }
+
+    /**
+     * Calculates Total no. of attempts for the specified delayList.
+     *
+     * @param delayList list of 'Delay' objects
+     * @return total no. of retry attempts
+     */
+    private static int calcTotalAttempts(List<Delay> delayList) {
+        int sum = 0;
+        for (Delay delay : delayList) {
+            sum += delay.getAttempts();
+        }
+        return sum;
+    }
+
+    private final int maxRetries;
+    private final List<Delay> delays;
+
+    @VisibleForTesting
+    DelaysAndMaxRetry(int maxRetries, List<Delay> delays) {
+        this.maxRetries = maxRetries;
+        this.delays = ImmutableList.copyOf(delays);
+    }
+
+    public int getMaxRetries() {
+        return maxRetries;
+    }
+
+    /**
+     * <p>
+     * This method expands an ArrayList containing Delay objects into an array
+     * holding the only delaytime in the order.
+     * </p>
+     * <p/>
+     * So if the list has 2 Delay objects the first having attempts=2 and
+     * delaytime 4000 the second having attempts=1 and delaytime=300000 will be
+     * expanded into this array:
+     * <p/>
+     * <pre>
+     * long[0] = 4000
+     * long[1] = 4000
+     * long[2] = 300000
+     * </pre>
+     *
+     * @param list the list to expand
+     * @return the expanded list
+     */
+    public long[] getExpendedDelays() {
+        long[] delaysAsLong = new long[calcTotalAttempts(delays)];
+        Iterator<Delay> i = delays.iterator();
+        int idx = 0;
+        while (i.hasNext()) {
+            Delay delay = i.next();
+            for (int j = 0; j < delay.getAttempts(); j++) {
+                delaysAsLong[idx++] = delay.getDelayTime();
+            }
+        }
+        return delaysAsLong;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof DelaysAndMaxRetry) {
+            DelaysAndMaxRetry that = (DelaysAndMaxRetry) o;
+            return Objects.equal(this.maxRetries, that.maxRetries)
+                && Objects.equal(this.delays, that.delays);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(maxRetries, delays);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/8a8af97a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
index 92ce97b..f23c918 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelayTest.java
@@ -76,6 +76,20 @@ public class DelayTest {
     }
 
     @Test
+    public void stringConstructorShouldThrowWhenAttemptsOmitted() throws Exception {
+        expectedException.expect(NumberFormatException.class);
+
+        new Delay("*36");
+    }
+
+    @Test
+    public void stringConstructorShouldThrowWhenDelayOmitted() throws Exception {
+        expectedException.expect(MessagingException.class);
+
+        new Delay("2*");
+    }
+
+    @Test
     public void stringConstructorShouldWorkForNumberAttemptsAndUnit() throws Exception {
         assertThat(new Delay("2*36s")).isEqualTo(new Delay(2, 36000));
     }
@@ -93,4 +107,11 @@ public class DelayTest {
 
         new Delay("36invalid");
     }
+
+    @Test
+    public void stringConstructorShouldThrowOnEmptyString() throws Exception {
+        expectedException.expect(MessagingException.class);
+
+        new Delay("");
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/8a8af97a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
new file mode 100644
index 0000000..da4ecda
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remoteDelivery/DelaysAndMaxRetryTest.java
@@ -0,0 +1,136 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.mail.MessagingException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.google.common.collect.ImmutableList;
+
+public class DelaysAndMaxRetryTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void fromShouldParseSingleDelay() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(1, "1s");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(1, ImmutableList.of(new Delay(1, 1000)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldParseTwoDelays() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(2, "1s,2s");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(2, ImmutableList.of(new Delay(1, 1000), new Delay(1, 2000)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldAdaptMaxRetriesWhenUnderAttempts() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(1, "1s,2*2s");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(3, ImmutableList.of(new Delay(1, 1000), new Delay(2, 2000)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldAdaptDelaysWhenUnderMaxRetries() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(4, "1s,2*2s");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(4, ImmutableList.of(new Delay(1, 1000), new Delay(3, 2000)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldHandleNullValuesForDelayAsString() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(1, null);
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(1, ImmutableList.of(new Delay(Delay.DEFAULT_ATTEMPTS, Delay.DEFAULT_DELAY_TIME)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldIgnoreEmptyDelay() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(1, "1s,,2s");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(2, ImmutableList.of(new Delay(1, 1000), new Delay(1, 2000)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldHandleParsingFailures() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(3, "1s,invalid,2s");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(3, ImmutableList.of(new Delay(3, 1000)));
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldHandleEmptyStringWithZeroMaxRetries() throws Exception {
+        DelaysAndMaxRetry actual = DelaysAndMaxRetry.from(0, "");
+
+        DelaysAndMaxRetry expected = new DelaysAndMaxRetry(0, ImmutableList.<Delay>of());
+
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void fromShouldThrowOnEmptyStringWithNonZeroMaxRetry() throws Exception {
+        expectedException.expect(MessagingException.class);
+
+        DelaysAndMaxRetry.from(2, "");
+    }
+
+    @Test
+    public void getExpendedDelaysShouldReturnEmptyWhenNoDelay() throws Exception {
+        DelaysAndMaxRetry testee = DelaysAndMaxRetry.from(0, "");
+
+        assertThat(testee.getExpendedDelays()).isEmpty();
+    }
+
+    @Test
+    public void getExpendedDelaysShouldExpandSingleDelays() throws Exception {
+        DelaysAndMaxRetry testee = DelaysAndMaxRetry.from(3, "1*1S,1*2S,1*5S");
+
+        assertThat(testee.getExpendedDelays()).containsExactly(1000, 2000, 5000);
+    }
+
+    @Test
+    public void getExpendedDelaysShouldExpandMultipleDelays() throws Exception {
+        DelaysAndMaxRetry testee = DelaysAndMaxRetry.from(3, "1*1S,2*2S,2*5S");
+
+        assertThat(testee.getExpendedDelays()).containsExactly(1000, 2000, 2000, 5000, 5000);
+    }
+}


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


[49/50] [abbrv] james-project git commit: JAMES-1877 Use placeholder with loggers

Posted by ro...@apache.org.
JAMES-1877 Use placeholder with loggers


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

Branch: refs/heads/master
Commit: c80de0e041052f8ac6937e310c49b5a59528f861
Parents: d584c13
Author: Benoit Tellier <bt...@linagora.com>
Authored: Tue Jan 10 14:58:56 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 18:14:32 2017 +0700

----------------------------------------------------------------------
 .../AddressesArrayToMailAddressListConverter.java              | 2 +-
 .../transport/mailets/remoteDelivery/DeliveryRunnable.java     | 6 +++---
 .../mailets/remoteDelivery/RemoteDeliveryConfiguration.java    | 6 +++---
 3 files changed, 7 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c80de0e0/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
index d468947..8d7dcec 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
@@ -46,7 +46,7 @@ public class AddressesArrayToMailAddressListConverter {
                 try {
                     return Optional.of(new MailAddress(input.toString()));
                 } catch (AddressException e) {
-                    logger.debug("Can't parse unsent address: " + e.getMessage());
+                    logger.debug("Can't parse unsent address: {}", e.getMessage());
                     return Optional.absent();
                 }
             }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c80de0e0/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index 4841402..a7cf23b 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -96,7 +96,7 @@ public class DeliveryRunnable implements Runnable {
 
             try {
                 if (configuration.isDebug()) {
-                    logger.debug(Thread.currentThread().getName() + " will process mail " + mail.getName());
+                    logger.debug("{} will process mail {}", Thread.currentThread().getName(), mail.getName());
                 }
                 attemptDelivery(mail);
                 LifecycleUtil.dispose(mail);
@@ -146,13 +146,13 @@ public class DeliveryRunnable implements Runnable {
         if (retries < configuration.getMaxRetries()) {
             reAttemptDelivery(mail, retries);
         } else {
-            logger.debug("Bouncing message " + mail.getName() + " after " + retries + " retries");
+            logger.debug("Bouncing message {} after {} retries", mail.getName(), retries);
             bouncer.bounce(mail, new Exception("Too many retries failure. Bouncing after " + retries + " retries.", executionResult.getException().orNull()));
         }
     }
 
     private void reAttemptDelivery(Mail mail, int retries) throws MailQueue.MailQueueException {
-        logger.debug("Storing message " + mail.getName() + " into outgoing after " + retries + " retries");
+        logger.debug("Storing message {} into outgoing after {} retries", mail.getName(), retries);
         DeliveryRetriesHelper.incrementRetries(mail);
         mail.setLastUpdated(dateSupplier.get());
         // Something happened that will delay delivery. Store it back in the retry repository.

http://git-wip-us.apache.org/repos/asf/james-project/blob/c80de0e0/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
index 3bfde9e..9b5fb58 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/RemoteDeliveryConfiguration.java
@@ -146,7 +146,7 @@ public class RemoteDeliveryConfiguration {
                 Optional.fromNullable(mailetConfig.getInitParameter(CONNECTIONTIMEOUT))
                     .or(String.valueOf(DEFAULT_CONNECTION_TIMEOUT)));
         } catch (Exception e) {
-            LOGGER.warn("Invalid timeout setting: " + mailetConfig.getInitParameter(TIMEOUT));
+            LOGGER.warn("Invalid timeout setting: {}", mailetConfig.getInitParameter(TIMEOUT));
             return DEFAULT_CONNECTION_TIMEOUT;
         }
     }
@@ -159,7 +159,7 @@ public class RemoteDeliveryConfiguration {
                 return DEFAULT_SMTP_TIMEOUT;
             }
         } catch (Exception e) {
-            LOGGER.warn("Invalid timeout setting: " + mailetConfig.getInitParameter(TIMEOUT));
+            LOGGER.warn("Invalid timeout setting: {}", mailetConfig.getInitParameter(TIMEOUT));
             return DEFAULT_SMTP_TIMEOUT;
         }
     }
@@ -171,7 +171,7 @@ public class RemoteDeliveryConfiguration {
                     .or(String.valueOf(DEFAULT_MAX_RETRY)));
             return DelaysAndMaxRetry.from(intendedMaxRetries, mailetConfig.getInitParameter(DELAY_TIME));
         } catch (Exception e) {
-            LOGGER.warn("Invalid maxRetries setting: " + mailetConfig.getInitParameter(MAX_RETRIES));
+            LOGGER.warn("Invalid maxRetries setting: {}", mailetConfig.getInitParameter(MAX_RETRIES));
             return DelaysAndMaxRetry.defaults();
         }
     }


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


[37/50] [abbrv] james-project git commit: JAMES-1877 Enable logging on mailet tests

Posted by ro...@apache.org.
JAMES-1877 Enable logging on mailet tests


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

Branch: refs/heads/master
Commit: dbeea267cdf191517535f87dbb68a165995deace
Parents: 730a7ab
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 13:13:36 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:52 2017 +0700

----------------------------------------------------------------------
 server/mailet/mailets/pom.xml                   |  6 ++++++
 .../mailets/src/test/resources/logback-test.xml | 21 ++++++++++++++++++++
 2 files changed, 27 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/dbeea267/server/mailet/mailets/pom.xml
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/pom.xml b/server/mailet/mailets/pom.xml
index ea7603c..d519861 100644
--- a/server/mailet/mailets/pom.xml
+++ b/server/mailet/mailets/pom.xml
@@ -148,6 +148,12 @@
         </dependency>
         <!-- Test dependencies -->
         <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.1.6</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/dbeea267/server/mailet/mailets/src/test/resources/logback-test.xml
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/resources/logback-test.xml b/server/mailet/mailets/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..0210dd6
--- /dev/null
+++ b/server/mailet/mailets/src/test/resources/logback-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+        <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+                <resetJUL>true</resetJUL>
+        </contextListener>
+
+        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+                <encoder>
+                        <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
+                        <immediateFlush>false</immediateFlush>
+                </encoder>
+        </appender>
+
+        <root level="DEBUG">
+                <appender-ref ref="CONSOLE" />
+        </root>
+
+        <logger name="org.apache.james" level="DEBUG"/>
+
+</configuration>


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


[36/50] [abbrv] james-project git commit: JAMES-1877 Extract DNS resolution to a DNS helper

Posted by ro...@apache.org.
JAMES-1877 Extract DNS resolution to a DNS helper


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

Branch: refs/heads/master
Commit: 730a7ab7ef7dea7c2db5b45855f89b31ccb1534a
Parents: 7f8cf9e
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 10:56:14 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:52 2017 +0700

----------------------------------------------------------------------
 .../mailets/remoteDelivery/DnsHelper.java       | 69 ++++++++++++++++++++
 .../mailets/remoteDelivery/MailDelivrer.java    | 51 ++++-----------
 2 files changed, 83 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/730a7ab7/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
new file mode 100644
index 0000000..5e92040
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DnsHelper.java
@@ -0,0 +1,69 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remoteDelivery;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.dnsservice.api.TemporaryResolutionException;
+import org.apache.james.dnsservice.library.MXHostAddressIterator;
+import org.apache.mailet.HostAddress;
+import org.slf4j.Logger;
+
+@SuppressWarnings("deprecation")
+public class DnsHelper {
+
+    private final DNSService dnsServer;
+    private final RemoteDeliveryConfiguration configuration;
+    private final Logger logger;
+
+    public DnsHelper(DNSService dnsServer, RemoteDeliveryConfiguration configuration, Logger logger) {
+        this.dnsServer = dnsServer;
+        this.configuration = configuration;
+        this.logger = logger;
+    }
+
+    public Iterator<HostAddress> retrieveHostAddressIterator(String host) throws TemporaryResolutionException {
+        if (configuration.getGatewayServer().isEmpty()) {
+            return new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
+        } else {
+            return getGatewaySMTPHostAddresses(configuration.getGatewayServer());
+        }
+    }
+
+    /**
+     * 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) {
+        return new MXHostAddressIterator(gatewayServers.iterator(), dnsServer, false, logger);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/730a7ab7/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
index 941ef21..df77eb8 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/MailDelivrer.java
@@ -34,7 +34,6 @@ 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.mailet.HostAddress;
 import org.apache.mailet.Mail;
 import org.apache.mailet.MailAddress;
@@ -46,14 +45,14 @@ public class MailDelivrer {
 
     private final RemoteDeliveryConfiguration configuration;
     private final MailDelivrerToHost mailDelivrerToHost;
-    private final DNSService dnsServer;
+    private final DnsHelper dnsHelper;
     private final MessageComposer messageComposer;
     private final Logger logger;
 
     public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Logger logger) {
         this.configuration = configuration;
         this.mailDelivrerToHost = mailDelivrerToHost;
-        this.dnsServer = dnsServer;
+        this.dnsHelper = new DnsHelper(dnsServer, configuration, logger);
         this.messageComposer = new MessageComposer(configuration);
         this.logger = logger;
     }
@@ -107,27 +106,23 @@ public class MailDelivrer {
             logger.debug("Attempting to deliver " + mail.getName());
         }
 
-        // Figure out which servers to try to send to. This collection
-        // will hold all the possible target servers
-        Iterator<HostAddress> targetServers;
-        if (configuration.getGatewayServer().isEmpty()) {
-            MailAddress rcpt = mail.getRecipients().iterator().next();
-            String host = rcpt.getDomain();
-
-            // Lookup the possible targets
-            try {
-                targetServers = new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, false, logger);
-            } catch (TemporaryResolutionException e) {
-                return handleTemporaryResolutionException(mail, host);
-            }
+        String host = retrieveTargetHostname(mail);
+        try {
+            // Figure out which servers to try to send to. This collection
+            // will hold all the possible target servers
+            Iterator<HostAddress> targetServers = dnsHelper.retrieveHostAddressIterator(host);
             if (!targetServers.hasNext()) {
                 return handleNoTargetServer(mail, host);
             }
-        } else {
-            targetServers = getGatewaySMTPHostAddresses(configuration.getGatewayServer());
+            return doDeliver(mail, mail.getMessage(), InternetAddressConverter.convert(mail.getRecipients()), targetServers);
+        } catch (TemporaryResolutionException e) {
+            return handleTemporaryResolutionException(mail, host);
         }
+    }
 
-        return doDeliver(mail, mail.getMessage(), InternetAddressConverter.convert(mail.getRecipients()), targetServers);
+    private String retrieveTargetHostname(Mail mail) {
+        MailAddress rcpt = mail.getRecipients().iterator().next();
+        return rcpt.getDomain();
     }
 
     @SuppressWarnings("deprecation")
@@ -337,24 +332,6 @@ public class MailDelivrer {
         }
     }
 
-    /**
-     * 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
-     */
-    @SuppressWarnings("deprecation")
-    private Iterator<HostAddress> getGatewaySMTPHostAddresses(Collection<String> gatewayServers) {
-        return new MXHostAddressIterator(gatewayServers.iterator(), dnsServer, false, logger);
-    }
-
     private void logSendFailedException(SendFailedException sfe) {
         if (configuration.isDebug()) {
             EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);


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


[50/50] [abbrv] james-project git commit: Merge remote-tracking branch 'benoit/JAMES-1877'

Posted by ro...@apache.org.
Merge remote-tracking branch 'benoit/JAMES-1877'


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

Branch: refs/heads/master
Commit: 131c01148ce32c88320a168fdc773c001252603d
Parents: 3b6c5e0 c80de0e
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Tue Jan 10 15:17:23 2017 +0100
Committer: Raphael Ouazana <ra...@linagora.com>
Committed: Tue Jan 10 15:17:23 2017 +0100

----------------------------------------------------------------------
 mailet/base/pom.xml                             |   16 +
 .../org/apache/mailet/base/GenericMailet.java   |    2 +-
 .../java/org/apache/mailet/base/MailetUtil.java |   12 +-
 .../org/apache/mailet/base/MailetUtilTest.java  |    9 +-
 .../org/apache/mailet/base/test/FakeMail.java   |   64 +
 .../mailet/base/test/FakeMailContext.java       |  112 +-
 .../apache/mailet/base/test/FakeMailTest.java   |   41 +
 .../transport/mailets/ToProcessorTest.java      |   12 +-
 .../james/util/streams/IteratorsTest.java       |    2 +-
 .../org/apache/james/util/TimeConverter.java    |   11 +-
 .../apache/james/util/TimeConverterTest.java    |  252 ++-
 server/mailet/mailets/pom.xml                   |    6 +
 .../james/transport/mailets/RemoteDelivery.java | 1561 +-----------------
 .../mailets/RemoteDeliverySocketFactory.java    |  137 --
 ...ddressesArrayToMailAddressListConverter.java |   66 +
 .../mailets/remoteDelivery/Bouncer.java         |  147 ++
 .../mailets/remoteDelivery/Converter7Bit.java   |   65 +
 .../transport/mailets/remoteDelivery/Delay.java |  115 ++
 .../remoteDelivery/DelaysAndMaxRetry.java       |  161 ++
 .../remoteDelivery/DeliveryRetriesHelper.java   |   50 +
 .../remoteDelivery/DeliveryRunnable.java        |  174 ++
 .../mailets/remoteDelivery/DnsHelper.java       |   52 +
 .../EnhancedMessagingException.java             |  176 ++
 .../mailets/remoteDelivery/ExecutionResult.java |   92 ++
 .../remoteDelivery/HeloNameProvider.java        |   53 +
 .../InternetAddressConverter.java               |   44 +
 .../mailets/remoteDelivery/MailDelivrer.java    |  274 +++
 .../remoteDelivery/MailDelivrerToHost.java      |  136 ++
 .../mailets/remoteDelivery/MessageComposer.java |  143 ++
 .../RemoteDeliveryConfiguration.java            |  317 ++++
 .../RemoteDeliverySocketFactory.java            |  137 ++
 .../mailets/remoteDelivery/Repeat.java          |   37 +
 ...ssesArrayToMailAddressListConverterTest.java |   59 +
 .../mailets/remoteDelivery/BouncerTest.java     |  457 +++++
 .../mailets/remoteDelivery/DelayTest.java       |  118 ++
 .../remoteDelivery/DelaysAndMaxRetryTest.java   |  136 ++
 .../remoteDelivery/DeliveryRetryHelperTest.java |   83 +
 .../remoteDelivery/DeliveryRunnableTest.java    |  248 +++
 .../remoteDelivery/HeloNameProviderTest.java    |   79 +
 .../InternetAddressConverterTest.java           |   62 +
 .../remoteDelivery/MailDelivrerTest.java        |  462 ++++++
 .../RemoteDeliveryConfigurationTest.java        |  911 ++++++++++
 .../RemoteDeliveryRunningTest.java              |   85 +
 .../remoteDelivery/RemoteDeliveryTest.java      |  207 +++
 .../mailets/remoteDelivery/RepeatTest.java      |   56 +
 .../mailets/src/test/resources/logback-test.xml |   21 +
 46 files changed, 5790 insertions(+), 1670 deletions(-)
----------------------------------------------------------------------



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


[27/50] [abbrv] james-project git commit: JAMES-1877 Connect do not need to catch exception

Posted by ro...@apache.org.
JAMES-1877 Connect do not need to catch exception

This exception will not cause doDelivery (which is host specific to fail
This allow a more clean error handling, where errors are handled at the doDelivery for one server error


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

Branch: refs/heads/master
Commit: 2a4936d339e76ba41a684ae024f9ce33193c4646
Parents: 942cdfc
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Dec 2 09:14:02 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Jan 10 15:12:51 2017 +0700

----------------------------------------------------------------------
 .../remoteDelivery/DeliveryRunnable.java        | 35 ++++++--------------
 1 file changed, 11 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/2a4936d3/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
index f497767..c45e736 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/DeliveryRunnable.java
@@ -253,6 +253,11 @@ public class DeliveryRunnable implements Runnable {
                 lastError = handleSendFailException(mail, sfe);
             } catch (MessagingException me) {
                 lastError = handleMessagingException(mail, me);
+                if (configuration.isDebug()) {
+                    logger.debug(me.getMessage(), me.getCause());
+                } else {
+                    logger.info(me.getMessage());
+                }
             }
         }
         // If we encountered an exception while looping through,
@@ -290,9 +295,7 @@ public class DeliveryRunnable implements Runnable {
         try {
             transport = (SMTPTransport) session.getTransport(outgoingMailServer);
             transport.setLocalHost( props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName()) );
-            if (!connect(outgoingMailServer, transport)) {
-                return false;
-            }
+            connect(outgoingMailServer, transport);
             transport.sendMessage(adaptToTransport(message, transport), addr);
             logger.debug("Mail (" + mail.getName() + ")  sent successfully to " + outgoingMailServer.getHostName() +
                 " at " + outgoingMailServer.getHost() + " from " + props.get("mail.smtp.from") + " for " + mail.getRecipients());
@@ -538,27 +541,11 @@ public class DeliveryRunnable implements Runnable {
         }
     }
 
-    private boolean connect(HostAddress outgoingMailServer, SMTPTransport transport) {
-        try {
-            if (configuration.getAuthUser() != null) {
-                transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
-            } else {
-                transport.connect();
-            }
-            return true;
-        } 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()) {
-                logger.debug(me.getMessage(), me.getCause());
-            } else {
-                logger.info(me.getMessage());
-            }
-            return false;
+    private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException {
+        if (configuration.getAuthUser() != null) {
+            transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
+        } else {
+            transport.connect();
         }
     }
 


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