Bouncer { public void bounce(Mail mail, Exception ex) { if (!mail.hasSender()) { LOGGER.debug("Null Sender: no bounce will be generated for {}", mail.getName()); } else { if (configuration.getBounceProcessor() != null) { computeErrorCode(ex).ifPresent(mail::setAttribute); mail.setAttribute(new Attribute(DELIVERY_ERROR, AttributeValue.of(getErrorMsg(ex)))); try { mailetContext.sendMail(mail, configuration.getBounceProcessor()); } catch (MessagingException e) { LOGGER.warn("Exception re-inserting failed mail: ", e); } } else { bounceWithMailetContext(mail, ex); } } } Bouncer(RemoteDeliveryConfiguration configuration, MailetContext mailetContext); void bounce(Mail mail, Exception ex); String explanationText(Mail mail, Exception ex); String getErrorMsg(Exception ex); String getNestedExceptionMessage(MessagingException me); static final AttributeName DELIVERY_ERROR; static final AttributeName DELIVERY_ERROR_CODE; static final AttributeName IS_DELIVERY_PERMANENT_ERROR; }
@Test public void bounceShouldCallMailetContextBounceByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldIncludeMessagingExceptionMessageByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldCustomizeSendFailedExceptionByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldCustomizeUnknownHostExceptionByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldCustomizeConnectionExceptionByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldCustomizeSocketExceptionByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldCustomizeNestedMessagingExceptionByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldNotBounceWithNoSenderByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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 bounceShouldSupportExceptionWithoutMessagesByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldNotSupportMessagingExceptionWithoutMessagesByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").state(Mail.DEFAULT) .sender(MailAddressFixture.ANY_AT_JAMES) .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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldWorkWhenProcessorSpecified() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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(new Attribute(DELIVERY_ERROR, AttributeValue.of(errorMessage))) .state(BOUNCE_PROCESSOR) .fromMailet() .build(); assertThat(mailetContext.getSentMails()).containsOnly(expected); assertThat(mailetContext.getBouncedMails()).isEmpty(); }
@Test public void bounceShouldNotBounceWhenNoSenderWhenProcessorSpecified() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").state(Mail.DEFAULT) .build(); testee.bounce(mail, new MessagingException("message")); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).isEmpty(); }
@Test public void bounceShouldDisplayAddressByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldDisplayAddressesByDefault() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( DEFAULT_REMOTE_DELIVERY_CONFIG, mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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.empty()); assertThat(mailetContext.getSentMails()).isEmpty(); assertThat(mailetContext.getBouncedMails()).containsOnly(expected); }
@Test public void bounceShouldWorkWhenProcessorSpecifiedAndNoExceptionMessage() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").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(new Attribute(DELIVERY_ERROR, AttributeValue.of("null"))) .state(BOUNCE_PROCESSOR) .fromMailet() .build(); assertThat(mailetContext.getSentMails()).containsOnly(expected); assertThat(mailetContext.getBouncedMails()).isEmpty(); }
@Test public void bounceShouldAttachErrorCodeWhenSmtpError() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").state(Mail.DEFAULT) .sender(MailAddressFixture.ANY_AT_JAMES) .build(); SMTPSendFailedException ex = mock(SMTPSendFailedException.class); when(ex.getReturnCode()).thenReturn(SMTP_ERROR_CODE_521); testee.bounce(mail, ex); FakeMailContext.SentMail expected = FakeMailContext.sentMailBuilder() .sender(MailAddressFixture.ANY_AT_JAMES) .attribute(new Attribute(DELIVERY_ERROR, AttributeValue.of("null"))) .attribute(new Attribute(DELIVERY_ERROR_CODE, AttributeValue.of(SMTP_ERROR_CODE_521))) .state(BOUNCE_PROCESSOR) .fromMailet() .build(); assertThat(mailetContext.getSentMails()).containsOnly(expected); assertThat(mailetContext.getBouncedMails()).isEmpty(); }
@Test public void bounceShouldNotAttachErrorCodeWhenNotMessagingException() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").state(Mail.DEFAULT) .sender(MailAddressFixture.ANY_AT_JAMES) .build(); testee.bounce(mail, new Exception()); FakeMailContext.SentMail expected = FakeMailContext.sentMailBuilder() .sender(MailAddressFixture.ANY_AT_JAMES) .attribute(new Attribute(DELIVERY_ERROR, AttributeValue.of("null"))) .state(BOUNCE_PROCESSOR) .fromMailet() .build(); assertThat(mailetContext.getSentMails()).containsOnly(expected); assertThat(mailetContext.getBouncedMails()).isEmpty(); }
@Test public void bounceShouldNotAttachErrorCodeWhenNotSmtpError() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").state(Mail.DEFAULT) .sender(MailAddressFixture.ANY_AT_JAMES) .build(); testee.bounce(mail, new MessagingException("not smtp related")); FakeMailContext.SentMail expected = FakeMailContext.sentMailBuilder() .sender(MailAddressFixture.ANY_AT_JAMES) .attribute(new Attribute(DELIVERY_ERROR, AttributeValue.of("not smtp related"))) .state(BOUNCE_PROCESSOR) .fromMailet() .build(); assertThat(mailetContext.getSentMails()).containsOnly(expected); assertThat(mailetContext.getBouncedMails()).isEmpty(); }
@Test public void bounceShouldAttachNullErrorMessageWhenNoException() throws Exception { RemoteDeliveryConfiguration configuration = new RemoteDeliveryConfiguration( FakeMailetConfig.builder() .setProperty(RemoteDeliveryConfiguration.HELO_NAME, HELLO_NAME) .setProperty(RemoteDeliveryConfiguration.BOUNCE_PROCESSOR, BOUNCE_PROCESSOR) .build(), mock(DomainList.class)); Bouncer testee = new Bouncer(configuration, mailetContext); Mail mail = FakeMail.builder().name("name").state(Mail.DEFAULT) .sender(MailAddressFixture.ANY_AT_JAMES) .build(); testee.bounce(mail, null); FakeMailContext.SentMail expected = FakeMailContext.sentMailBuilder() .sender(MailAddressFixture.ANY_AT_JAMES) .attribute(new Attribute(DELIVERY_ERROR, AttributeValue.of("null"))) .state(BOUNCE_PROCESSOR) .fromMailet() .build(); assertThat(mailetContext.getSentMails()).containsOnly(expected); assertThat(mailetContext.getBouncedMails()).isEmpty(); }