Repository: james-project
Updated Branches:
  refs/heads/master b03574767 -> 0e437d259


http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java
new file mode 100644
index 0000000..e652e0b
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java
@@ -0,0 +1,202 @@
+/****************************************************************
+ * 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.mailbox.quota.mailing.subscribers;
+
+import static 
org.apache.james.mailbox.quota.model.HistoryEvolution.HighestThresholdRecentness.AlreadyReachedDuringGracePriod;
+import static 
org.apache.james.mailbox.quota.model.HistoryEvolution.HighestThresholdRecentness.NotAlreadyReachedDuringGracePeriod;
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.NOW;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._80;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+
+import org.apache.james.mailbox.model.Quota;
+import org.apache.james.mailbox.quota.QuotaCount;
+import org.apache.james.mailbox.quota.QuotaSize;
+import org.apache.james.mailbox.quota.model.HistoryEvolution;
+import org.apache.james.mailbox.quota.model.QuotaThresholdChange;
+import 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.Quotas.Counts;
+import org.apache.james.mailbox.quota.model.QuotaThresholdFixture.Quotas.Sizes;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class QuotaThresholdNoticeTest {
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(QuotaThresholdNotice.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    void buildShouldReturnEmptyWhenNoThresholds() {
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._82_PERCENT)
+            .build())
+            .isEmpty();
+    }
+
+    @Test
+    void buildShouldReturnEmptyWhenNoChanges() {
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._82_PERCENT)
+            .sizeThreshold(HistoryEvolution.noChanges())
+            .build())
+            .isEmpty();
+    }
+
+    @Test
+    void buildShouldReturnEmptyWhenBelow() {
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._82_PERCENT)
+            .sizeThreshold(HistoryEvolution.lowerThresholdReached(new 
QuotaThresholdChange(_80, NOW)))
+            .build())
+            .isEmpty();
+    }
+
+    @Test
+    void buildShouldReturnEmptyWhenAboveButRecentChanges() {
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._82_PERCENT)
+            .sizeThreshold(HistoryEvolution.higherThresholdReached(new 
QuotaThresholdChange(_80, NOW), AlreadyReachedDuringGracePriod))
+            .build())
+            .isEmpty();
+    }
+
+    @Test
+    void buildShouldReturnPresentWhenAbove() {
+        Quota<QuotaSize> sizeQuota = Sizes._82_PERCENT;
+        Quota<QuotaCount> countQuota = Counts._82_PERCENT;
+        QuotaThresholdChange sizeThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(sizeQuota)
+            .countQuota(countQuota)
+            
.sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            .build())
+            .isNotEmpty()
+            .contains(new QuotaThresholdNotice(Optional.empty(), 
Optional.of(sizeThresholdChange.getQuotaThreshold()), sizeQuota, countQuota));
+    }
+
+    @Test
+    void buildShouldFilterOutNotInterestingFields() {
+        Quota<QuotaSize> sizeQuota = Sizes._82_PERCENT;
+        Quota<QuotaCount> countQuota = Counts._82_PERCENT;
+        QuotaThresholdChange sizeThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+        QuotaThresholdChange countThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(sizeQuota)
+            .countQuota(countQuota)
+            
.sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            
.countThreshold(HistoryEvolution.lowerThresholdReached(countThresholdChange))
+            .build())
+            .isNotEmpty()
+            .contains(new QuotaThresholdNotice(Optional.empty(), 
Optional.of(sizeThresholdChange.getQuotaThreshold()), sizeQuota, countQuota));
+    }
+
+    @Test
+    void buildShouldKeepAllInterestingFields() {
+        Quota<QuotaSize> sizeQuota = Sizes._82_PERCENT;
+        Quota<QuotaCount> countQuota = Counts._82_PERCENT;
+        QuotaThresholdChange sizeThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+        QuotaThresholdChange countThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(sizeQuota)
+            .countQuota(countQuota)
+            
.sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            
.countThreshold(HistoryEvolution.higherThresholdReached(countThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            .build())
+            .isNotEmpty()
+            .contains(new 
QuotaThresholdNotice(Optional.of(countThresholdChange.getQuotaThreshold()), 
Optional.of(sizeThresholdChange.getQuotaThreshold()), sizeQuota, countQuota));
+    }
+
+    @Test
+    void generateReportShouldGenerateAHumanReadableMessage() {
+        QuotaThresholdChange sizeThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+        QuotaThresholdChange countThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._92_PERCENT)
+            
.sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            
.countThreshold(HistoryEvolution.higherThresholdReached(countThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            .build()
+            .get()
+            .generateReport())
+            .isEqualTo("You receive this email because you recently exceeded a 
threshold related to the quotas of your email account.\n" +
+                "\n" +
+                "You currently occupy more than 80 % of the total size 
allocated to you.\n" +
+                "You currently occupy 82 bytes on a total of 100 bytes 
allocated to you.\n" +
+                "\n" +
+                "You currently occupy more than 80 % of the total message 
count allocated to you.\n" +
+                "You currently have 92 messages on a total of 100 allowed for 
you.\n" +
+                "\n" +
+                "You need to be aware that actions leading to exceeded quotas 
will be denied. This will result in a degraded service.\n" +
+                "To mitigate this issue you might reach your administrator in 
order to increase your configured quota. You might also delete some non 
important emails.");
+    }
+
+    @Test
+    void generateReportShouldOmitCountPartWhenNone() {
+        QuotaThresholdChange sizeThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._72_PERCENT)
+            
.sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            .build()
+            .get()
+            .generateReport())
+            .isEqualTo("You receive this email because you recently exceeded a 
threshold related to the quotas of your email account.\n" +
+                "\n" +
+                "You currently occupy more than 80 % of the total size 
allocated to you.\n" +
+                "You currently occupy 82 bytes on a total of 100 bytes 
allocated to you.\n" +
+                "\n" +
+                "You need to be aware that actions leading to exceeded quotas 
will be denied. This will result in a degraded service.\n" +
+                "To mitigate this issue you might reach your administrator in 
order to increase your configured quota. You might also delete some non 
important emails.");
+    }
+
+    @Test
+    void generateReportShouldOmitSizePartWhenNone() {
+        QuotaThresholdChange countThresholdChange = new 
QuotaThresholdChange(_80, NOW);
+
+        assertThat(QuotaThresholdNotice.builder()
+            .sizeQuota(Sizes._82_PERCENT)
+            .countQuota(Counts._92_PERCENT)
+            
.countThreshold(HistoryEvolution.higherThresholdReached(countThresholdChange, 
NotAlreadyReachedDuringGracePeriod))
+            .build()
+            .get()
+            .generateReport())
+            .isEqualTo("You receive this email because you recently exceeded a 
threshold related to the quotas of your email account.\n" +
+                "\n" +
+                "You currently occupy more than 80 % of the total message 
count allocated to you.\n" +
+                "You currently have 92 messages on a total of 100 allowed for 
you.\n" +
+                "\n" +
+                "You need to be aware that actions leading to exceeded quotas 
will be denied. This will result in a degraded service.\n" +
+                "To mitigate this issue you might reach your administrator in 
order to increase your configured quota. You might also delete some non 
important emails.");
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/HistoryEvolutionTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/HistoryEvolutionTest.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/HistoryEvolutionTest.java
new file mode 100644
index 0000000..566991c
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/HistoryEvolutionTest.java
@@ -0,0 +1,104 @@
+/****************************************************************
+ * 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.mailbox.quota.model;
+
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.NOW;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._75;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class HistoryEvolutionTest {
+
+    private static final QuotaThresholdChange SAMPLE_THRESHOLD = new 
QuotaThresholdChange(_75, NOW);
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(HistoryEvolution.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    void isModifiedShouldReturnFalseWhenNoChange() {
+        assertThat(
+            HistoryEvolution.noChanges()
+                .isChange())
+            .isFalse();
+    }
+
+    @Test
+    void isModifiedShouldReturnTrueWhenLowerThresholdReached() {
+        assertThat(
+            HistoryEvolution.lowerThresholdReached(SAMPLE_THRESHOLD)
+                .isChange())
+            .isTrue();
+    }
+
+    @Test
+    void 
isModifiedShouldReturnTrueWhenHigherThresholdAlreadyReachedWithinGracePeriod() {
+        assertThat(
+            HistoryEvolution.higherThresholdReached(SAMPLE_THRESHOLD, 
HistoryEvolution.HighestThresholdRecentness.AlreadyReachedDuringGracePriod)
+                .isChange())
+            .isTrue();
+    }
+
+    @Test
+    void 
isModifiedShouldReturnTrueWhenHigherThresholdReachedNotAlreadyReachedWithinGracePeriod()
 {
+        assertThat(
+            HistoryEvolution.higherThresholdReached(SAMPLE_THRESHOLD, 
HistoryEvolution.HighestThresholdRecentness.NotAlreadyReachedDuringGracePeriod)
+                .isChange())
+            .isTrue();
+    }
+
+    @Test
+    void currentThresholdNotRecentlyReachedShouldReturnFalseWhenNoChange() {
+        assertThat(
+            HistoryEvolution.noChanges()
+                .currentThresholdNotRecentlyReached())
+            .isFalse();
+    }
+
+    @Test
+    void 
currentThresholdNotRecentlyReachedShouldReturnFalseWhenLowerThresholdReached() {
+        assertThat(
+            HistoryEvolution.lowerThresholdReached(SAMPLE_THRESHOLD)
+                .currentThresholdNotRecentlyReached())
+            .isFalse();
+    }
+
+    @Test
+    void 
currentThresholdNotRecentlyReachedShouldReturnFalseWhenHigherThresholdReachedAlreadyReachedWithinGracePeriod()
 {
+        assertThat(
+            HistoryEvolution.higherThresholdReached(SAMPLE_THRESHOLD, 
HistoryEvolution.HighestThresholdRecentness.AlreadyReachedDuringGracePriod)
+                .currentThresholdNotRecentlyReached())
+            .isFalse();
+    }
+
+    @Test
+    void 
currentThresholdNotRecentlyReachedShouldReturnTrueWhenHigherThresholdReachedNotAlreadyReachedWithinGracePeriod()
 {
+        assertThat(
+            HistoryEvolution.higherThresholdReached(SAMPLE_THRESHOLD, 
HistoryEvolution.HighestThresholdRecentness.NotAlreadyReachedDuringGracePeriod)
+                .currentThresholdNotRecentlyReached())
+            .isTrue();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdChangeTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdChangeTest.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdChangeTest.java
new file mode 100644
index 0000000..5fb150e
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdChangeTest.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.quota.model;
+
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.ONE_HOUR_AGO;
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.THREE_HOURS_AGO;
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.TWO_HOURS_AGO;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._75;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class QuotaThresholdChangeTest {
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(QuotaThresholdChange.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    void isAfterShouldReturnTrueWhenRecent() {
+        QuotaThresholdChange change = new QuotaThresholdChange(_75, 
TWO_HOURS_AGO);
+        assertThat(change.isAfter(THREE_HOURS_AGO)).isTrue();
+    }
+
+    @Test
+    void isAfterShouldReturnFalseWhenOld() {
+        QuotaThresholdChange change = new QuotaThresholdChange(_75, 
TWO_HOURS_AGO);
+
+        assertThat(change.isAfter(ONE_HOUR_AGO)).isFalse();
+    }
+
+    @Test
+    void isAfterShouldReturnFalseWhenSameInstant() {
+        QuotaThresholdChange change = new QuotaThresholdChange(_75, 
TWO_HOURS_AGO);
+
+        assertThat(change.isAfter(TWO_HOURS_AGO)).isFalse();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdFixture.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdFixture.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdFixture.java
new file mode 100644
index 0000000..1770420
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdFixture.java
@@ -0,0 +1,149 @@
+/****************************************************************
+ * 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.mailbox.quota.model;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
+
+import org.apache.james.mailbox.mock.MockMailboxSession;
+import org.apache.james.mailbox.model.Quota;
+import org.apache.james.mailbox.model.QuotaRoot;
+import org.apache.james.mailbox.quota.QuotaCount;
+import org.apache.james.mailbox.quota.QuotaSize;
+import 
org.apache.james.mailbox.quota.mailing.QuotaMailingListenerConfiguration;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMailContext;
+
+import com.google.common.collect.ImmutableList;
+
+public interface QuotaThresholdFixture {
+    QuotaThreshold _50 = new QuotaThreshold(0.50);
+    QuotaThreshold _75 = new QuotaThreshold(0.75);
+    QuotaThreshold _759 = new QuotaThreshold(0.759);
+    QuotaThreshold _80 = new QuotaThreshold(0.8);
+    QuotaThreshold _90 = new QuotaThreshold(0.9);
+    QuotaThreshold _95 = new QuotaThreshold(0.95);
+    QuotaThreshold _99 = new QuotaThreshold(0.99);
+
+    interface Quotas {
+        interface Counts {
+            Quota<QuotaCount> _32_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(32))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+
+            Quota<QuotaCount> _40_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(40))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+
+            Quota<QuotaCount> _52_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(52))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+
+            Quota<QuotaCount> _72_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(72))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+
+            Quota<QuotaCount> _82_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(82))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+
+            Quota<QuotaCount> _85_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(85))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+
+            Quota<QuotaCount> _92_PERCENT = Quota.<QuotaCount>builder()
+                .used(QuotaCount.count(92))
+                .computedLimit(QuotaCount.count(100))
+                .build();
+        }
+        interface Sizes {
+            Quota<QuotaSize> _30_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(30))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+            Quota<QuotaSize> _42_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(42))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+
+            Quota<QuotaSize> _55_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(55))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+
+            Quota<QuotaSize> _60_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(60))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+
+            Quota<QuotaSize> _75_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(75))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+
+            Quota<QuotaSize> _82_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(82))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+
+            Quota<QuotaSize> _92_PERCENT = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(92))
+                .computedLimit(QuotaSize.size(100))
+                .build();
+
+            Quota<QuotaSize> _992_PERTHOUSAND = Quota.<QuotaSize>builder()
+                .used(QuotaSize.size(992))
+                .computedLimit(QuotaSize.size(1000))
+                .build();
+        }
+    }
+
+    interface TestConstants {
+        Duration GRACE_PERIOD = Duration.ofDays(1);
+        QuotaThresholds SINGLE_THRESHOLD = new 
QuotaThresholds(ImmutableList.of(_50));
+        QuotaMailingListenerConfiguration DEFAULT_CONFIGURATION = new 
QuotaMailingListenerConfiguration(SINGLE_THRESHOLD, GRACE_PERIOD);
+        String BOB = "bob@domain";
+        MockMailboxSession BOB_SESSION = new MockMailboxSession(BOB);
+        Instant NOW = Instant.now();
+        QuotaRoot QUOTAROOT = QuotaRoot.quotaRoot("any", Optional.empty());
+        Instant ONE_HOUR_AGO = NOW.minus(Duration.ofHours(1));
+        Instant TWO_HOURS_AGO = NOW.minus(Duration.ofHours(2));
+        Instant THREE_HOURS_AGO = NOW.minus(Duration.ofHours(3));
+        Instant SIX_HOURS_AGO = NOW.minus(Duration.ofHours(6));
+        Instant TWELVE_HOURS_AGO = NOW.minus(Duration.ofHours(12));
+        Instant TWO_DAYS_AGO = NOW.minus(Duration.ofDays(2));
+        Instant SIX_DAYS_AGO = NOW.minus(Duration.ofDays(6));
+        Instant TWELVE_DAYS_AGO = NOW.minus(Duration.ofDays(12));
+    }
+
+    static FakeMailContext mailetContext() {
+        return FakeMailContext.builder()
+            .postmaster(MailAddressFixture.POSTMASTER_AT_JAMES)
+            .build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistoryTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistoryTest.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistoryTest.java
new file mode 100644
index 0000000..9ea774d
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdHistoryTest.java
@@ -0,0 +1,87 @@
+/****************************************************************
+ * 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.mailbox.quota.model;
+
+import static 
org.apache.james.mailbox.quota.model.HistoryEvolution.HighestThresholdRecentness.AlreadyReachedDuringGracePriod;
+import static 
org.apache.james.mailbox.quota.model.HistoryEvolution.HighestThresholdRecentness.NotAlreadyReachedDuringGracePeriod;
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.GRACE_PERIOD;
+import static 
org.apache.james.mailbox.quota.model.QuotaThresholdFixture.TestConstants.NOW;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._50;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._75;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Duration;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class QuotaThresholdHistoryTest {
+
+    @Test
+    public void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(QuotaThresholdHistory.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void 
compareWithCurrentThresholdShouldReturnAboveWhenStrictlyAboveDuringDuration() {
+        assertThat(
+            new QuotaThresholdHistory(
+                    new QuotaThresholdChange(_50, 
NOW.minus(Duration.ofDays(24))),
+                    new QuotaThresholdChange(_75, 
NOW.minus(Duration.ofDays(12))),
+                    new QuotaThresholdChange(_50, 
NOW.minus(Duration.ofDays(6))))
+                .compareWithCurrentThreshold(new QuotaThresholdChange(_75, 
NOW), GRACE_PERIOD))
+            .isEqualTo(HistoryEvolution.higherThresholdReached(new 
QuotaThresholdChange(_75, NOW), NotAlreadyReachedDuringGracePeriod));
+    }
+
+    @Test
+    public void 
compareWithCurrentThresholdShouldReturnBelowWhenLowerThanLastChange() {
+        assertThat(
+            new QuotaThresholdHistory(
+                new QuotaThresholdChange(_50, NOW.minus(Duration.ofDays(24))),
+                new QuotaThresholdChange(_75, NOW.minus(Duration.ofDays(12))))
+                .compareWithCurrentThreshold(new QuotaThresholdChange(_50, 
NOW), GRACE_PERIOD))
+            .isEqualTo(HistoryEvolution.lowerThresholdReached(new 
QuotaThresholdChange(_50, NOW)));
+    }
+
+    @Test
+    public void 
compareWithCurrentThresholdShouldReturnNoChangeWhenEqualsLastChange() {
+        assertThat(
+            new QuotaThresholdHistory(
+                new QuotaThresholdChange(_50, NOW.minus(Duration.ofDays(24))),
+                new QuotaThresholdChange(_75, NOW.minus(Duration.ofDays(12))))
+                .compareWithCurrentThreshold(new QuotaThresholdChange(_75, 
NOW), GRACE_PERIOD))
+            .isEqualTo(HistoryEvolution.noChanges());
+    }
+
+    @Test
+    public void 
compareWithCurrentThresholdShouldReturnAboveWithRecentChangesWhenThresholdExceededDuringDuration()
 {
+        assertThat(
+            new QuotaThresholdHistory(
+                    new QuotaThresholdChange(_50, 
NOW.minus(Duration.ofDays(24))),
+                    new QuotaThresholdChange(_75, 
NOW.minus(Duration.ofHours(12))),
+                    new QuotaThresholdChange(_50, 
NOW.minus(Duration.ofHours(6))))
+                .compareWithCurrentThreshold(new QuotaThresholdChange(_75, 
NOW), GRACE_PERIOD))
+            .isEqualTo(HistoryEvolution.higherThresholdReached(new 
QuotaThresholdChange(_75, NOW), AlreadyReachedDuringGracePriod));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdTest.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdTest.java
new file mode 100644
index 0000000..f46eb38
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdTest.java
@@ -0,0 +1,141 @@
+/****************************************************************
+ * 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.mailbox.quota.model;
+
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._75;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._759;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._90;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.mailbox.model.Quota;
+import org.apache.james.mailbox.quota.QuotaSize;
+import org.apache.james.mailbox.quota.model.QuotaThresholdFixture.Quotas.Sizes;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class QuotaThresholdTest {
+
+    @Test
+    public void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(QuotaThreshold.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void constructorShouldThrowBelowLowerValue() {
+        assertThatThrownBy(() -> new QuotaThreshold(-0.00001))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+
+    @Test
+    public void constructorShouldThrowAboveUpperValue() {
+        assertThatThrownBy(() -> new QuotaThreshold(1.00001))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void constructorShouldNotThrowOnLowerValue() {
+        assertThatCode(() -> new QuotaThreshold(0.))
+            .doesNotThrowAnyException();
+    }
+
+    @Test
+    public void constructorShouldNotThrowOnUpperValue() {
+        assertThatCode(() -> new QuotaThreshold(1.))
+            .doesNotThrowAnyException();
+    }
+
+    @Test
+    public void isExceededShouldReturnFalseWhenBelowThreshold() {
+        assertThat(_75.isExceeded(Sizes._60_PERCENT))
+            .isFalse();
+    }
+
+    @Test
+    public void isExceededShouldReturnTrueWhenAboveThreshold() {
+        assertThat(_75.isExceeded(Sizes._82_PERCENT))
+            .isTrue();
+    }
+
+    @Test
+    public void isExceededShouldReturnFalseWhenOnThreshold() {
+        assertThat(_75.isExceeded(Sizes._75_PERCENT))
+            .isFalse();
+    }
+
+    @Test
+    public void isExceededShouldReturnFalseWhenUnlimited() {
+        Quota<QuotaSize> quota = Quota.<QuotaSize>builder()
+            .computedLimit(QuotaSize.unlimited())
+            .used(QuotaSize.size(80))
+            .build();
+
+        assertThat(_75.isExceeded(quota))
+            .isFalse();
+    }
+
+    @Test
+    public void nonZeroShouldFilterZero() {
+        assertThat(QuotaThreshold.ZERO.nonZero())
+            .isEmpty();
+    }
+
+    @Test
+    public void nonZeroShouldNotFilterNonZeroValues() {
+        assertThat(_75.nonZero())
+            .contains(_75);
+    }
+
+    @Test
+    public void 
getQuotaOccupationRatioAsPercentShouldReturnIntRepresentationOfThreshold() {
+        assertThat(_75.getQuotaOccupationRatioAsPercent())
+            .isEqualTo(75);
+    }
+
+    @Test
+    public void getQuotaOccupationRatioAsPercentShouldTruncateValues() {
+        assertThat(_759.getQuotaOccupationRatioAsPercent())
+            .isEqualTo(75);
+    }
+
+    @Test
+    public void compareToShouldReturnNegativeWhenLowerThanComparedValue() {
+        assertThat(_75.compareTo(_90))
+            .isLessThan(0);
+    }
+
+    @Test
+    public void compareToShouldReturnPositiveWhenHigherThanComparedValue() {
+        assertThat(_90.compareTo(_75))
+            .isGreaterThan(0);
+    }
+
+    @Test
+    public void compareToShouldReturnZeroWhenEquals() {
+        assertThat(_75.compareTo(_75))
+            .isEqualTo(0);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdsTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdsTest.java
 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdsTest.java
new file mode 100644
index 0000000..09245eb
--- /dev/null
+++ 
b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/model/QuotaThresholdsTest.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.quota.model;
+
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._50;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._80;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._95;
+import static org.apache.james.mailbox.quota.model.QuotaThresholdFixture._99;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.mailbox.model.Quota;
+import org.apache.james.mailbox.quota.QuotaSize;
+import org.apache.james.mailbox.quota.model.QuotaThresholdFixture.Quotas.Sizes;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class QuotaThresholdsTest {
+
+    @Test
+    public void 
highestExceededThresholdShouldReturnZeroWhenBelowAllThresholds() {
+        assertThat(
+            new QuotaThresholds(ImmutableList.of(_50, _80, _95, _99))
+                .highestExceededThreshold(Quota.<QuotaSize>builder()
+                    .used(QuotaSize.size(40))
+                    .computedLimit(QuotaSize.size(100))
+                    .build()))
+            .isEqualTo(QuotaThreshold.ZERO);
+    }
+
+    @Test
+    public void highestExceededThresholdShouldReturnHighestExceededThreshold() 
{
+        assertThat(
+            new QuotaThresholds(ImmutableList.of(_50, _80, _95, _99))
+                .highestExceededThreshold(Sizes._92_PERCENT))
+            .isEqualTo(_80);
+    }
+
+    @Test
+    public void 
highestExceededThresholdShouldReturnHighestThresholdWhenAboveAllThresholds() {
+        assertThat(
+            new QuotaThresholds(ImmutableList.of(_50, _80, _95, _99))
+                .highestExceededThreshold(Sizes._992_PERTHOUSAND))
+            .isEqualTo(_99);
+    }
+
+    @Test
+    public void highestExceededThresholdShouldReturnZeroWhenNoThresholds() {
+        assertThat(
+            new QuotaThresholds(ImmutableList.of())
+                .highestExceededThreshold(Sizes._992_PERTHOUSAND))
+            .isEqualTo(QuotaThreshold.ZERO);
+    }
+
+    @Test
+    public void highestExceededThresholdShouldReturnZeroWhenUnlimitedQuota() {
+        assertThat(
+            new QuotaThresholds(ImmutableList.of(_50, _80, _95, _99))
+                .highestExceededThreshold(Quota.<QuotaSize>builder()
+                    .used(QuotaSize.size(992))
+                    .computedLimit(QuotaSize.unlimited())
+                    .build()))
+            .isEqualTo(QuotaThreshold.ZERO);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/pom.xml b/mailbox/pom.xml
index a8204d2..560d59b 100644
--- a/mailbox/pom.xml
+++ b/mailbox/pom.xml
@@ -53,6 +53,8 @@
         <module>tool</module>
         <module>zoo-seq-provider</module>
 
+        <module>plugin/quota-mailing</module>
+        <module>plugin/quota-mailing-memory</module>
         <module>plugin/spamassassin</module>
     </modules>
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
----------------------------------------------------------------------
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
index 37779cb..80dcc18 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/event/MailboxEventDispatcher.java
@@ -19,12 +19,14 @@
 
 package org.apache.james.mailbox.store.event;
 
+import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedMap;
 
 import javax.inject.Inject;
 
+import org.apache.james.mailbox.Event;
 import org.apache.james.mailbox.MailboxListener;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageUid;
@@ -167,6 +169,10 @@ public class MailboxEventDispatcher {
     }
 
     public void quota(MailboxSession session, QuotaRoot quotaRoot, 
Quota<QuotaCount> countQuota, Quota<QuotaSize> sizeQuota) {
-        listener.event(new MailboxListener.QuotaUsageUpdatedEvent(session, 
quotaRoot, countQuota, sizeQuota));
+        listener.event(new MailboxListener.QuotaUsageUpdatedEvent(session, 
quotaRoot, countQuota, sizeQuota, Instant.now()));
+    }
+
+    public void event(Event event) {
+        listener.event(event);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/mailet/base/src/test/java/org/apache/mailet/base/MailAddressFixture.java
----------------------------------------------------------------------
diff --git 
a/mailet/base/src/test/java/org/apache/mailet/base/MailAddressFixture.java 
b/mailet/base/src/test/java/org/apache/mailet/base/MailAddressFixture.java
index fed98ba..24ebc74 100644
--- a/mailet/base/src/test/java/org/apache/mailet/base/MailAddressFixture.java
+++ b/mailet/base/src/test/java/org/apache/mailet/base/MailAddressFixture.java
@@ -38,6 +38,7 @@ public class MailAddressFixture {
     public static final MailAddress ANY_AT_LOCAL = createMailAddress("any@" + 
JAMES_LOCAL);
     public static final MailAddress OTHER_AT_LOCAL = 
createMailAddress("other@" + JAMES_LOCAL);
     public static final MailAddress ANY_AT_JAMES = createMailAddress("any@" + 
JAMES_APACHE_ORG);
+    public static final MailAddress POSTMASTER_AT_JAMES = 
createMailAddress("postmaster@" + JAMES_APACHE_ORG);
     public static final MailAddress OTHER_AT_JAMES = 
createMailAddress("other@" + JAMES_APACHE_ORG);
     public static final MailAddress ANY_AT_JAMES2 = createMailAddress("any@" + 
JAMES2_APACHE_ORG);
     public static final MailAddress OTHER_AT_JAMES2 = 
createMailAddress("other@" + JAMES2_APACHE_ORG);

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/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 687b996..eff41c9 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
@@ -20,7 +20,6 @@
 package org.apache.mailet.base.test;
 
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -28,6 +27,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.TimeUnit;
 
 import javax.mail.MessagingException;
@@ -365,15 +365,15 @@ public class FakeMailContext implements MailetContext {
     }
 
     private final HashMap<String, Object> attributes;
-    private final List<SentMail> sentMails;
-    private final List<BouncedMail> bouncedMails;
+    private final Collection<SentMail> sentMails;
+    private final Collection<BouncedMail> bouncedMails;
     private final Optional<Logger> logger;
     private final MailAddress postmaster;
 
     private FakeMailContext(Optional<Logger> logger, MailAddress postmaster) {
         attributes = new HashMap<>();
-        sentMails = new ArrayList<>();
-        bouncedMails = new ArrayList<>();
+        sentMails = new ConcurrentLinkedQueue<>();
+        bouncedMails = new ConcurrentLinkedQueue<>();
         this.logger = logger;
         this.postmaster = postmaster;
     }
@@ -590,11 +590,15 @@ public class FakeMailContext implements MailetContext {
     }
 
     public List<SentMail> getSentMails() {
-        return sentMails;
+        return ImmutableList.copyOf(sentMails);
+    }
+
+    public void resetSentMails() {
+        sentMails.clear();
     }
 
     public List<BouncedMail> getBouncedMails() {
-        return bouncedMails;
+        return ImmutableList.copyOf(bouncedMails);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/0e437d25/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 9c296f1..9ba6851 100644
--- a/pom.xml
+++ b/pom.xml
@@ -824,6 +824,17 @@
             </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
+                <artifactId>apache-james-mailbox-quota-mailing</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>apache-james-mailbox-quota-mailing</artifactId>
+                <type>test-jar</type>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
                 <artifactId>apache-james-mailbox-spring</artifactId>
                 <version>${project.version}</version>
             </dependency>


---------------------------------------------------------------------
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