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 <btell...@linagora.com>
Authored: Thu Dec 1 18:18:30 2016 +0700
Committer: Benoit Tellier <btell...@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-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to