JAMES-3946 DropList Cassandra and Memory app (#2263)

diff --git a/server/apps/distributed-app/docs/modules/ROOT/nav.adoc b/server/apps/distributed-app/docs/modules/ROOT/nav.adoc
index bf98a74..60b767f 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/nav.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/nav.adoc
@@ -34,6 +34,7 @@
 **** xref:configure/batchsizes.adoc[batchsizes.properties]
 **** xref:configure/dns.adoc[dnsservice.xml]
 **** xref:configure/domainlist.adoc[domainlist.xml]
+**** xref:configure/droplists.adoc[DropLists]
 **** xref:configure/healthcheck.adoc[healthcheck.properties]
 **** xref:configure/mailetcontainer.adoc[mailetcontainer.xml]
 **** xref:configure/mailets.adoc[Packaged Mailets]
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/droplists.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/droplists.adoc
new file mode 100644
index 0000000..e63a846
--- /dev/null
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/droplists.adoc
@@ -0,0 +1,32 @@
+= Distributed James Server — DropLists
+:navtitle: DropLists
+
+The DropList, also known as the mail blacklist, is a collection of
+domains and email addresses that are denied from sending emails within the system.
+It is disabled by default.
+To enable it, modify the `droplists.properties` file and include the `IsInDropList` matcher in the `mailetcontainer.xml`.
+To disable it, adjust the `droplists.properties` file and remove the `IsInDropList` matcher from the `mailetcontainer.xml`.
+
+.droplists.properties content
+|===
+| Property name | explanation
+
+| enabled
+| Boolean. Governs whether DropLists should be enabled. Defaults to `false`.
+|===
+
+== Enabling Matcher
+
+Plug the `IsInDropList` matcher within `mailetcontainer.xml` :
+
+....
+<mailet match="org.apache.james.transport.matchers.IsInDropList" class="ToProcessor">
+    <processor>transport</processor>
+</mailet>
+....
+
+== DropList management
+
+DropList management, including adding and deleting entries, is performed through the WebAdmin REST API.
+
+See xref:operate/webadmin.adoc#_administrating_droplists[WebAdmin DropLists].
\ No newline at end of file
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc
index 2fa9d90..bc9b0f9 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc
@@ -66,6 +66,7 @@
 ** xref:configure/collecting-contacts.adoc[This page] documents contact collection
 ** xref:configure/collecting-events.adoc[This page] documents event collection
 ** xref:configure/dsn.adoc[this page] specified how to support SMTP Delivery Submission Notification (link:https://tools.ietf.org/html/rfc3461[RFC-3461])
+** xref:configure/droplists.adoc[This page] allows configuring drop lists.
 
 == System properties
 
diff --git a/server/apps/distributed-app/sample-configuration/droplists.properties b/server/apps/distributed-app/sample-configuration/droplists.properties
new file mode 100644
index 0000000..bbc2756
--- /dev/null
+++ b/server/apps/distributed-app/sample-configuration/droplists.properties
@@ -0,0 +1,3 @@
+# Configuration file for DropLists
+
+enabled=false
\ No newline at end of file
diff --git a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java
index eb4bbce..c4b6436 100644
--- a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java
+++ b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java
@@ -52,6 +52,7 @@
         private Optional<VaultConfiguration> vaultConfiguration;
         private Optional<Boolean> jmapEnabled;
         private Optional<Boolean> quotaCompatibilityMode;
+        private Optional<Boolean> dropListsEnabled;
 
         private Builder() {
             searchConfiguration = Optional.empty();
@@ -64,6 +65,7 @@
             vaultConfiguration = Optional.empty();
             jmapEnabled = Optional.empty();
             quotaCompatibilityMode = Optional.empty();
+            dropListsEnabled = Optional.empty();
         }
 
         public Builder workingDirectory(String path) {
@@ -134,6 +136,11 @@
             return this;
         }
 
+        public Builder enableDropLists() {
+            this.dropListsEnabled = Optional.of(true);
+            return this;
+        }
+
         public CassandraRabbitMQJamesConfiguration build() {
             ConfigurationPath configurationPath = this.configurationPath.orElse(new ConfigurationPath(FileSystem.FILE_PROTOCOL_AND_CONF));
             JamesServerResourceLoader directories = new JamesServerResourceLoader(rootDirectory
@@ -190,6 +197,16 @@
                 }
             });
 
+            boolean dropListsEnabled = this.dropListsEnabled.orElseGet(() -> {
+                try {
+                    return propertiesProvider.getConfiguration("droplists").getBoolean("enabled", false);
+                } catch (FileNotFoundException e) {
+                    return false;
+                } catch (ConfigurationException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+
             return new CassandraRabbitMQJamesConfiguration(
                 configurationPath,
                 directories,
@@ -199,7 +216,8 @@
                 mailQueueChoice,
                 mailQueueViewChoice, vaultConfiguration,
                 jmapEnabled,
-                quotaCompatibilityMode);
+                quotaCompatibilityMode,
+                dropListsEnabled);
         }
     }
 
@@ -217,12 +235,13 @@
     private final VaultConfiguration vaultConfiguration;
     private final boolean jmapEnabled;
     private final boolean quotaCompatibilityMode;
+    private final boolean dropListsEnabled;
 
     public CassandraRabbitMQJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories,
                                                BlobStoreConfiguration blobStoreConfiguration, SearchConfiguration searchConfiguration,
                                                UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation, MailQueueChoice mailQueueChoice,
                                                MailQueueViewChoice mailQueueViewChoice, VaultConfiguration vaultConfiguration,
-                                               boolean jmapEnabled, boolean quotaCompatibilityMode) {
+                                               boolean jmapEnabled, boolean quotaCompatibilityMode, boolean dropListsEnabled) {
         this.configurationPath = configurationPath;
         this.directories = directories;
         this.blobStoreConfiguration = blobStoreConfiguration;
@@ -233,6 +252,7 @@
         this.vaultConfiguration = vaultConfiguration;
         this.jmapEnabled = jmapEnabled;
         this.quotaCompatibilityMode = quotaCompatibilityMode;
+        this.dropListsEnabled = dropListsEnabled;
     }
 
     public MailQueueViewChoice getMailQueueViewChoice() {
@@ -276,4 +296,8 @@
     public boolean isQuotaCompatibilityMode() {
         return quotaCompatibilityMode;
     }
+
+    public boolean isDropListsEnabled() {
+        return dropListsEnabled;
+    }
 }
