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