You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/05/24 09:17:18 UTC

[james-project] 02/04: JAMES-3588 Mailet container should support 'propagate'

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 5c61fbcfbcf06e2df31a1874226f055d83f2a317
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu May 20 12:05:10 2021 +0700

    JAMES-3588 Mailet container should support 'propagate'
---
 .../distributed/configure/mailetcontainer.adoc     | 10 ++-
 .../org/apache/james/mailets/MailetErrorsTest.java | 97 ++++++++++++++++++++++
 .../mailets/OneRuntimeExceptionMailet.java         | 40 +++++++++
 .../mailets/OneRuntimeExceptionMatcher.java        | 40 +++++++++
 .../mailetcontainer/impl/camel/CamelProcessor.java |  2 +
 .../impl/camel/MatcherSplitter.java                |  2 +
 src/site/xdoc/server/config-mailetcontainer.xml    |  9 +-
 7 files changed, 196 insertions(+), 4 deletions(-)

diff --git a/docs/modules/servers/pages/distributed/configure/mailetcontainer.adoc b/docs/modules/servers/pages/distributed/configure/mailetcontainer.adoc
index 06b9bf8..fe7c715 100644
--- a/docs/modules/servers/pages/distributed/configure/mailetcontainer.adoc
+++ b/docs/modules/servers/pages/distributed/configure/mailetcontainer.adoc
@@ -72,8 +72,14 @@ If an exception is encountered during the execution of a mailet or a matcher, th
 process the mail using the *error* processor.
 
 The *onMailetException* property allows you to override this behaviour. You can specify another
-processor than the *error* one for handling the errors of this mailet. The *ignore* special value also
-allows to continue processing and ignore the error.
+processor than the *error* one for handling the errors of this mailet.
+
+The *ignore* special value also allows to continue processing and ignore the error.
+
+The *propagate* special value causes the mailet container to rethrow the
+exception, propagating it to the execution context. In an SMTP execution context, the spooler will then requeue
+the item and automatic retries will be setted up - note that attempts will be done for each recipients. In LMTP
+(if LMTP is configured to execute the mailetContainer), the entire mail transaction is reported as failed to the caller.
 
 Moreover, the *onMatcherException* allows you to override matcher error handling. You can
 specify another processor than the *error* one for handling the errors of this mailet. The *matchall*
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
index 700d212..5fcfbe9 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
@@ -46,6 +46,7 @@ import org.apache.james.transport.mailets.NoopMailet;
 import org.apache.james.transport.mailets.Null;
 import org.apache.james.transport.mailets.OneRuntimeErrorMailet;
 import org.apache.james.transport.mailets.OneRuntimeExceptionMailet;
+import org.apache.james.transport.mailets.OneRuntimeExceptionMatcher;
 import org.apache.james.transport.mailets.OneThreadSuicideMailet;
 import org.apache.james.transport.mailets.RuntimeErrorMailet;
 import org.apache.james.transport.mailets.RuntimeExceptionMailet;
@@ -124,6 +125,102 @@ class MailetErrorsTest {
     }
 
     @Test