diff --git a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java
index aebbf32..e0c750f 100644
--- a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java
+++ b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java
@@ -41,6 +41,7 @@
 import org.apache.james.modules.data.CassandraDLPConfigurationStoreModule;
 import org.apache.james.modules.data.CassandraDelegationStoreModule;
 import org.apache.james.modules.data.CassandraDomainListModule;
+import org.apache.james.modules.data.CassandraDropListsModule;
 import org.apache.james.modules.data.CassandraJmapModule;
 import org.apache.james.modules.data.CassandraRecipientRewriteTableModule;
 import org.apache.james.modules.data.CassandraSieveQuotaLegacyModule;
@@ -75,6 +76,7 @@
 import org.apache.james.modules.server.DKIMMailetModule;
 import org.apache.james.modules.server.DLPRoutesModule;
 import org.apache.james.modules.server.DataRoutesModules;
+import org.apache.james.modules.server.DropListsRoutesModule;
 import org.apache.james.modules.server.InconsistencyQuotasSolvingRoutesModule;
 import org.apache.james.modules.server.JMXServerModule;
 import org.apache.james.modules.server.JmapTasksModule;
@@ -209,7 +211,8 @@
                 .chooseModules(configuration.getUsersRepositoryImplementation()))
             .combineWith(chooseDeletedMessageVault(configuration.getVaultConfiguration()))
             .combineWith(chooseQuotaModule(configuration))
-            .overrideWith(chooseJmapModules(configuration));
+            .overrideWith(chooseJmapModules(configuration))
+            .overrideWith(chooseDropListsModule(configuration));
     }
 
     private static Module chooseMailQueue(CassandraRabbitMQJamesConfiguration configuration) {
@@ -258,4 +261,14 @@
             return Modules.combine(new CassandraMailboxQuotaModule(), new CassandraSieveQuotaModule());
         }
     }
+
+    private static Module chooseDropListsModule(CassandraRabbitMQJamesConfiguration configuration) {
+        if (configuration.isDropListsEnabled()) {
+            return Modules.combine(new CassandraDropListsModule(), new DropListsRoutesModule());
+        }
+        return binder -> {
+
+        };
+    }
+
 }
diff --git a/server/apps/memory-app/sample-configuration/droplists.properties b/server/apps/memory-app/sample-configuration/droplists.properties
new file mode 100644
index 0000000..bbc2756
--- /dev/null
+++ b/server/apps/memory-app/sample-configuration/droplists.properties
@@ -0,0 +1,3 @@
+# Configuration file for DropLists
+
+enabled=false
\ No newline at end of file
diff --git a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java
index 88bfff3..aa0e08f 100644
--- a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java
+++ b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java
@@ -41,12 +41,14 @@
         private Optional<ConfigurationPath> configurationPath;
         private Optional<UsersRepositoryModuleChooser.Implementation> usersRepositoryImplementation;
         private Optional<Boolean> jmapEnabled;
+        private Optional<Boolean> dropListsEnabled;
 
         private Builder() {
             rootDirectory = Optional.empty();
             configurationPath = Optional.empty();
             usersRepositoryImplementation = Optional.empty();
             jmapEnabled = Optional.empty();
+            dropListsEnabled = Optional.empty();
         }
 
         public Builder workingDirectory(String path) {
@@ -87,6 +89,11 @@
             return this;
         }
 
+        public Builder enableDropLists() {
+            this.dropListsEnabled = Optional.of(true);
+            return this;
+        }
+
         public MemoryJamesConfiguration build() {
             ConfigurationPath configurationPath = this.configurationPath.orElse(new ConfigurationPath(FileSystem.FILE_PROTOCOL_AND_CONF));
             JamesServerResourceLoader directories = new JamesServerResourceLoader(rootDirectory
@@ -112,10 +119,22 @@
                 }
             });
 
+            boolean dropListsEnabled = this.dropListsEnabled.orElseGet(() -> {
+                PropertiesProvider propertiesProvider = new PropertiesProvider(fileSystem, configurationPath);
+                try {
+                    return propertiesProvider.getConfiguration("droplists").getBoolean("enabled", false);
+                } catch (FileNotFoundException e) {
+                    return false;
+                } catch (ConfigurationException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+
+
             return new MemoryJamesConfiguration(
                 configurationPath,
                 directories,
-                usersRepositoryChoice, jmapEnabled);
+                usersRepositoryChoice, jmapEnabled, dropListsEnabled);
         }
     }
 
@@ -127,12 +146,16 @@
     private final JamesDirectoriesProvider directories;
     private final UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation;
     private final boolean jmapEnabled;
+    private final boolean dropListsEnabled;
 
-    public MemoryJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories, UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation, boolean jmapEnabled) {
+    public MemoryJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories,
+                                    UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation,
+                                    boolean jmapEnabled, boolean dropListsEnabled) {
         this.configurationPath = configurationPath;
         this.directories = directories;
         this.usersRepositoryImplementation = usersRepositoryImplementation;
         this.jmapEnabled = jmapEnabled;
+        this.dropListsEnabled = dropListsEnabled;
     }
 
     @Override