+    void propagateShouldAllowReprocessing(@TempDir File temporaryFolder) throws Exception {
+        jamesServer = TemporaryJamesServer.builder()
+            .withMailetContainer(MailetContainer.builder()
+                .putProcessor(CommonProcessors.transport())
+                .putProcessor(errorProcessor())
+                .putProcessor(ProcessorConfiguration.root()
+                    .addMailet(MailetConfiguration.builder()
+                        .matcher(All.class)
+                        .mailet(OneRuntimeExceptionMailet.class)
+                        .addProperty("onMailetException", "propagate"))
+                    .addMailet(MailetConfiguration.TO_TRANSPORT)))
+            .build(temporaryFolder);
+        jamesServer.start();
+        jamesServer.getProbe(DataProbeImpl.class).fluent()
+            .addDomain(DEFAULT_DOMAIN)
+            .addUser(FROM, PASSWORD)
+            .addUser(RECIPIENT, PASSWORD);
+
+        smtpMessageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, RECIPIENT);
+
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .select(TestIMAPClient.INBOX)
+            .awaitMessage(awaitAtMostOneMinute);
+    }
+
+    @Test
+    void retryExceptionShouldSucceedUponSplittedMail(@TempDir File temporaryFolder) throws Exception {
+        jamesServer = TemporaryJamesServer.builder()
+            .withMailetContainer(MailetContainer.builder()
+                .putProcessor(ProcessorConfiguration.transport()
+                    .addMailet(MailetConfiguration.builder()
+                        .matcher(RecipientIs.class)
+                        .matcherCondition(RECIPIENT)
+                        .mailet(OneRuntimeExceptionMailet.class)
+                        .addProperty("onMailetException", "propagate"))
+                    .addMailetsFrom(CommonProcessors.transport()))
+                .putProcessor(errorProcessor())
+                .putProcessor(CommonProcessors.root()))
+            .build(temporaryFolder);
+        jamesServer.start();
+        jamesServer.getProbe(DataProbeImpl.class).fluent()
+            .addDomain(DEFAULT_DOMAIN)
+            .addUser(FROM, PASSWORD)
+            .addUser(RECIPIENT, PASSWORD);
+
+        smtpMessageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, ImmutableList.of(FROM, RECIPIENT));
+
+        assertThat(testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .select(TestIMAPClient.INBOX)
+            .awaitMessage(awaitAtMostOneMinute)
+            .getMessageCount(TestIMAPClient.INBOX)).isEqualTo(1);
+
+        assertThat(testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(FROM, PASSWORD)
+            .select(TestIMAPClient.INBOX)
+            .awaitMessage(awaitAtMostOneMinute)
+            .getMessageCount(TestIMAPClient.INBOX)).isGreaterThanOrEqualTo(1);
+    }
+
+    @Test
+    void retryExceptionShouldSucceedUponSplittedMailForMatcher(@TempDir File temporaryFolder) throws Exception {
+        jamesServer = TemporaryJamesServer.builder()
+            .withMailetContainer(MailetContainer.builder()
+                .putProcessor(ProcessorConfiguration.transport()
+                    .addMailet(MailetConfiguration.builder()
+                        .matcher(OneRuntimeExceptionMatcher.class)
+                        .mailet(NoopMailet.class)
+                        .addProperty("onMatchException", "propagate"))
+                    .addMailetsFrom(CommonProcessors.transport()))
+                .putProcessor(errorProcessor())
+                .putProcessor(CommonProcessors.root()))
+            .build(temporaryFolder);
+        jamesServer.start();
+        jamesServer.getProbe(DataProbeImpl.class).fluent()
+            .addDomain(DEFAULT_DOMAIN)
+            .addUser(FROM, PASSWORD)
+            .addUser(RECIPIENT, PASSWORD);
+
+        smtpMessageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, RECIPIENT);
+
+        assertThat(testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .select(TestIMAPClient.INBOX)
+            .awaitMessage(awaitAtMostOneMinute)
+            .getMessageCount(TestIMAPClient.INBOX)).isEqualTo(1);
+    }
+
+    @Test
     void matcherProcessingShouldHandleClassNotFoundException(@TempDir File temporaryFolder) throws Exception {
         jamesServer = TemporaryJamesServer.builder()
             .withBase(SMTP_ONLY_MODULE)
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/OneRuntimeExceptionMailet.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/OneRuntimeExceptionMailet.java
new file mode 100644
index 0000000..982f4f8
--- /dev/null
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/OneRuntimeExceptionMailet.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * 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;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.mail.MessagingException;
+
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMailet;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
+
+public class OneRuntimeExceptionMailet extends GenericMailet {
+    private final AtomicInteger callCount = new AtomicInteger(0);
+
+    @Override
+    public void service(Mail mail) throws MessagingException {
+        System.out.println("exection " + ImmutableList.copyOf(mail.getRecipients()));
+        if (callCount.getAndIncrement() == 0) {
+            throw new RuntimeException();
+        }
+    }
+}
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/OneRuntimeExceptionMatcher.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/OneRuntimeExceptionMatcher.java
new file mode 100644
index 0000000..b0e7c99
--- /dev/null
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/OneRuntimeExceptionMatcher.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * 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;
+
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.james.core.MailAddress;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMatcher;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
+
+public class OneRuntimeExceptionMatcher extends GenericMatcher {
+    private final AtomicInteger callCount = new AtomicInteger(0);
+
+    @Override
+    public Collection<MailAddress> match(Mail mail) {
+        if (callCount.getAndIncrement() == 0) {
+            throw new RuntimeException();
+        }
+        return ImmutableList.of();
+    }
+}
diff --git a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/CamelProcessor.java b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/CamelProcessor.java
index fef4dc1..51bb14b 100644
--- a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/CamelProcessor.java
+++ b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/CamelProcessor.java
@@ -92,6 +92,8 @@ public class CamelProcessor {
                 // changed by the mailet
                 LOGGER.warn("Encountered error while executing mailet {}. Ignoring it.", mailet, ex);
                 ProcessorUtil.verifyMailAddresses(mail.getRecipients());
+            } else if (onMailetException.equalsIgnoreCase("propagate")) {
+                throw me;
             } else {
                 ProcessorUtil.handleException(me, mail, mailet.getMailetConfig().getMailetName(), onMailetException, LOGGER);
             }
diff --git a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
index 91b338f..4dd0d13 100644
--- a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
+++ b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
@@ -130,6 +130,8 @@ public class MatcherSplitter {
                     LOGGER.warn("Encountered error while executing matcher {}. matching all.", matcher, ex);
                     matchedRcpts = mail.getRecipients();
                     // no need to verify addresses
+                } else if (onMatchException.equalsIgnoreCase("propagate")) {
+                    throw new RuntimeException(me);
                 } else {
                     ProcessorUtil.handleException(me, mail, matcher.getMatcherConfig().getMatcherName(), onMatchException, LOGGER);
                 }
diff --git a/src/site/xdoc/server/config-mailetcontainer.xml b/src/site/xdoc/server/config-mailetcontainer.xml
index e80af19..c475f9a 100644
--- a/src/site/xdoc/server/config-mailetcontainer.xml
+++ b/src/site/xdoc/server/config-mailetcontainer.xml
@@ -88,8 +88,13 @@
       process the mail using the <strong>error</strong> processor.</p>
 
       <p>The <strong>onMailetException</strong> property allows you to override this behaviour. You can specify another
-          processor than the <strong>error</strong> one for handling the errors of this mailet. The <strong>ignore</strong> special value also
-      allows to continue processing and ignore the error.</p>
+          processor than the <strong>error</strong> one for handling the errors of this mailet. <br/>
+
+          The <strong>ignore</strong> special value also allows to continue processing and ignore the error. <br/>
+          The <strong>propagate</strong> special value causes the mailet container to rethrow the
+          exception, propagating it to the execution context. In an SMTP execution context, the spooler will then requeue
+          the item and automatic retries will be setted up - note that attempts will be done for each recipients. In LMTP
+          (if LMTP is configured to execute the mailetContainer), the entire mail transaction is reported as failed to the caller.</p>
 
       <p>Moreover, the <strong>onMatcherException</strong> allows you to override matcher error handling. You can
           specify another processor than the <strong>error</strong> one for handling the errors of this mailet. The <strong>matchall</strong>

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