@@ -152,4 +175,8 @@
     public boolean isJmapEnabled() {
         return jmapEnabled;
     }
+
+    public boolean isDropListsEnabled() {
+        return dropListsEnabled;
+    }
 }
diff --git a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java
index 959c3fe..f1797b5 100644
--- a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -34,6 +34,7 @@
 import org.apache.james.modules.data.MemoryDataJmapModule;
 import org.apache.james.modules.data.MemoryDataModule;
 import org.apache.james.modules.data.MemoryDelegationStoreModule;
+import org.apache.james.modules.data.MemoryDropListsModule;
 import org.apache.james.modules.data.MemoryUsersRepositoryModule;
 import org.apache.james.modules.eventstore.MemoryEventStoreModule;
 import org.apache.james.modules.mailbox.MemoryMailboxModule;
@@ -49,6 +50,7 @@
 import org.apache.james.modules.server.DKIMMailetModule;
 import org.apache.james.modules.server.DLPRoutesModule;
 import org.apache.james.modules.server.DataRoutesModules;
+import org.apache.james.modules.server.DropListsRoutesModule;
 import org.apache.james.modules.server.InconsistencyQuotasSolvingRoutesModule;
 import org.apache.james.modules.server.JMXServerModule;
 import org.apache.james.modules.server.JmapTasksModule;
@@ -174,7 +176,8 @@
             .combineWith(IN_MEMORY_SERVER_AGGREGATE_MODULE)
             .combineWith(new UsersRepositoryModuleChooser(new MemoryUsersRepositoryModule())
                 .chooseModules(configuration.getUsersRepositoryImplementation()))
-            .combineWith(chooseJmapModule(configuration));
+            .combineWith(chooseJmapModule(configuration))
+            .combineWith(chooseDropListsModule(configuration));
     }
 
     private static Module chooseJmapModule(MemoryJamesConfiguration configuration) {
@@ -186,4 +189,13 @@
         };
     }
 
+    private static Module chooseDropListsModule(MemoryJamesConfiguration configuration) {
+        if (configuration.isDropListsEnabled()) {
+            return Modules.combine(new MemoryDropListsModule(), new DropListsRoutesModule());
+        }
+        return binder -> {
+
+        };
+    }
+
 }
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraDropListsModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraDropListsModule.java
new file mode 100644
index 0000000..450cc85
--- /dev/null
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraDropListsModule.java
@@ -0,0 +1,33 @@
+/****************************************************************
+ * 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.modules.data;
+
+import org.apache.james.droplists.api.DropList;
+import org.apache.james.droplists.cassandra.CassandraDropList;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+
+public class CassandraDropListsModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind(DropList.class).to(CassandraDropList.class).in(Scopes.SINGLETON);
+    }
+}
\ No newline at end of file
diff --git a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDropListsModule.java b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDropListsModule.java
new file mode 100644
index 0000000..7810e73
--- /dev/null
+++ b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDropListsModule.java
@@ -0,0 +1,33 @@
+/****************************************************************
+ * 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.modules.data;
+
+import org.apache.james.droplists.api.DropList;
+import org.apache.james.droplists.memory.MemoryDropList;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+
+public class MemoryDropListsModule extends AbstractModule {
+    @Override
+    public void configure() {
+        bind(DropList.class).to(MemoryDropList.class).in(Scopes.SINGLETON);
+    }
+}
\ No newline at end of file
diff --git a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DropListsRoutesModule.java b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DropListsRoutesModule.java
new file mode 100644
index 0000000..5a06422
--- /dev/null
+++ b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DropListsRoutesModule.java
@@ -0,0 +1,34 @@
+/****************************************************************
+ * 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.modules.server;
+
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.routes.DropListRoutes;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.Multibinder;
+
+public class DropListsRoutesModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        Multibinder.newSetBinder(binder(), Routes.class).addBinding()
+            .to(DropListRoutes.class);
+    }
+}