You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/04/17 00:31:52 UTC

[james-project] branch master updated (58af5c3 -> ee0028e)

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

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


    from 58af5c3  JAMES-3149 Reactive GetMessageList
     new 58322e7  JAMES-3117 Add logs for HealthChecks for unhealthy/degraded status
     new 2e7b08b  JAMES-3117 Add PeriodicalHealthChecks/Test
     new 3b61bfa  JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test
     new d73c003  JAMES-3117 Add healthcheck.propeties config file in guice packages
     new b7fb64a  JAMES-3117 Refactor PeriodicalHealthChecks/Test for using configuration
     new 765d20a  JAMES-3117 Guice wiring for PeriodicalHealthChecks
     new d3c90b4  JAMES-3117 Update changelog
     new d8ceefb  JAMES-3117 Remove 'intialDelay', change 'period' to Duration type
     new 9c0e636  JAMES-3117 Use reactor test library for testing with virtual time
     new 3ba85a3  JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test
     new fbd6fc6  JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test
     new 48d1daf  JAMES-3117 Add healthcheck.propeties config file in guice packages
     new 2cd0bdc  JAMES-3117 Remove logging in all healthChecks.
     new f8fe440  JAMES-3117 Add Builder in 'Result' class
     new 215eca8  JAMES-3117 Add logging for callers of HealthChecks
     new f3ec7e0  JAMES-3117 Remove Builder in 'Result' class
     new 75a2b67  JAMES-3117 Add PeriodicalHealthChecks/Test
     new f98c58f  JAMES-3117 Add logging for callers of HealthChecks
     new 985f27b  JAMES-3117 Install PeriodicalHealthChecksModule in CommonServicesModule
     new 5f74bef  JAMES-3117 Reactive HealthCheck
     new a6ada75  JAMES-3137 Create a cache Keyspace
     new 505d353  JAMES-3137 Inject a separate Cassandra session for cache keyspace
     new 9c27d70  JAMES-3137 Split responsibilities between guice & backend
     new 41035e1  JAMES-3137 Move Cassandra cache session initialization to its own module
     new 4ad5014  JAMES-3134 Check RabbitMQ version at start up
     new f16dff5  JAMES-3134 Update RabbitMQ related documentation
     new e530af7  JAMES-3139 RabbitMQEventBus error dispatching tests
     new ee06965  JAMES-3139 RabbitMQEventBus error dispatching handling
     new 5801a79  JAMES-3139 reDeliver() DispatchingFailureGroup should deliver events to all groups
     new 083b54a  JAMES-3139 DispatchingFailureGroup integration test
     new 10cbd3e  JAMES-3139 Speed up integration tests for Distributed James
     new f6308a3  JAMES-3139 Decrease the hard coded retries before opening a RabbitMQ connection
     new 2f7bc3d  JAMES-3143 Fix typo in Cassandra message inconsistency ADR
     new 2feeea7  JAMES-3149 default method for mailbox counters
     new 3943d2f  JAMES-3149 reactify more getMailboxes
     new cb1179e  JAMES-3149 Reactive getMailboxes
     new f9b3122  [REFACTORING] remove duplicate reactor scala extension
     new 125c08e  [REFACTORING] CassandraMessageIdToImapUidDAO never uses cassandraUtils
     new ee0028e  JAMES-2897 Insert statements needs ifNotExist when LWT is used in inserts

The 39 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.md                                       |   3 +-
 .../backends/cassandra/init/KeyspaceFactory.java   |  19 +--
 .../cassandra/init/ResilientClusterProvider.java   |   4 +-
 .../init/SessionWithInitializedTablesFactory.java  |  18 +--
 .../init/configuration/ClusterConfiguration.java   |  67 +--------
 .../init/configuration/InjectionNames.java         |   5 +-
 .../init/configuration/KeyspaceConfiguration.java  |  98 +++++++++++++
 .../cassandra/utils/CassandraAsyncExecutor.java    |   6 +
 .../cassandra/utils/CassandraHealthCheck.java      |  25 ++--
 .../james/backends/cassandra/CassandraCluster.java |  18 ++-
 .../cassandra/CassandraClusterExtension.java       |  13 ++
 .../james/backends/cassandra/DockerCassandra.java  |  63 ++++----
 .../cassandra/DockerCassandraExtension.java        |   6 +
 .../backends/cassandra/DockerCassandraRule.java    |   5 +
 .../init/ResilientClusterProviderTest.java         | 135 +++++-------------
 .../SessionWithInitializedTablesFactoryTest.java   |   5 +-
 .../backends/es/ElasticSearchHealthCheck.java      |   6 +-
 .../backends/rabbitmq/RabbitMQHealthCheck.java     |  25 ++--
 .../backends/rabbitmq/RabbitMQServerVersion.java   |  44 +++---
 .../backends/rabbitmq/SimpleConnectionPool.java    |  15 +-
 .../rabbitmq/RabbitMQServerVersionTest.java        |  96 +++++++++++++
 .../apache/james/core/healthcheck/HealthCheck.java |  12 +-
 .../org/apache/james/core/healthcheck/Result.java  |  31 ++--
 .../destination/conf/healthcheck.properties}       |  25 +---
 .../destination/conf/healthcheck.properties}       |  25 +---
 .../destination/conf/healthcheck.properties}       |  25 +---
 .../destination/conf/healthcheck.properties}       |  25 +---
 .../destination/conf/healthcheck.properties}       |  25 +---
 .../jpa/destination/conf/healthcheck.properties}   |  25 +---
 .../destination/conf/healthcheck.properties}       |  25 +---
 .../org/apache/james/mailbox/MailboxManager.java   |   4 +
 .../events/EventDeadLettersHealthCheck.java        |  28 ++--
 .../mail/CassandraMessageIdToImapUidDAO.java       |  14 +-
 .../cassandra/mail/CassandraMessageMapper.java     |  16 +--
 .../james/mailbox/events/EventDispatcher.java      |  60 ++++++--
 .../james/mailbox/events/RabbitMQEventBus.java     |  18 ++-
 .../james/mailbox/events/RabbitMQEventBusTest.java | 158 +++++++++++++++++++++
 .../james/mailbox/jpa/mail/JPAMessageMapper.java   |   8 --
 .../jpa/mail/TransactionalMessageMapper.java       |   6 -
 .../mailbox/maildir/mail/MaildirMessageMapper.java |   9 --
 .../inmemory/mail/InMemoryMessageMapper.java       |  14 --
 .../james/mailbox/store/StoreMailboxManager.java   |  58 ++++----
 .../james/mailbox/store/mail/MessageMapper.java    |   7 +-
 .../StoreMailboxMessageResultIteratorTest.java     |   6 -
 .../store/mail/model/MessageMapperTest.java        |  18 ---
 .../CassandraRabbitMQAwsS3SmtpTestRuleFactory.java |   8 ++
 .../mpt/smtp/CassandraSmtpTestRuleFactory.java     |   8 ++
 pom.xml                                            |   5 -
 .../mailbox/CassandraCacheSessionModule.java       |  84 +++++++++++
 .../modules/mailbox/CassandraSessionModule.java    |  46 +++++-
 .../modules/mailbox/KeyspacesConfiguration.java    | 155 ++++++++++++++++++++
 .../org/apache/james/server/CassandraProbe.java    |   9 +-
 .../java/org/apache/james/CacheSessionTest.java    | 102 +++++++++++++
 .../java/org/apache/james/DockerCassandraRule.java |  10 +-
 .../org/apache/james/KeyspaceCreationTest.java     | 143 +++++++++++++++++++
 .../mailbox/KeyspacesConfigurationTest.java}       |  43 +++---
 .../apache/james/modules/RabbitMQExtension.java    |  12 ++
 .../apache/james/modules/TestRabbitMQModule.java   |  14 ++
 server/container/guice/guice-common/pom.xml        |  10 ++
 .../org/apache/james/PeriodicalHealthChecks.java   | 115 +++++++++++++++
 .../james/PeriodicalHealthChecksConfiguration.java | 107 ++++++++++++++
 .../apache/james/modules/CommonServicesModule.java |   1 +
 .../modules/PeriodicalHealthChecksModule.java}     |  36 +++--
 .../PeriodicalHealthChecksConfigurationTest.java   | 127 +++++++++++++++++
 .../apache/james/PeriodicalHealthChecksTest.java   | 104 ++++++++++++++
 .../james/jpa/healthcheck/JPAHealthCheck.java      |  10 +-
 .../jmap/draft/methods/GetMailboxesMethod.java     | 109 +++++++-------
 .../jmap/draft/methods/GetMailboxesMethodTest.java |   4 +
 .../rabbitmq/FixingGhostMailboxTest.java           |   5 +-
 .../RabbitMQEventDeadLettersIntegrationTest.java   |  87 ++++++++++--
 .../james/webadmin/routes/HealthCheckRoutes.java   |   8 ++
 src/adr/0022-cassandra-message-inconsistency.md    |   2 +-
 src/site/site.xml                                  |   1 +
 .../config-healthcheck.xml}                        |  25 ++--
 upgrade-instructions.md                            |  10 +-
 75 files changed, 2038 insertions(+), 680 deletions(-)
 copy json/src/test/java/org/apache/james/dto/BaseType.java => backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java (90%)
 create mode 100644 backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/KeyspaceConfiguration.java
 copy mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageIdDto.java => backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersion.java (58%)
 create mode 100644 backends-common/rabbitmq/src/test/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersionTest.java
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/cassandra-ldap/destination/conf/healthcheck.properties} (68%)
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties} (68%)
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties} (68%)
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/cassandra/destination/conf/healthcheck.properties} (68%)
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/jpa-smtp/destination/conf/healthcheck.properties} (68%)
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/jpa/destination/conf/healthcheck.properties} (68%)
 copy dockerfiles/{packaging/guice/cassandra/package/etc/james/templates/webadmin.properties => run/guice/memory/destination/conf/healthcheck.properties} (68%)
 create mode 100644 server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraCacheSessionModule.java
 create mode 100644 server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/KeyspacesConfiguration.java
 create mode 100644 server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
 create mode 100644 server/container/guice/cassandra-guice/src/test/java/org/apache/james/KeyspaceCreationTest.java
 copy server/{mailet/mailets/src/test/java/org/apache/james/transport/matchers/dlp/DlpDomainRulesTest.java => container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/KeyspacesConfigurationTest.java} (60%)
 create mode 100644 server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
 create mode 100644 server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
 copy server/container/guice/{blob-export-guice/src/main/java/org/apache/james/modules/LocalFileBlobExportMechanismModule.java => guice-common/src/main/java/org/apache/james/modules/PeriodicalHealthChecksModule.java} (61%)
 create mode 100644 server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
 create mode 100644 server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
 copy src/site/xdoc/{mailbox/mailbox-cassandra.xml => server/config-healthcheck.xml} (53%)


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


[james-project] 13/39: JAMES-3117 Remove logging in all healthChecks.

Posted by bt...@apache.org.
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 2cd0bdc09a58089b041097c79515f8782629c768
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Fri Mar 27 15:43:19 2020 +0700

    JAMES-3117 Remove logging in all healthChecks.
---
 .../james/backends/cassandra/utils/CassandraHealthCheck.java   |  6 +-----
 .../org/apache/james/backends/es/ElasticSearchHealthCheck.java |  7 +------
 .../apache/james/backends/rabbitmq/RabbitMQHealthCheck.java    | 10 ++--------
 .../james/mailbox/events/EventDeadLettersHealthCheck.java      |  7 +------
 .../main/java/org/apache/james/GuiceLifecycleHealthCheck.java  |  4 ----
 .../api/projections/MessageFastViewProjectionHealthCheck.java  |  4 ----
 .../java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java  | 10 +---------
 7 files changed, 6 insertions(+), 42 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java
index cc78894..d184e30 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java
@@ -24,8 +24,6 @@ import javax.inject.Inject;
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.datastax.driver.core.Session;
 
@@ -35,7 +33,6 @@ import com.datastax.driver.core.Session;
  */
 public class CassandraHealthCheck implements HealthCheck {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(CassandraHealthCheck.class);
     private static final ComponentName COMPONENT_NAME = new ComponentName("Cassandra backend");
     private static final String SAMPLE_QUERY = "SELECT NOW() FROM system.local";
 
@@ -59,8 +56,7 @@ public class CassandraHealthCheck implements HealthCheck {
             session.execute(SAMPLE_QUERY);
             return Result.healthy(COMPONENT_NAME);
         } catch (Exception e) {
-            LOGGER.error("Error checking cassandra backend", e);
-            return Result.unhealthy(COMPONENT_NAME, e.getMessage());
+            return Result.unhealthy(COMPONENT_NAME, "Error checking Cassandra backend", e);
         }
     }
 }
diff --git a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
index 6182f01..89037e7 100644
--- a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
+++ b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
@@ -32,14 +32,11 @@ import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.Requests;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 
 
 public class ElasticSearchHealthCheck implements HealthCheck {
-    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchHealthCheck.class);
     private static final ComponentName COMPONENT_NAME = new ComponentName("ElasticSearch Backend");
 
     private final Set<IndexName> indexNames;
@@ -69,8 +66,7 @@ public class ElasticSearchHealthCheck implements HealthCheck {
 
             return toHealthCheckResult(response);
         } catch (IOException e) {
-            LOGGER.error("Error while contacting cluster", e);
-            return Result.unhealthy(COMPONENT_NAME, "Error while contacting cluster. Check James server logs.");
+            return Result.unhealthy(COMPONENT_NAME, "Error while contacting cluster", e);
         }
     }
 
@@ -81,7 +77,6 @@ public class ElasticSearchHealthCheck implements HealthCheck {
             case YELLOW:
                 return Result.healthy(COMPONENT_NAME);
             case RED:
-                LOGGER.error("ElasticSearchCluster return RED status");
                 return Result.unhealthy(COMPONENT_NAME, response.getClusterName() + " status is RED");
             default:
                 throw new NotImplementedException("Un-handled ElasticSearch cluster status");
diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
index c50f99e..09a03df 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
@@ -24,11 +24,8 @@ import javax.inject.Inject;
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class RabbitMQHealthCheck implements HealthCheck {
-    private static final Logger LOGGER = LoggerFactory.getLogger(RabbitMQHealthCheck.class);
     private static final ComponentName COMPONENT_NAME = new ComponentName("RabbitMQ backend");
 
     private final SimpleConnectionPool connectionPool;
@@ -51,13 +48,10 @@ public class RabbitMQHealthCheck implements HealthCheck {
             if (connectionPool.tryConnection() && rabbitChannelPoolImpl.tryChannel()) {
                 return Result.healthy(COMPONENT_NAME);
             } else {
-                String message = "The created connection was not opened";
-                LOGGER.error("Unhealthy RabbitMQ instances: {}", message);
-                return Result.unhealthy(COMPONENT_NAME, message);
+                return Result.unhealthy(COMPONENT_NAME, "The created connection was not opened");
             }
         } catch (Exception e) {
-            LOGGER.error("Unhealthy RabbitMQ instances: could not establish a connection", e);
-            return Result.unhealthy(COMPONENT_NAME, e.getMessage());
+            return Result.unhealthy(COMPONENT_NAME, "Unhealthy RabbitMQ instances: could not establish a connection", e);
         }
     }
 }
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java b/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
index 50a0a8b..8fc1b1b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
@@ -24,11 +24,8 @@ import javax.inject.Inject;
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class EventDeadLettersHealthCheck implements HealthCheck {
-    private static final Logger LOGGER = LoggerFactory.getLogger(EventDeadLettersHealthCheck.class);
     private static final ComponentName COMPONENT_NAME = new ComponentName("EventDeadLettersHealthCheck");
 
     private final EventDeadLetters eventDeadLetters;
@@ -49,14 +46,12 @@ public class EventDeadLettersHealthCheck implements HealthCheck {
             boolean containEvents = eventDeadLetters.containEvents().block();
 
             if (containEvents) {
-                LOGGER.warn("EventDeadLetters is not empty");
                 return Result.degraded(COMPONENT_NAME, "EventDeadLetters contain events. This might indicate transient failure on mailbox event processing.");
             }
 
             return Result.healthy(COMPONENT_NAME);
         } catch (Exception e) {
-            LOGGER.error("EventDeadLettersHealthCheck threw an exception", e);
-            return Result.unhealthy(COMPONENT_NAME, e.getMessage());
+            return Result.unhealthy(COMPONENT_NAME, "Error checking EventDeadLettersHealthCheck", e);
         }
     }
 }
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java b/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java
index 2ee8e9e..b7a6fe4 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java
@@ -24,11 +24,8 @@ import javax.inject.Inject;
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class GuiceLifecycleHealthCheck implements HealthCheck {
-    private static final Logger LOGGER = LoggerFactory.getLogger(GuiceLifecycleHealthCheck.class);
     private final IsStartedProbe probe;
 
     @Inject
@@ -46,7 +43,6 @@ public class GuiceLifecycleHealthCheck implements HealthCheck {
         if (probe.isStarted()) {
             return Result.healthy(componentName());
         } else {
-            LOGGER.error("James server is not started");
             return Result.unhealthy(componentName(), "James server is not started.");
         }
     }
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java
index b2877ad..de18851 100644
--- a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java
@@ -29,12 +29,9 @@ import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class MessageFastViewProjectionHealthCheck implements HealthCheck {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(MessageFastViewProjectionHealthCheck.class);
     private static final ComponentName COMPONENT_NAME = new ComponentName("MessageFastViewProjection");
     private static final double MAXIMUM_MISS_PERCENTAGE_ACCEPTED = 10;
 
@@ -67,7 +64,6 @@ public class MessageFastViewProjectionHealthCheck implements HealthCheck {
         long totalCount = hitCount + missCount;
         double missCountPercentage = missCount * 100.0d / totalCount;
         if (missCountPercentage > MAXIMUM_MISS_PERCENTAGE_ACCEPTED) {
-            LOGGER.warn("MessageFastViewProjection missCountPercentage exceeded the threshold");
             return Result.degraded(COMPONENT_NAME,
                 String.format("retrieveMissCount percentage %s%% (%d/%d) is higher than the threshold %s%%",
                     missCountPercentage, missCount, totalCount, MAXIMUM_MISS_PERCENTAGE_ACCEPTED));
diff --git a/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java b/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java
index 81bc728..8cd1706 100644
--- a/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java
+++ b/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java
@@ -27,13 +27,9 @@ import javax.persistence.EntityManagerFactory;
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 
 public class JPAHealthCheck implements HealthCheck {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(JPAHealthCheck.class);
     private final EntityManagerFactory entityManagerFactory;
 
     @Inject
@@ -48,18 +44,14 @@ public class JPAHealthCheck implements HealthCheck {
 
     @Override
     public Result check() {
-        LOGGER.debug("Checking if EntityManager is created successfully");
         try {
             if (entityManagerFactory.createEntityManager().isOpen()) {
-                LOGGER.debug("EntityManager can execute queries, the connection is healthy");
                 return healthy(componentName());
             }
         } catch (IllegalStateException stateException) {
-            LOGGER.debug("EntityManagerFactory or EntityManager threw an IllegalStateException, the connection is unhealthy");
-            return unhealthy(componentName(), stateException.getMessage());
+            return unhealthy(componentName(), "EntityManagerFactory or EntityManager thrown an IllegalStateException, the connection is unhealthy", stateException);
         }
 
-        LOGGER.error("EntityManager is not open, the connection is unhealthy");
         return unhealthy(componentName(), "entityManager is not open");
     }
 }


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


[james-project] 31/39: JAMES-3139 Speed up integration tests for Distributed James

Posted by bt...@apache.org.
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 10cbd3ea1e12b81789f9f1bdf6066b5c5ded4fea
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Apr 16 09:51:58 2020 +0700

    JAMES-3139 Speed up integration tests for Distributed James
    
    Decrease wait delays, timeouts, and retry count to fasten our integration
    tests
    
    This is a generalisation of a cryptic optimization performed for
    RabbitMQEventDeadLettersIntegrationTest
---
 .../java/org/apache/james/modules/TestRabbitMQModule.java  | 14 ++++++++++++++
 .../rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java  | 12 +-----------
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/TestRabbitMQModule.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/TestRabbitMQModule.java
index 35438ac..47012aa 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/TestRabbitMQModule.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/TestRabbitMQModule.java
@@ -39,6 +39,13 @@ import com.google.inject.Provides;
 import com.google.inject.multibindings.Multibinder;
 
 public class TestRabbitMQModule extends AbstractModule {
+    private static final int MAX_THREE_RETRIES = 3;
+    private static final int MIN_DELAY_OF_ONE_HUNDRED_MILLISECONDS = 100;
+    private static final int CONNECTION_TIMEOUT_OF_ONE_SECOND = 1000;
+    private static final int CHANNEL_RPC_TIMEOUT_OF_ONE_SECOND = 1000;
+    private static final int HANDSHAKE_TIMEOUT_OF_ONE_SECOND = 1000;
+    private static final int SHUTDOWN_TIMEOUT_OF_ONE_SECOND = 1000;
+    private static final int NETWORK_RECOVERY_INTERVAL_OF_ONE_SECOND = 1000;
 
     private final DockerRabbitMQ rabbitMQ;
 
@@ -67,6 +74,13 @@ public class TestRabbitMQModule extends AbstractModule {
             .amqpUri(rabbitMQ.amqpUri())
             .managementUri(rabbitMQ.managementUri())
             .managementCredentials(DEFAULT_MANAGEMENT_CREDENTIAL)
+            .maxRetries(MAX_THREE_RETRIES)
+            .minDelayInMs(MIN_DELAY_OF_ONE_HUNDRED_MILLISECONDS)
+            .connectionTimeoutInMs(CONNECTION_TIMEOUT_OF_ONE_SECOND)
+            .channelRpcTimeoutInMs(CHANNEL_RPC_TIMEOUT_OF_ONE_SECOND)
+            .handshakeTimeoutInMs(HANDSHAKE_TIMEOUT_OF_ONE_SECOND)
+            .shutdownTimeoutInMs(SHUTDOWN_TIMEOUT_OF_ONE_SECOND)
+            .networkRecoveryIntervalInMs(NETWORK_RECOVERY_INTERVAL_OF_ONE_SECOND)
             .build();
     }
 
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
index 58f4772..b8cbbd0 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
@@ -29,7 +29,6 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.hasSize;
 
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -45,7 +44,6 @@ import org.apache.james.GuiceModuleTestExtension;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
 import org.apache.james.backends.rabbitmq.DockerRabbitMQ;
-import org.apache.james.backends.rabbitmq.RabbitMQConnectionFactory;
 import org.apache.james.core.Username;
 import org.apache.james.junit.categories.BasicFeature;
 import org.apache.james.mailbox.DefaultMailboxes;
@@ -203,15 +201,7 @@ class RabbitMQEventDeadLettersIntegrationTest {
         .extension(new RetryEventsListenerExtension())
         .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
             .combineWith(CassandraRabbitMQJamesServerMain.MODULES)
-            .overrideWith(new WebadminIntegrationTestModule())
-            .overrideWith(binder -> {
-                try {
-                    binder.bind(RabbitMQConnectionFactory.class)
-                        .toInstance(RABBIT_MQ_EXTENSION.dockerRabbitMQ().createRabbitConnectionFactory());
-                } catch (URISyntaxException e) {
-                    throw new RuntimeException(e);
-                }
-            }))
+            .overrideWith(new WebadminIntegrationTestModule()))
         .build();
 
     //This value is duplicated from default configuration to ensure we keep the same behavior over time


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


[james-project] 32/39: JAMES-3139 Decrease the hard coded retries before opening a RabbitMQ connection

Posted by bt...@apache.org.
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 f6308a32a2573a4fb0cc40e7b1d7ec478f027bd6
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Apr 16 10:01:00 2020 +0700

    JAMES-3139 Decrease the hard coded retries before opening a RabbitMQ connection
    
    100 backed of retries is too long, we should fail faster, as failure is
    handled upstream.
---
 .../java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java
index 6aa1ba5..f4fa4e1 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java
@@ -54,7 +54,7 @@ public class SimpleConnectionPool implements AutoCloseable {
     }
 
     public Mono<Connection> getResilientConnection() {
-        int numRetries = 100;
+        int numRetries = 10;
         Duration initialDelay = Duration.ofMillis(100);
         Duration forever = Duration.ofMillis(Long.MAX_VALUE);
         return Mono.defer(this::getOpenConnection)


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


[james-project] 12/39: JAMES-3117 Add healthcheck.propeties config file in guice packages

Posted by bt...@apache.org.
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 48d1dafc8964c4ac85de6dfaa9325c24bd06cfbd
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Thu Mar 26 20:49:49 2020 +0700

    JAMES-3117 Add healthcheck.propeties config file in guice packages
---
 .../guice/cassandra-ldap/destination/conf/healthcheck.properties   | 7 +++++--
 .../destination/conf/healthcheck.properties                        | 7 +++++--
 .../cassandra-rabbitmq/destination/conf/healthcheck.properties     | 7 +++++--
 .../run/guice/cassandra/destination/conf/healthcheck.properties    | 7 +++++--
 .../run/guice/jpa-smtp/destination/conf/healthcheck.properties     | 7 +++++--
 dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties  | 7 +++++--
 .../run/guice/memory/destination/conf/healthcheck.properties       | 7 +++++--
 7 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
index 235a828..9f3bad1 100644
--- a/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
index 24873dc..9f3bad1 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
-# healthcheck.period=PT60s
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
index 24873dc..9f3bad1 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
-# healthcheck.period=PT60s
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
index 24873dc..9f3bad1 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
-# healthcheck.period=PT60s
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 
diff --git a/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties b/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
index 24873dc..9f3bad1 100644
--- a/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
-# healthcheck.period=PT60s
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 
diff --git a/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties b/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
index 24873dc..9f3bad1 100644
--- a/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
-# healthcheck.period=PT60s
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 
diff --git a/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties b/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
index 24873dc..9f3bad1 100644
--- a/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
@@ -22,6 +22,9 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
-# healthcheck.period=PT60s
+# Optional. Period between two PeriodicalHealthChecks.
+# Units supported are (ms - millisecond, s - second, m - minute, h - hour, d - day). Default unit is millisecond.
+# Default duration is 60 seconds.
+# Duration must be greater or at least equals to 10 seconds.
+# healthcheck.period=60s
 


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


[james-project] 22/39: JAMES-3137 Inject a separate Cassandra session for cache keyspace

Posted by bt...@apache.org.
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 505d35385bd8f21bcce2eb1bb6f0e216930b47e7
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 27 11:12:51 2020 +0700

    JAMES-3137 Inject a separate Cassandra session for cache keyspace
---
 .../init/SessionWithInitializedTablesFactory.java  |  36 +++++--
 .../init/configuration/InjectionNames.java         |  24 +++++
 .../james/backends/cassandra/CassandraCluster.java |   2 +-
 .../SessionWithInitializedTablesFactoryTest.java   |   3 +-
 .../modules/mailbox/CassandraSessionModule.java    |  26 +++++-
 .../java/org/apache/james/CacheSessionTest.java    | 103 +++++++++++++++++++++
 6 files changed, 185 insertions(+), 9 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
index 89bcf0d..b3d18f4 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
@@ -23,6 +23,7 @@ import java.util.stream.Stream;
 
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
@@ -30,6 +31,7 @@ import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.components.CassandraTable;
 import org.apache.james.backends.cassandra.components.CassandraType;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManager;
 
@@ -39,18 +41,25 @@ import com.datastax.driver.core.Session;
 @Singleton
 public class SessionWithInitializedTablesFactory implements Provider<Session> {
     private final CassandraModule module;
+    private final CassandraModule cacheModule;
     private final Session session;
+    private final Session cacheSession;
 
     @Inject
-    public SessionWithInitializedTablesFactory(ClusterConfiguration clusterConfiguration, Cluster cluster, CassandraModule module) {
+    public SessionWithInitializedTablesFactory(ClusterConfiguration clusterConfiguration,
+                                               Cluster cluster,
+                                               CassandraModule module,
+                                               @Named(InjectionNames.CACHE) CassandraModule cacheModule) {
         this.module = module;
+        this.cacheModule = cacheModule;
         this.session = createSession(cluster, clusterConfiguration.getKeyspace());
+        this.cacheSession = createCacheSession(cluster, clusterConfiguration.getKeyspace());
     }
 
     private Session createSession(Cluster cluster, String keyspace) {
         Session session = cluster.connect(keyspace);
         try {
-            if (allOperationsAreFullyPerformed(session)) {
+            if (allOperationsAreFullyPerformed(session, module)) {
                 new CassandraSchemaVersionDAO(session)
                     .updateVersion(CassandraSchemaVersionManager.MAX_VERSION)
                     .block();
@@ -62,17 +71,28 @@ public class SessionWithInitializedTablesFactory implements Provider<Session> {
         }
     }
 
-    private boolean allOperationsAreFullyPerformed(Session session) {
-        Stream<Boolean> operations = Stream.of(createTypes(session), createTables(session));
+    private Session createCacheSession(Cluster cluster, String keyspace) {
+        Session session = cluster.connect(keyspace);
+        try {
+            allOperationsAreFullyPerformed(session, cacheModule);
+            return session;
+        } catch (Exception e) {
+            session.close();
+            throw e;
+        }
+    }
+
+    private boolean allOperationsAreFullyPerformed(Session session, CassandraModule module) {
+        Stream<Boolean> operations = Stream.of(createTypes(session, module), createTables(session, module));
         return operations.allMatch(updated -> updated);
     }
 
-    private boolean createTypes(Session session) {
+    private boolean createTypes(Session session, CassandraModule module) {
         return new CassandraTypesCreator(module, session)
                 .initializeTypes() == CassandraType.InitializationStatus.FULL;
     }
 
-    private boolean createTables(Session session) {
+    private boolean createTables(Session session, CassandraModule module) {
         return new CassandraTableManager(module, session)
             .initializeTables() == CassandraTable.InitializationStatus.FULL;
     }
@@ -82,6 +102,10 @@ public class SessionWithInitializedTablesFactory implements Provider<Session> {
         return session;
     }
 
+    public Session getCacheSession() {
+        return cacheSession;
+    }
+
     @PreDestroy
     public synchronized void destroy() {
         session.close();
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java
new file mode 100644
index 0000000..482b707
--- /dev/null
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java
@@ -0,0 +1,24 @@
+/****************************************************************
+ * 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.backends.cassandra.init.configuration;
+
+public interface InjectionNames {
+    String CACHE = "cache";
+}
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
index 91c61ad..3be615e 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
@@ -61,7 +61,7 @@ public final class CassandraCluster implements AutoCloseable {
             .build();
         this.nonPrivilegedCluster = ClusterFactory.create(configuration);
         this.nonPrivilegedSession = new TestingSession(new SessionWithInitializedTablesFactory(configuration,
-            nonPrivilegedCluster, module).get());
+            nonPrivilegedCluster, module, CassandraModule.EMPTY_MODULE).get());
         this.typesProvider = new CassandraTypesProvider(module, nonPrivilegedSession);
     }
 
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
index 817fe85..c971fea 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
@@ -126,7 +126,8 @@ class SessionWithInitializedTablesFactoryTest {
         return () -> new SessionWithInitializedTablesFactory(
                 clusterConfiguration,
                 cluster,
-                MODULE)
+                MODULE,
+                CassandraModule.EMPTY_MODULE)
             .get();
     }
 
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
index 7f097b3..dcf3842 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
@@ -29,6 +29,7 @@ import org.apache.james.backends.cassandra.init.ResilientClusterProvider;
 import org.apache.james.backends.cassandra.init.SessionWithInitializedTablesFactory;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
 import org.apache.james.backends.cassandra.utils.CassandraHealthCheck;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
@@ -52,6 +53,8 @@ import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
 
 public class CassandraSessionModule extends AbstractModule {
 
@@ -65,13 +68,14 @@ public class CassandraSessionModule extends AbstractModule {
     @Override
     protected void configure() {
         bind(CassandraUtils.class).in(Scopes.SINGLETON);
-        bind(Session.class).toProvider(SessionWithInitializedTablesFactory.class);
         bind(Cluster.class).toProvider(ResilientClusterProvider.class);
 
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().toInstance(CassandraZonedDateTimeModule.MODULE);
         cassandraDataDefinitions.addBinding().toInstance(CassandraSchemaVersionModule.MODULE);
 
+        Multibinder.newSetBinder(binder(), CassandraModule.class, Names.named(InjectionNames.CACHE));
+
         bind(CassandraSchemaVersionManager.class).in(Scopes.SINGLETON);
         bind(CassandraSchemaVersionDAO.class).in(Scopes.SINGLETON);
 
@@ -85,10 +89,30 @@ public class CassandraSessionModule extends AbstractModule {
 
     @Provides
     @Singleton
+    Session provideSession(SessionWithInitializedTablesFactory sessionFactory) {
+        return sessionFactory.get();
+    }
+
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    Session provideCacheSession(SessionWithInitializedTablesFactory sessionFactory) {
+        return sessionFactory.getCacheSession();
+    }
+
+    @Provides
+    @Singleton
     CassandraModule composeDataDefinitions(Set<CassandraModule> modules) {
         return CassandraModule.aggregateModules(modules);
     }
 
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    CassandraModule composeCacheDefinitions(@Named(InjectionNames.CACHE) Set<CassandraModule> modules) {
+        return CassandraModule.aggregateModules(modules);
+    }
+
     @Provides
     @Singleton
     BatchSizes getBatchSizesConfiguration(PropertiesProvider propertiesProvider) {
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
new file mode 100644
index 0000000..ca6f9a7
--- /dev/null
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
@@ -0,0 +1,103 @@
+/****************************************************************
+ * 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;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static org.apache.james.CassandraJamesServerMain.ALL_BUT_JMX_CASSANDRA_MODULE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
+import org.apache.james.lifecycle.api.StartUpCheck;
+import org.apache.james.modules.ConfigurationProbe;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.core.Session;
+import com.google.inject.Inject;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+class CacheSessionTest {
+    private static final String TABLE_NAME = "tablename";
+
+    static class CacheSessionTestCheck implements StartUpCheck {
+        static final String NAME = "CacheSessionTest-check";
+        private final Session cacheSession;
+
+        @Inject
+        CacheSessionTestCheck(@Named(InjectionNames.CACHE) Session cacheSession) {
+            this.cacheSession = cacheSession;
+        }
+
+        @Override
+        public CheckResult check() {
+            try {
+                cacheSession.execute(select().from(TABLE_NAME));
+                return CheckResult.builder()
+                    .checkName(NAME)
+                    .resultType(ResultType.GOOD)
+                    .build();
+            } catch (Exception e) {
+                return CheckResult.builder()
+                    .checkName(NAME)
+                    .resultType(ResultType.BAD)
+                    .description(String.format("%s do not exist", TABLE_NAME))
+                    .build();
+            }
+        }
+
+        @Override
+        public String checkName() {
+            return NAME;
+        }
+    }
+
+    @RegisterExtension
+    static JamesServerExtension testExtension = new JamesServerBuilder()
+        .extension(new DockerElasticSearchExtension())
+        .extension(new CassandraExtension())
+        .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+            .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+            .overrideWith(TestJMAPServerModule.limitToTenMessages()))
+        .overrideServerModule(binder -> Multibinder.newSetBinder(binder, CassandraModule.class, Names.named(InjectionNames.CACHE))
+            .addBinding()
+            .toInstance(CassandraModule.table(TABLE_NAME)
+                .comment("Testing table")
+                .statement(statement -> statement
+                    .addPartitionKey("id", DataType.timeuuid())
+                    .addClusteringColumn("clustering", DataType.bigint()))
+                .build()))
+        .overrideServerModule(binder -> Multibinder.newSetBinder(binder, StartUpCheck.class)
+            .addBinding()
+            .to(CacheSessionTestCheck.class))
+        .disableAutoStart()
+        .build();
+
+    @Test
+    void cacheTableShouldBeWellCreated(GuiceJamesServer jamesServer) {
+        assertThatCode(jamesServer::start)
+            .doesNotThrowAnyException();
+    }
+}


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


[james-project] 24/39: JAMES-3137 Move Cassandra cache session initialization to its own module

Posted by bt...@apache.org.
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 41035e157dae8d54213e8cd648f4593ca4d9f0ac
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Apr 9 09:55:35 2020 +0700

    JAMES-3137 Move Cassandra cache session initialization to its own module
---
 .../CassandraRabbitMQAwsS3SmtpTestRuleFactory.java |  8 +++
 .../mpt/smtp/CassandraSmtpTestRuleFactory.java     |  8 +++
 .../mailbox/CassandraCacheSessionModule.java       | 84 ++++++++++++++++++++++
 .../modules/mailbox/CassandraSessionModule.java    | 51 +++----------
 .../java/org/apache/james/CacheSessionTest.java    |  5 +-
 5 files changed, 110 insertions(+), 46 deletions(-)

diff --git a/mpt/impl/smtp/cassandra-rabbitmq-object-storage/src/test/java/org/apache/james/mpt/smtp/CassandraRabbitMQAwsS3SmtpTestRuleFactory.java b/mpt/impl/smtp/cassandra-rabbitmq-object-storage/src/test/java/org/apache/james/mpt/smtp/CassandraRabbitMQAwsS3SmtpTestRuleFactory.java
index fe02a02..01c82b4 100644
--- a/mpt/impl/smtp/cassandra-rabbitmq-object-storage/src/test/java/org/apache/james/mpt/smtp/CassandraRabbitMQAwsS3SmtpTestRuleFactory.java
+++ b/mpt/impl/smtp/cassandra-rabbitmq-object-storage/src/test/java/org/apache/james/mpt/smtp/CassandraRabbitMQAwsS3SmtpTestRuleFactory.java
@@ -29,6 +29,7 @@ import org.apache.james.dnsservice.api.DNSService;
 import org.apache.james.modules.TestAwsS3BlobStoreModule;
 import org.apache.james.modules.TestRabbitMQModule;
 import org.apache.james.modules.blobstore.BlobStoreChoosingModule;
+import org.apache.james.modules.mailbox.KeyspacesConfiguration;
 import org.apache.james.modules.objectstorage.aws.s3.DockerAwsS3TestRule;
 import org.apache.james.modules.protocols.SmtpGuiceProbe.SmtpServerConnectedType;
 import org.apache.james.modules.rabbitmq.RabbitMQModule;
@@ -64,6 +65,13 @@ public final class CassandraRabbitMQAwsS3SmtpTestRuleFactory {
             .overrideWith(
                 new TestRabbitMQModule(DockerRabbitMQSingleton.SINGLETON),
                 new TestAwsS3BlobStoreModule(awsS3TestRule),
+                binder -> binder.bind(KeyspacesConfiguration.class)
+                    .toInstance(KeyspacesConfiguration.builder()
+                        .keyspace(DockerCassandra.KEYSPACE)
+                        .cacheKeyspace(DockerCassandra.CACHE_KEYSPACE)
+                        .replicationFactor(1)
+                        .disableDurableWrites()
+                        .build()),
                 binder -> binder.bind(ClusterConfiguration.class).toInstance(
                     DockerCassandra.configurationBuilder(cassandraHost)
                         .build()),
diff --git a/mpt/impl/smtp/cassandra/src/test/java/org/apache/james/mpt/smtp/CassandraSmtpTestRuleFactory.java b/mpt/impl/smtp/cassandra/src/test/java/org/apache/james/mpt/smtp/CassandraSmtpTestRuleFactory.java
index e8d85c8..98cb9c4 100644
--- a/mpt/impl/smtp/cassandra/src/test/java/org/apache/james/mpt/smtp/CassandraSmtpTestRuleFactory.java
+++ b/mpt/impl/smtp/cassandra/src/test/java/org/apache/james/mpt/smtp/CassandraSmtpTestRuleFactory.java
@@ -24,6 +24,7 @@ import org.apache.james.GuiceJamesServer;
 import org.apache.james.backends.cassandra.DockerCassandra;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
 import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.modules.mailbox.KeyspacesConfiguration;
 import org.apache.james.modules.protocols.SmtpGuiceProbe.SmtpServerConnectedType;
 import org.apache.james.modules.server.CamelMailetContainerModule;
 import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
@@ -55,6 +56,13 @@ public final class CassandraSmtpTestRuleFactory {
                 binder -> binder.bind(ClusterConfiguration.class).toInstance(
                     DockerCassandra.configurationBuilder(cassandraHost)
                         .build()),
+                binder -> binder.bind(KeyspacesConfiguration.class)
+                    .toInstance(KeyspacesConfiguration.builder()
+                        .keyspace(DockerCassandra.KEYSPACE)
+                        .cacheKeyspace(DockerCassandra.CACHE_KEYSPACE)
+                        .replicationFactor(1)
+                        .disableDurableWrites()
+                        .build()),
                 binder -> binder.bind(DNSService.class).toInstance(dnsService));
     }
 }
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraCacheSessionModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraCacheSessionModule.java
new file mode 100644
index 0000000..71796b1
--- /dev/null
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraCacheSessionModule.java
@@ -0,0 +1,84 @@
+/****************************************************************
+ * 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.mailbox;
+
+import java.util.Set;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.init.KeyspaceFactory;
+import org.apache.james.backends.cassandra.init.SessionWithInitializedTablesFactory;
+import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+public class CassandraCacheSessionModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind(InitializedCacheCluster.class).in(Scopes.SINGLETON);
+        Multibinder.newSetBinder(binder(), CassandraModule.class, Names.named(InjectionNames.CACHE));
+    }
+
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    KeyspaceConfiguration provideCacheKeyspaceConfiguration(KeyspacesConfiguration keyspacesConfiguration) {
+        return keyspacesConfiguration.cacheKeyspaceConfiguration();
+    }
+
+    @Singleton
+    @Named(InjectionNames.CACHE)
+    @Provides
+    Session provideSession(@Named(InjectionNames.CACHE) KeyspaceConfiguration keyspaceConfiguration,
+                           InitializedCacheCluster cluster,
+                           @Named(InjectionNames.CACHE) CassandraModule module) {
+        return new SessionWithInitializedTablesFactory(keyspaceConfiguration, cluster.cluster, module).get();
+    }
+
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    CassandraModule composeCacheDefinitions(@Named(InjectionNames.CACHE) Set<CassandraModule> modules) {
+        return CassandraModule.aggregateModules(modules);
+    }
+
+    static class InitializedCacheCluster {
+        private final Cluster cluster;
+
+        @Inject
+        private InitializedCacheCluster(Cluster cluster, ClusterConfiguration clusterConfiguration, KeyspacesConfiguration keyspacesConfiguration) {
+            this.cluster = cluster;
+
+            if (clusterConfiguration.shouldCreateKeyspace()) {
+                KeyspaceFactory.createKeyspace(keyspacesConfiguration.cacheKeyspaceConfiguration(), cluster);
+            }
+        }
+    }
+}
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
index e68713e..d8004f2 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
@@ -30,7 +30,6 @@ import org.apache.james.backends.cassandra.init.ResilientClusterProvider;
 import org.apache.james.backends.cassandra.init.SessionWithInitializedTablesFactory;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
-import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
 import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.backends.cassandra.utils.CassandraHealthCheck;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
@@ -39,7 +38,6 @@ import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManage
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.lifecycle.api.StartUpCheck;
-import org.apache.james.lifecycle.api.Startable;
 import org.apache.james.mailbox.store.BatchSizes;
 import org.apache.james.server.CassandraProbe;
 import org.apache.james.util.Host;
@@ -57,8 +55,6 @@ import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
 
 public class CassandraSessionModule extends AbstractModule {
 
@@ -75,19 +71,13 @@ public class CassandraSessionModule extends AbstractModule {
         bind(Cluster.class).toProvider(ResilientClusterProvider.class);
 
         bind(InitializedCluster.class).in(Scopes.SINGLETON);
-        bind(MainSessionWithInitializedTablesFactory.class).in(Scopes.SINGLETON);
-        bind(CacheSessionWithInitializedTablesFactory.class).in(Scopes.SINGLETON);
 
-        bind(Session.class).toProvider(MainSessionWithInitializedTablesFactory.class);
-        bind(Session.class).annotatedWith(Names.named(InjectionNames.CACHE))
-            .toProvider(CacheSessionWithInitializedTablesFactory.class);
+        bind(Session.class).toProvider(SessionWithInitializedTablesFactory.class);
 
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().toInstance(CassandraZonedDateTimeModule.MODULE);
         cassandraDataDefinitions.addBinding().toInstance(CassandraSchemaVersionModule.MODULE);
 
-        Multibinder.newSetBinder(binder(), CassandraModule.class, Names.named(InjectionNames.CACHE));
-
         bind(CassandraSchemaVersionManager.class).in(Scopes.SINGLETON);
         bind(CassandraSchemaVersionDAO.class).in(Scopes.SINGLETON);
 
@@ -99,16 +89,17 @@ public class CassandraSessionModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), HealthCheck.class).addBinding().to(CassandraHealthCheck.class);
     }
 
-    @Provides
     @Singleton
-    CassandraModule composeDataDefinitions(Set<CassandraModule> modules) {
-        return CassandraModule.aggregateModules(modules);
+    @Provides
+    SessionWithInitializedTablesFactory provideSessionFactory(KeyspaceConfiguration keyspaceConfiguration,
+                                               InitializedCluster cluster,
+                                               CassandraModule module) {
+        return new SessionWithInitializedTablesFactory(keyspaceConfiguration, cluster.cluster, module);
     }
 
-    @Named(InjectionNames.CACHE)
     @Provides
     @Singleton
-    CassandraModule composeCacheDefinitions(@Named(InjectionNames.CACHE) Set<CassandraModule> modules) {
+    CassandraModule composeDataDefinitions(Set<CassandraModule> modules) {
         return CassandraModule.aggregateModules(modules);
     }
 
@@ -174,32 +165,7 @@ public class CassandraSessionModule extends AbstractModule {
         return keyspacesConfiguration.mainKeyspaceConfiguration();
     }
 
-    @Named(InjectionNames.CACHE)
-    @Provides
-    @Singleton
-    KeyspaceConfiguration provideCacheKeyspaceConfiguration(KeyspacesConfiguration keyspacesConfiguration) {
-        return keyspacesConfiguration.cacheKeyspaceConfiguration();
-    }
-
-    private static class MainSessionWithInitializedTablesFactory extends SessionWithInitializedTablesFactory {
-        @Inject
-        public MainSessionWithInitializedTablesFactory(KeyspaceConfiguration keyspaceConfiguration,
-                                                       InitializedCluster cluster,
-                                                       CassandraModule module) {
-            super(keyspaceConfiguration, cluster.cluster, module);
-        }
-    }
-
-    private static class CacheSessionWithInitializedTablesFactory extends SessionWithInitializedTablesFactory {
-        @Inject
-        public CacheSessionWithInitializedTablesFactory(@Named(InjectionNames.CACHE) KeyspaceConfiguration keyspaceConfiguration,
-                                                        InitializedCluster cluster,
-                                                        @Named(InjectionNames.CACHE) CassandraModule module) {
-            super(keyspaceConfiguration, cluster.cluster, module);
-        }
-    }
-
-    private static class InitializedCluster implements Startable {
+    static class InitializedCluster {
         private final Cluster cluster;
 
         @Inject
@@ -208,7 +174,6 @@ public class CassandraSessionModule extends AbstractModule {
 
             if (clusterConfiguration.shouldCreateKeyspace()) {
                 KeyspaceFactory.createKeyspace(keyspacesConfiguration.mainKeyspaceConfiguration(), cluster);
-                KeyspaceFactory.createKeyspace(keyspacesConfiguration.cacheKeyspaceConfiguration(), cluster);
             }
         }
     }
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
index ca6f9a7..d689285 100644
--- a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
@@ -21,14 +21,13 @@ package org.apache.james;
 
 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
 import static org.apache.james.CassandraJamesServerMain.ALL_BUT_JMX_CASSANDRA_MODULE;
-import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
 import org.apache.james.lifecycle.api.StartUpCheck;
-import org.apache.james.modules.ConfigurationProbe;
 import org.apache.james.modules.TestJMAPServerModule;
+import org.apache.james.modules.mailbox.CassandraCacheSessionModule;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
@@ -79,7 +78,7 @@ class CacheSessionTest {
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
         .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
-            .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+            .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE, new CassandraCacheSessionModule())
             .overrideWith(TestJMAPServerModule.limitToTenMessages()))
         .overrideServerModule(binder -> Multibinder.newSetBinder(binder, CassandraModule.class, Names.named(InjectionNames.CACHE))
             .addBinding()


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


[james-project] 30/39: JAMES-3139 DispatchingFailureGroup integration test

Posted by bt...@apache.org.
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 083b54a92d60817be349d31485176236494b4eba
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Apr 9 17:03:09 2020 +0700

    JAMES-3139 DispatchingFailureGroup integration test
---
 .../apache/james/modules/RabbitMQExtension.java    | 12 +++
 .../RabbitMQEventDeadLettersIntegrationTest.java   | 99 ++++++++++++++++++++--
 2 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/RabbitMQExtension.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/RabbitMQExtension.java
index 2677b5a..ac1c489 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/RabbitMQExtension.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/RabbitMQExtension.java
@@ -22,6 +22,8 @@ package org.apache.james.modules;
 import org.apache.james.GuiceModuleTestExtension;
 import org.apache.james.backends.rabbitmq.DockerRabbitMQ;
 import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
 
 import com.google.inject.Module;
 
@@ -47,4 +49,14 @@ public class RabbitMQExtension implements GuiceModuleTestExtension {
     public DockerRabbitMQ dockerRabbitMQ() {
         return rabbitMQRule.dockerRabbitMQ();
     }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return parameterContext.getParameter().getType() == DockerRabbitMQ.class;
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return dockerRabbitMQ();
+    }
 }
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
index 36dd401..58f4772 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
@@ -22,13 +22,14 @@ package org.apache.james.webadmin.integration.rabbitmq;
 import static io.restassured.RestAssured.given;
 import static io.restassured.RestAssured.when;
 import static io.restassured.RestAssured.with;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.awaitility.Duration.ONE_HUNDRED_MILLISECONDS;
 import static org.awaitility.Duration.ONE_MINUTE;
-import static org.awaitility.Duration.TEN_SECONDS;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.hasSize;
 
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -43,10 +44,13 @@ import org.apache.james.GuiceJamesServer;
 import org.apache.james.GuiceModuleTestExtension;
 import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
+import org.apache.james.backends.rabbitmq.DockerRabbitMQ;
+import org.apache.james.backends.rabbitmq.RabbitMQConnectionFactory;
 import org.apache.james.core.Username;
 import org.apache.james.junit.categories.BasicFeature;
 import org.apache.james.mailbox.DefaultMailboxes;
 import org.apache.james.mailbox.events.Event;
+import org.apache.james.mailbox.events.EventDispatcher.DispatchingFailureGroup;
 import org.apache.james.mailbox.events.Group;
 import org.apache.james.mailbox.events.MailboxListener;
 import org.apache.james.mailbox.model.MailboxId;
@@ -83,7 +87,9 @@ import io.restassured.http.ContentType;
 @Tag(BasicFeature.TAG)
 class RabbitMQEventDeadLettersIntegrationTest {
     public static class RetryEventsListenerGroup extends Group {
+    }
 
+    public static class RetryEventsListenerGroup2 extends Group {
     }
 
     public static class RetryEventsListener implements MailboxListener.GroupMailboxListener {
@@ -139,42 +145,73 @@ class RabbitMQEventDeadLettersIntegrationTest {
         }
     }
 
+    public static class RetryEventsListener2 extends RetryEventsListener {
+        static final Group GROUP = new RetryEventsListenerGroup2();
+
+        @Override
+        public Group getDefaultGroup() {
+            return GROUP;
+        }
+    }
+
     public static class RetryEventsListenerExtension implements GuiceModuleTestExtension {
         private RetryEventsListener retryEventsListener;
+        private RetryEventsListener2 retryEventsListener2;
 
         @Override
         public void beforeEach(ExtensionContext extensionContext) throws Exception {
             retryEventsListener = new RetryEventsListener();
+            retryEventsListener2 = new RetryEventsListener2();
         }
 
         @Override
         public Module getModule() {
-            return binder -> Multibinder.newSetBinder(binder, MailboxListener.GroupMailboxListener.class)
-                .addBinding()
-                .toInstance(retryEventsListener);
+            return binder -> {
+                Multibinder<MailboxListener.GroupMailboxListener> setBinder = Multibinder.newSetBinder(binder, MailboxListener.GroupMailboxListener.class);
+                setBinder.addBinding().toInstance(retryEventsListener);
+                setBinder.addBinding().toInstance(retryEventsListener2);
+            };
         }
 
         @Override
         public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-            return parameterContext.getParameter().getType() == RetryEventsListener.class;
+            Class<?> paramType = parameterContext.getParameter().getType();
+            return paramType == RetryEventsListener.class
+                || paramType == RetryEventsListener2.class;
         }
 
         @Override
         public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
-            return retryEventsListener;
+            Class<?> paramType = parameterContext.getParameter().getType();
+            if (paramType == RetryEventsListener.class) {
+                return retryEventsListener;
+            } else if (paramType == RetryEventsListener2.class) {
+                return retryEventsListener2;
+            }
+
+            throw new IllegalArgumentException("unsupported type");
         }
     }
-    
+
+    private static RabbitMQExtension RABBIT_MQ_EXTENSION = new RabbitMQExtension();
     @RegisterExtension
     static JamesServerExtension testExtension = new JamesServerBuilder()
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
         .extension(new AwsS3BlobStoreExtension())
-        .extension(new RabbitMQExtension())
+        .extension(RABBIT_MQ_EXTENSION)
         .extension(new RetryEventsListenerExtension())
         .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
             .combineWith(CassandraRabbitMQJamesServerMain.MODULES)
-            .overrideWith(new WebadminIntegrationTestModule()))
+            .overrideWith(new WebadminIntegrationTestModule())
+            .overrideWith(binder -> {
+                try {
+                    binder.bind(RabbitMQConnectionFactory.class)
+                        .toInstance(RABBIT_MQ_EXTENSION.dockerRabbitMQ().createRabbitConnectionFactory());
+                } catch (URISyntaxException e) {
+                    throw new RuntimeException(e);
+                }
+            }))
         .build();
 
     //This value is duplicated from default configuration to ensure we keep the same behavior over time
@@ -186,6 +223,7 @@ class RabbitMQEventDeadLettersIntegrationTest {
     private static final String BOB_PASSWORD = "bobPassword";
     private static final String EVENTS_ACTION = "reDeliver";
     private static final String GROUP_ID = new RetryEventsListenerGroup().asString();
+    private static final String DISPATCHING_FAILURE_GROUP_ID = DispatchingFailureGroup.INSTANCE.getClass().getName();
     private static final MailboxPath BOB_INBOX_PATH = MailboxPath.inbox(Username.of(BOB));
 
     private Duration slowPacedPollInterval = ONE_HUNDRED_MILLISECONDS;
@@ -582,4 +620,47 @@ class RabbitMQEventDeadLettersIntegrationTest {
         .then()
             .statusCode(HttpStatus.OK_200);
     }
+
+    @Test
+    void failedDispatchingShouldBeRedeliveredToAllListeners(RetryEventsListener listener1,
+                                                            RetryEventsListener2 listener2,
+                                                            DockerRabbitMQ dockerRabbitMQ) {
+        dockerRabbitMQ.pause();
+        try {
+            generateInitialEvent();
+        } catch (Exception e) {
+            // ignore
+        }
+        dockerRabbitMQ.unpause();
+
+        waitForFailedDispatching();
+        waitForReDeliver(DISPATCHING_FAILURE_GROUP_ID);
+
+        awaitAtMostTenSeconds.untilAsserted(() ->
+            assertThat(listener1.getSuccessfulEvents())
+                .hasSameSizeAs(listener2.getSuccessfulEvents())
+                .hasSize(1));
+    }
+
+    private void waitForReDeliver(String groupId) {
+        String taskId = with()
+            .queryParam("action", EVENTS_ACTION)
+        .post(EventDeadLettersRoutes.BASE_PATH + "/groups/" + groupId)
+            .jsonPath()
+            .get("taskId");
+        with()
+            .basePath(TasksRoutes.BASE)
+            .get(taskId + "/await");
+    }
+
+    private void waitForFailedDispatching() {
+        calmlyAwait.untilAsserted(() ->
+            given()
+                .basePath(EventDeadLettersRoutes.BASE_PATH + "/groups/" + DISPATCHING_FAILURE_GROUP_ID)
+                .get()
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .body(".", hasSize(1)));
+    }
 }


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


[james-project] 06/39: JAMES-3117 Guice wiring for PeriodicalHealthChecks

Posted by bt...@apache.org.
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 765d20aff6fe938a704c799ceabdc303a491f469
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Mar 23 10:05:05 2020 +0700

    JAMES-3117 Guice wiring for PeriodicalHealthChecks
---
 .../org/apache/james/CassandraJamesServerMain.java |  2 +
 .../modules/PeriodicalHealthChecksModule.java      | 69 ++++++++++++++++++++++
 .../org/apache/james/MemoryJamesServerMain.java    |  2 +
 3 files changed, 73 insertions(+)

diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
index 9bc756c..31b1a38 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
@@ -25,6 +25,7 @@ import org.apache.james.eventsourcing.eventstore.cassandra.EventNestedTypes;
 import org.apache.james.json.DTOModule;
 import org.apache.james.modules.BlobExportMechanismModule;
 import org.apache.james.modules.MailboxModule;
+import org.apache.james.modules.PeriodicalHealthChecksModule;
 import org.apache.james.modules.activemq.ActiveMQQueueModule;
 import org.apache.james.modules.data.CassandraDLPConfigurationStoreModule;
 import org.apache.james.modules.data.CassandraDomainListModule;
@@ -131,6 +132,7 @@ public class CassandraJamesServerMain implements JamesServerMain {
         new CassandraSessionModule(),
         new CassandraSieveRepositoryModule(),
         new CassandraUsersRepositoryModule(),
+        new PeriodicalHealthChecksModule(),
         BLOB_MODULE,
         CASSANDRA_EVENT_STORE_JSON_SERIALIZATION_DEFAULT_MODULE);
 
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/PeriodicalHealthChecksModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/PeriodicalHealthChecksModule.java
new file mode 100644
index 0000000..c338d00
--- /dev/null
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/PeriodicalHealthChecksModule.java
@@ -0,0 +1,69 @@
+/****************************************************************
+ * 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;
+
+import java.io.FileNotFoundException;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.PeriodicalHealthChecks;
+import org.apache.james.PeriodicalHealthChecksConfiguration;
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.utils.InitializationOperation;
+import org.apache.james.utils.InitilizationOperationBuilder;
+import org.apache.james.utils.PropertiesProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.multibindings.ProvidesIntoSet;
+
+public class PeriodicalHealthChecksModule extends AbstractModule {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PeriodicalHealthChecksModule.class);
+    private static final String FILENAME = "healthcheck";
+
+    @Override
+    protected void configure() {
+        Multibinder.newSetBinder(binder(), HealthCheck.class);
+    }
+
+    @Singleton
+    @Provides
+    PeriodicalHealthChecksConfiguration periodicalHealthChecksConfiguration(PropertiesProvider propertiesProvider) throws ConfigurationException {
+        try {
+            Configuration configuration = propertiesProvider.getConfigurations(FILENAME);
+            return PeriodicalHealthChecksConfiguration.from(configuration);
+        } catch (FileNotFoundException e) {
+            LOGGER.warn("Could not find {} configuration file, using default configuration", FILENAME);
+            return PeriodicalHealthChecksConfiguration.DEFAULT_CONFIGURATION;
+        }
+    }
+
+    @ProvidesIntoSet
+    InitializationOperation configurePeriodicalHealthChecks(PeriodicalHealthChecks periodicalHealthChecks) {
+        return InitilizationOperationBuilder
+            .forClass(PeriodicalHealthChecks.class)
+            .init(periodicalHealthChecks::start);
+    }
+}
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
index be8595a..bf72a33 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -26,6 +26,7 @@ import org.apache.james.jwt.JwtConfiguration;
 import org.apache.james.modules.BlobExportMechanismModule;
 import org.apache.james.modules.BlobMemoryModule;
 import org.apache.james.modules.MailboxModule;
+import org.apache.james.modules.PeriodicalHealthChecksModule;
 import org.apache.james.modules.data.MemoryDataJmapModule;
 import org.apache.james.modules.data.MemoryDataModule;
 import org.apache.james.modules.eventstore.MemoryEventStoreModule;
@@ -110,6 +111,7 @@ public class MemoryJamesServerMain implements JamesServerMain {
         new MemoryEventStoreModule(),
         new MemoryMailboxModule(),
         new MemoryMailQueueModule(),
+        new PeriodicalHealthChecksModule(),
         new TaskManagerModule());
 
     public static final Module SMTP_ONLY_MODULE = Modules.combine(


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


[james-project] 20/39: JAMES-3117 Reactive HealthCheck

Posted by bt...@apache.org.
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 5f74bef2a4163562aa29b48f334ef14b9c513b97
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Apr 1 11:04:55 2020 +0700

    JAMES-3117 Reactive HealthCheck
---
 .../cassandra/utils/CassandraAsyncExecutor.java    |  6 ++++++
 .../cassandra/utils/CassandraHealthCheck.java      | 21 +++++++++----------
 .../apache/james/core/healthcheck/HealthCheck.java | 12 ++++++++++-
 .../events/EventDeadLettersHealthCheck.java        | 24 +++++++++++-----------
 .../org/apache/james/PeriodicalHealthChecks.java   |  3 +--
 5 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java
index 8b911dd..86b192b 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraAsyncExecutor.java
@@ -48,6 +48,12 @@ public class CassandraAsyncExecutor {
                 .publishOn(Schedulers.elastic()));
     }
 
+    public Mono<ResultSet> execute(String statement) {
+        return Mono.defer(() -> Mono.fromFuture(FutureConverter
+                .toCompletableFuture(session.executeAsync(statement)))
+                .publishOn(Schedulers.elastic()));
+    }
+
     public Mono<Boolean> executeReturnApplied(Statement statement) {
         return execute(statement)
                 .map(row -> row.wasApplied());
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java
index d184e30..c502d57 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/CassandraHealthCheck.java
@@ -27,6 +27,8 @@ import org.apache.james.core.healthcheck.Result;
 
 import com.datastax.driver.core.Session;
 
+import reactor.core.publisher.Mono;
+
 /**
  * Health check for the Cassandra backend.
  *
@@ -36,11 +38,11 @@ public class CassandraHealthCheck implements HealthCheck {
     private static final ComponentName COMPONENT_NAME = new ComponentName("Cassandra backend");
     private static final String SAMPLE_QUERY = "SELECT NOW() FROM system.local";
 
-    private final Session session;
+    private final CassandraAsyncExecutor queryExecutor;
 
     @Inject
     public CassandraHealthCheck(Session session) {
-        this.session = session;
+        this.queryExecutor = new CassandraAsyncExecutor(session);
     }
 
     @Override
@@ -49,14 +51,11 @@ public class CassandraHealthCheck implements HealthCheck {
     }
 
     @Override
-    public Result check() {
-        try {
-            // execute a simple query to check if cassandra is responding
-            // idea from: https://stackoverflow.com/questions/10246287
-            session.execute(SAMPLE_QUERY);
-            return Result.healthy(COMPONENT_NAME);
-        } catch (Exception e) {
-            return Result.unhealthy(COMPONENT_NAME, "Error checking Cassandra backend", e);
-        }
+    public Mono<Result> checkReactive() {
+        // execute a simple query to check if cassandra is responding
+        // idea from: https://stackoverflow.com/questions/10246287
+        return queryExecutor.execute(SAMPLE_QUERY)
+            .map(resultSet -> Result.healthy(COMPONENT_NAME))
+            .onErrorResume(e -> Mono.just(Result.unhealthy(COMPONENT_NAME, "Error checking Cassandra backend", e)));
     }
 }
diff --git a/core/src/main/java/org/apache/james/core/healthcheck/HealthCheck.java b/core/src/main/java/org/apache/james/core/healthcheck/HealthCheck.java
index f7659b0..effd3c6 100644
--- a/core/src/main/java/org/apache/james/core/healthcheck/HealthCheck.java
+++ b/core/src/main/java/org/apache/james/core/healthcheck/HealthCheck.java
@@ -18,8 +18,18 @@
  ****************************************************************/
 package org.apache.james.core.healthcheck;
 
+import org.reactivestreams.Publisher;
+
+import reactor.core.publisher.Mono;
+
 public interface HealthCheck {
     ComponentName componentName();
 
-    Result check();
+    default Result check() {
+        return Mono.from(checkReactive()).block();
+    }
+
+    default Publisher<Result> checkReactive() {
+        return Mono.fromCallable(this::check);
+    }
 }
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java b/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
index 8fc1b1b..dfc8ce1 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
@@ -25,6 +25,8 @@ import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
 
+import reactor.core.publisher.Mono;
+
 public class EventDeadLettersHealthCheck implements HealthCheck {
     private static final ComponentName COMPONENT_NAME = new ComponentName("EventDeadLettersHealthCheck");
 
@@ -41,17 +43,15 @@ public class EventDeadLettersHealthCheck implements HealthCheck {
     }
 
     @Override
-    public Result check() {
-        try {
-            boolean containEvents = eventDeadLetters.containEvents().block();
-
-            if (containEvents) {
-                return Result.degraded(COMPONENT_NAME, "EventDeadLetters contain events. This might indicate transient failure on mailbox event processing.");
-            }
-
-            return Result.healthy(COMPONENT_NAME);
-        } catch (Exception e) {
-            return Result.unhealthy(COMPONENT_NAME, "Error checking EventDeadLettersHealthCheck", e);
-        }
+    public Mono<Result> checkReactive() {
+        return eventDeadLetters.containEvents()
+            .map(containEvents -> {
+                if (containEvents) {
+                    return Result.degraded(COMPONENT_NAME, "EventDeadLetters contain events. This might indicate transient failure on mailbox event processing.");
+                }
+
+                return Result.healthy(COMPONENT_NAME);
+            })
+            .onErrorResume(e -> Mono.just(Result.unhealthy(COMPONENT_NAME, "Error checking EventDeadLettersHealthCheck", e)));
     }
 }
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
index acede14..68c08b7 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -60,8 +60,7 @@ public class PeriodicalHealthChecks implements Startable {
     public void start() {
         disposable = Flux.interval(configuration.getPeriod(), scheduler)
             .flatMap(any -> Flux.fromIterable(healthChecks)
-                .flatMap(healthCheck ->
-                    Mono.fromCallable(healthCheck::check)))
+                .flatMap(healthCheck -> Mono.from(healthCheck.checkReactive())))
             .doOnNext(this::logResult)
             .onErrorContinue(this::logError)
             .subscribeOn(Schedulers.elastic())


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


[james-project] 19/39: JAMES-3117 Install PeriodicalHealthChecksModule in CommonServicesModule

Posted by bt...@apache.org.
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 985f27b5a562d46a285a55df99d4c4b9beb960ad
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Apr 1 10:50:16 2020 +0700

    JAMES-3117 Install PeriodicalHealthChecksModule in CommonServicesModule
---
 .../src/main/java/org/apache/james/CassandraJamesServerMain.java        | 2 --
 .../src/main/java/org/apache/james/modules/CommonServicesModule.java    | 1 +
 .../src/main/java/org/apache/james/MemoryJamesServerMain.java           | 2 --
 3 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
index 31b1a38..9bc756c 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
@@ -25,7 +25,6 @@ import org.apache.james.eventsourcing.eventstore.cassandra.EventNestedTypes;
 import org.apache.james.json.DTOModule;
 import org.apache.james.modules.BlobExportMechanismModule;
 import org.apache.james.modules.MailboxModule;
-import org.apache.james.modules.PeriodicalHealthChecksModule;
 import org.apache.james.modules.activemq.ActiveMQQueueModule;
 import org.apache.james.modules.data.CassandraDLPConfigurationStoreModule;
 import org.apache.james.modules.data.CassandraDomainListModule;
@@ -132,7 +131,6 @@ public class CassandraJamesServerMain implements JamesServerMain {
         new CassandraSessionModule(),
         new CassandraSieveRepositoryModule(),
         new CassandraUsersRepositoryModule(),
-        new PeriodicalHealthChecksModule(),
         BLOB_MODULE,
         CASSANDRA_EVENT_STORE_JSON_SERIALIZATION_DEFAULT_MODULE);
 
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/CommonServicesModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/CommonServicesModule.java
index 86473cc..2ad721c 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/CommonServicesModule.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/CommonServicesModule.java
@@ -62,6 +62,7 @@ public class CommonServicesModule extends AbstractModule {
         install(new CleanupTaskModule());
         install(new MimeMessageModule());
         install(new ClockModule());
+        install(new PeriodicalHealthChecksModule());
 
         bind(FileSystem.class).toInstance(fileSystem);
         bind(Configuration.class).toInstance(configuration);
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
index bf72a33..be8595a 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -26,7 +26,6 @@ import org.apache.james.jwt.JwtConfiguration;
 import org.apache.james.modules.BlobExportMechanismModule;
 import org.apache.james.modules.BlobMemoryModule;
 import org.apache.james.modules.MailboxModule;
-import org.apache.james.modules.PeriodicalHealthChecksModule;
 import org.apache.james.modules.data.MemoryDataJmapModule;
 import org.apache.james.modules.data.MemoryDataModule;
 import org.apache.james.modules.eventstore.MemoryEventStoreModule;
@@ -111,7 +110,6 @@ public class MemoryJamesServerMain implements JamesServerMain {
         new MemoryEventStoreModule(),
         new MemoryMailboxModule(),
         new MemoryMailQueueModule(),
-        new PeriodicalHealthChecksModule(),
         new TaskManagerModule());
 
     public static final Module SMTP_ONLY_MODULE = Modules.combine(


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


[james-project] 35/39: JAMES-3149 reactify more getMailboxes

Posted by bt...@apache.org.
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 3943d2f09c08b78ee082e62f0696133f1df3c25a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Apr 14 10:28:19 2020 +0700

    JAMES-3149 reactify more getMailboxes
---
 .../cassandra/mail/CassandraMessageMapper.java     | 16 ++-------
 .../james/mailbox/store/StoreMailboxManager.java   | 40 ++++++++--------------
 .../james/mailbox/store/mail/MessageMapper.java    | 10 ++----
 .../store/mail/model/MessageMapperTest.java        | 18 ----------
 4 files changed, 20 insertions(+), 64 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
index 7d189c2..7faa9cc 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
@@ -20,7 +20,6 @@
 package org.apache.james.mailbox.cassandra.mail;
 
 import java.time.Duration;
-import java.util.Collection;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
@@ -62,7 +61,6 @@ import com.google.common.collect.ImmutableList;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
 
 public class CassandraMessageMapper implements MessageMapper {
     public static final Logger LOGGER = LoggerFactory.getLogger(CassandraMessageMapper.class);
@@ -123,10 +121,11 @@ public class CassandraMessageMapper implements MessageMapper {
 
     @Override
     public MailboxCounters getMailboxCounters(Mailbox mailbox) {
-        return getMailboxCountersAsMono(mailbox).block();
+        return getMailboxCountersReactive(mailbox).block();
     }
 
-    private Mono<MailboxCounters> getMailboxCountersAsMono(Mailbox mailbox) {
+    @Override
+    public Mono<MailboxCounters> getMailboxCountersReactive(Mailbox mailbox) {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         return mailboxCounterDAO.retrieveMailboxCounters(mailboxId)
             .defaultIfEmpty(MailboxCounters.builder()
@@ -137,15 +136,6 @@ public class CassandraMessageMapper implements MessageMapper {
     }
 
     @Override
-    public List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) {
-        return Flux.fromIterable(mailboxes)
-            .publishOn(Schedulers.elastic())
-            .concatMap(this::getMailboxCountersAsMono)
-            .toStream()
-            .collect(Guavate.toImmutableList());
-    }
-
-    @Override
     public void delete(Mailbox mailbox, MailboxMessage message) {
         deleteAsFuture(message)
             .block();
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index 76f8bfb..e9ad7da 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -22,7 +22,6 @@ package org.apache.james.mailbox.store;
 import static org.apache.james.mailbox.store.mail.AbstractMessageMapper.UNLIMITED;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Optional;
@@ -87,7 +86,6 @@ import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
 import com.github.steveash.guavate.Guavate;
-import com.google.common.base.Functions;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -593,26 +591,23 @@ public class StoreMailboxManager implements MailboxManager {
 
     @Override
     public List<MailboxMetaData> search(MailboxQuery mailboxExpression, MailboxSession session) throws MailboxException {
-        return searchMailboxesMetadata(mailboxExpression, session, Right.Lookup);
+        return searchMailboxesMetadata(mailboxExpression, session, Right.Lookup)
+            .collect(Guavate.toImmutableList())
+            .block();
     }
 
-    private List<MailboxMetaData> searchMailboxesMetadata(MailboxQuery mailboxQuery, MailboxSession session, Right right) throws MailboxException {
-        List<Mailbox> mailboxes = searchMailboxes(mailboxQuery, session, right).collectList().block();
-
-        ImmutableMap<MailboxId, MailboxCounters> counters = getMailboxCounters(mailboxes, session)
-            .stream()
-            .collect(Guavate.toImmutableMap(
-                MailboxCounters::getMailboxId,
-                Functions.identity()));
+    private Flux<MailboxMetaData> searchMailboxesMetadata(MailboxQuery mailboxQuery, MailboxSession session, Right right) throws MailboxException {
+        Mono<List<Mailbox>> mailboxesMono = searchMailboxes(mailboxQuery, session, right).collectList();
+        MessageMapper messageMapper = mailboxSessionMapperFactory.getMessageMapper(session);
 
-        return mailboxes
-            .stream()
-            .filter(mailboxQuery::matches)
-            .map(Throwing.<Mailbox, MailboxMetaData>function(
-                mailbox -> toMailboxMetadata(session, mailboxes, mailbox, retrieveCounters(counters, mailbox)))
-                .sneakyThrow())
-            .sorted(MailboxMetaData.COMPARATOR)
-            .collect(Guavate.toImmutableList());
+        return mailboxesMono
+            .flatMapMany(mailboxes -> Flux.fromIterable(mailboxes)
+                .filter(mailboxQuery::matches)
+                .flatMap(mailbox -> messageMapper.getMailboxCountersReactive(mailbox)
+                    .map(Throwing.<MailboxCounters, MailboxMetaData>function(
+                        counters -> toMailboxMetadata(session, mailboxes, mailbox, counters))
+                        .sneakyThrow())))
+            .sort(MailboxMetaData.COMPARATOR);
     }
 
     private Flux<Mailbox> searchMailboxes(MailboxQuery mailboxQuery, MailboxSession session, Right right) throws MailboxException {
@@ -823,11 +818,4 @@ public class StoreMailboxManager implements MailboxManager {
         Mailbox mailbox = mapper.findMailboxByPathBlocking(mailboxPath);
         return mapper.hasChildren(mailbox, session.getPathDelimiter());
     }
-
-    private List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes, MailboxSession session) throws MailboxException {
-        MessageMapper messageMapper = mailboxSessionMapperFactory.getMessageMapper(session);
-        return messageMapper.getMailboxCounters(mailboxes.stream()
-            .filter(Throwing.<Mailbox>predicate(mailbox -> storeRightManager.hasRight(mailbox, Right.Read, session)).sneakyThrow())
-            .collect(Guavate.toImmutableList()));
-    }
 }
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
index c0d34d4..d7c8428 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
@@ -18,7 +18,6 @@
  ****************************************************************/
 package org.apache.james.mailbox.store.mail;
 
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -39,8 +38,7 @@ import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.apache.james.mailbox.store.mail.model.Property;
 import org.apache.james.mailbox.store.transaction.Mapper;
 
-import com.github.fge.lambdas.Throwing;
-import com.github.steveash.guavate.Guavate;
+import reactor.core.publisher.Mono;
 
 /**
  * Maps {@link MailboxMessage} in a {@link org.apache.james.mailbox.MessageManager}. A {@link MessageMapper} has a lifecycle from the start of a request
@@ -73,10 +71,8 @@ public interface MessageMapper extends Mapper {
 
     MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException;
 
-    default List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) throws MailboxException {
-        return mailboxes.stream()
-            .map(Throwing.<Mailbox, MailboxCounters>function(this::getMailboxCounters).sneakyThrow())
-            .collect(Guavate.toImmutableList());
+    default Mono<MailboxCounters> getMailboxCountersReactive(Mailbox mailbox) {
+        return Mono.fromCallable(() -> getMailboxCounters(mailbox));
     }
 
     /**
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
index ae67a36..66ee7bf 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
@@ -43,7 +43,6 @@ import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Mailbox;
-import org.apache.james.mailbox.model.MailboxCounters;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageMetaData;
@@ -128,23 +127,6 @@ public abstract class MessageMapperTest {
     }
 
     @Test
-    void getMailboxCountersShouldReturnStoredValue() throws MailboxException {
-        saveMessages();
-        assertThat(messageMapper.getMailboxCounters(ImmutableList.of(benwaInboxMailbox, benwaWorkMailbox)))
-            .containsExactlyInAnyOrder(
-                MailboxCounters.builder()
-                    .mailboxId(benwaInboxMailbox.getMailboxId())
-                    .count(5)
-                    .unseen(5)
-                    .build(),
-                MailboxCounters.builder()
-                    .mailboxId(benwaWorkMailbox.getMailboxId())
-                    .count(1)
-                    .unseen(1)
-                    .build());
-    }
-
-    @Test
     void mailboxCountShouldBeDecrementedAfterAMessageDelete() throws MailboxException {
         saveMessages();
         messageMapper.delete(benwaInboxMailbox, message1);


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


[james-project] 38/39: [REFACTORING] CassandraMessageIdToImapUidDAO never uses cassandraUtils

Posted by bt...@apache.org.
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 125c08ef4dfb43764f9a89175b42b0f9257bbf54
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Apr 15 22:13:10 2020 +0700

    [REFACTORING] CassandraMessageIdToImapUidDAO never uses cassandraUtils
---
 .../cassandra/mail/CassandraMessageIdToImapUidDAO.java        | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
index bbd693d..7eb2062 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
@@ -47,7 +47,6 @@ import javax.mail.Flags;
 import javax.mail.Flags.Flag;
 
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
-import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 import org.apache.james.mailbox.cassandra.ids.CassandraId;
@@ -62,7 +61,6 @@ import com.datastax.driver.core.Row;
 import com.datastax.driver.core.Session;
 import com.datastax.driver.core.Statement;
 import com.datastax.driver.core.querybuilder.QueryBuilder;
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
 
 import reactor.core.publisher.Flux;
@@ -79,10 +77,9 @@ public class CassandraMessageIdToImapUidDAO {
     private final PreparedStatement update;
     private final PreparedStatement selectAll;
     private final PreparedStatement select;
-    private CassandraUtils cassandraUtils;
 
     @Inject
-    public CassandraMessageIdToImapUidDAO(Session session, CassandraMessageId.Factory messageIdFactory, CassandraUtils cassandraUtils) {
+    public CassandraMessageIdToImapUidDAO(Session session, CassandraMessageId.Factory messageIdFactory) {
         this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
         this.messageIdFactory = messageIdFactory;
         this.delete = prepareDelete(session);
@@ -90,12 +87,6 @@ public class CassandraMessageIdToImapUidDAO {
         this.update = prepareUpdate(session);
         this.selectAll = prepareSelectAll(session);
         this.select = prepareSelect(session);
-        this.cassandraUtils = cassandraUtils;
-    }
-
-    @VisibleForTesting
-    public CassandraMessageIdToImapUidDAO(Session session, CassandraMessageId.Factory messageIdFactory) {
-        this(session, messageIdFactory, CassandraUtils.WITH_DEFAULT_CONFIGURATION);
     }
 
     private PreparedStatement prepareDelete(Session session) {


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


[james-project] 27/39: JAMES-3139 RabbitMQEventBus error dispatching tests

Posted by bt...@apache.org.
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 e530af7d476d5406343063356ce7ccdfa9f2a50f
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Wed Apr 8 16:49:25 2020 +0700

    JAMES-3139 RabbitMQEventBus error dispatching tests
---
 .../james/mailbox/events/RabbitMQEventBusTest.java | 64 ++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
index ee96d11..f462fba 100644
--- a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
@@ -76,6 +76,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
 import org.mockito.stubbing.Answer;
 
 import com.google.common.collect.ImmutableSet;
+
 import reactor.core.publisher.Mono;
 import reactor.rabbitmq.BindingSpecification;
 import reactor.rabbitmq.ExchangeSpecification;
@@ -716,6 +717,69 @@ class RabbitMQEventBusTest implements GroupContract.SingleEventBusGroupContract,
 
     }
 
+    @Nested
+    class ErrorDispatchingTest {
+
+        @AfterEach
+        void tearDown() {
+            rabbitMQExtension.getRabbitMQ().unpause();
+        }
+
+        @Test
+        void dispatchShouldNotSendToGroupListenerWhenError() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+
+            try {
+                eventBus().dispatch(EVENT, NO_KEYS).block();
+            } catch (Exception e) {
+                // ignore
+            }
+
+            getSpeedProfile().longWaitCondition()
+                .untilAsserted(() -> assertThat(eventCollector.getEvents()).isEmpty());
+        }
+
+        @Test
+        void dispatchShouldNotPersistEventWhenDispatchingNoKeyGetError() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+
+            try {
+                eventBus().dispatch(EVENT, NO_KEYS).block();
+            } catch (Exception e) {
+                // ignore
+            }
+
+            getSpeedProfile().longWaitCondition()
+                .untilAsserted(() ->
+                    assertThat(deadLetter().containEvents().block()).isFalse());
+        }
+
+        @Test
+        void dispatchShouldNotPersistEventWhenDispatchingWithKeysGetError() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+            eventBus().register(eventCollector, KEY_1);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+
+            try {
+                eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+            } catch (Exception e) {
+                // ignore
+            }
+
+            getSpeedProfile().longWaitCondition()
+                .untilAsserted(() ->
+                    assertThat(deadLetter().containEvents().block()).isFalse());
+        }
+    }
+
     private void assertThatListenerReceiveOneEvent(MailboxListener listener) {
         RabbitMQFixture.awaitAtMostThirtySeconds
             .untilAsserted(() -> verify(listener).event(EVENT));


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


[james-project] 33/39: JAMES-3143 Fix typo in Cassandra message inconsistency ADR

Posted by bt...@apache.org.
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 2f7bc3d2beb70dc557d0eb09158eda2d8527dfbc
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Apr 13 11:39:26 2020 +0700

    JAMES-3143 Fix typo in Cassandra message inconsistency ADR
---
 src/adr/0022-cassandra-message-inconsistency.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/adr/0022-cassandra-message-inconsistency.md b/src/adr/0022-cassandra-message-inconsistency.md
index ac366a7..7a83d55 100644
--- a/src/adr/0022-cassandra-message-inconsistency.md
+++ b/src/adr/0022-cassandra-message-inconsistency.md
@@ -60,7 +60,7 @@ The message is UNSEEN in IMAP
 Adopt `imapUidTable` as a source of truth. Because `messageId` allows tracking changes to messages accross mailboxes 
 upon copy and moves. Furthermore, that is the table on which conditional flags updates are performed.
 
-All writes will be performed to `messageIdTable` then performed on `imapUidTable` if successful.
+All writes will be performed to `imapUidTable` then performed on `messageIdTable` if successful.
 
 We thus need to modify CassandraMessageMapper 'add' + 'copy' to first write to the source of truth (`imapUidTable`)
 


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


[james-project] 08/39: JAMES-3117 Remove 'intialDelay', change 'period' to Duration type

Posted by bt...@apache.org.
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 d8ceefb911882c073a7da69310afbfdbf0825b54
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Wed Mar 25 18:01:50 2020 +0700

    JAMES-3117 Remove 'intialDelay', change 'period' to Duration type
---
 .../destination/conf/healthcheck.properties        |   3 -
 .../destination/conf/healthcheck.properties        |   7 +-
 .../destination/conf/healthcheck.properties        |   7 +-
 .../destination/conf/healthcheck.properties        |   7 +-
 .../destination/conf/healthcheck.properties        |   7 +-
 .../jpa/destination/conf/healthcheck.properties    |   7 +-
 .../memory/destination/conf/healthcheck.properties |   7 +-
 .../james/PeriodicalHealthChecksConfiguration.java |  54 +++------
 .../PeriodicalHealthChecksConfigurationTest.java   | 128 ++++++++-------------
 src/site/xdoc/server/config-healthcheck.xml        |   3 -
 10 files changed, 79 insertions(+), 151 deletions(-)

diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
index 23b9879..235a828 100644
--- a/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
 # Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
 # healthcheck.period=60
 
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
index 23b9879..24873dc 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
+# healthcheck.period=PT60s
 
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
index 23b9879..24873dc 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
+# healthcheck.period=PT60s
 
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
index 23b9879..24873dc 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
+# healthcheck.period=PT60s
 
diff --git a/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties b/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
index 23b9879..24873dc 100644
--- a/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
+# healthcheck.period=PT60s
 
diff --git a/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties b/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
index 23b9879..24873dc 100644
--- a/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
+# healthcheck.period=PT60s
 
diff --git a/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties b/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
index 23b9879..24873dc 100644
--- a/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
+++ b/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
@@ -22,9 +22,6 @@
 
 # Read https://james.apache.org/server/config-healthcheck.html for further details
 
-# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
-# healthcheck.initial.delay=60
-
-# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
-# healthcheck.period=60
+# Optional. Period between two PeriodicalHealthChecks. The formats accepted are based on the ISO-8601 duration format. Default duration is 60 seconds.
+# healthcheck.period=PT60s
 
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
index e8e052f..aa8bbfb 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
@@ -19,6 +19,7 @@
 
 package org.apache.james;
 
+import java.time.Duration;
 import java.util.Objects;
 
 import org.apache.commons.configuration2.Configuration;
@@ -28,71 +29,53 @@ import com.google.common.base.Preconditions;
 
 public class PeriodicalHealthChecksConfiguration {
 
-    static final String HEALTH_CHECK_INITIAL_DELAY = "healthcheck.initial.delay";
-    static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
-    static final long DEFAULT_HEALTH_CHECK_INITIAL_DELAY = 60;
-    static final long DEFAULT_HEALTH_CHECK_PERIOD = 60;
-    static final long ZERO = 0;
+    private static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
+    private static final String DEFAULT_HEALTH_CHECK_PERIOD = "PT60s";
     public static final PeriodicalHealthChecksConfiguration DEFAULT_CONFIGURATION = builder()
-        .initialDelay(DEFAULT_HEALTH_CHECK_INITIAL_DELAY)
-        .period(DEFAULT_HEALTH_CHECK_PERIOD)
+        .period(Duration.parse(DEFAULT_HEALTH_CHECK_PERIOD))
         .build();
 
     public interface Builder {
 
         @FunctionalInterface
-        interface RequiredInitialDelay {
-            RequiredPeriod initialDelay(long initialDelay);
-        }
-
-        @FunctionalInterface
         interface RequiredPeriod {
-            ReadyToBuild period(long period);
+            ReadyToBuild period(Duration period);
         }
 
         class ReadyToBuild {
-            private final long initialDelay;
-            private final long period;
+            private final Duration period;
 
-            ReadyToBuild(long initialDelay, long period) {
-                this.initialDelay = initialDelay;
+            ReadyToBuild(Duration period) {
                 this.period = period;
             }
 
             PeriodicalHealthChecksConfiguration build() {
-                Preconditions.checkArgument(initialDelay > ZERO, "'initialDelay' must be positive");
-                Preconditions.checkArgument(period > ZERO, "'period' must be positive");
+                Preconditions.checkArgument(!period.isNegative(), "'period' must be positive");
+                Preconditions.checkArgument(!period.isZero(), "'period' must be greater than zero");
 
-                return new PeriodicalHealthChecksConfiguration(initialDelay, period);
+                return new PeriodicalHealthChecksConfiguration(period);
             }
         }
     }
 
-    public static Builder.RequiredInitialDelay builder() {
-        return initialDelay -> period -> new Builder.ReadyToBuild(initialDelay, period);
+    public static Builder.RequiredPeriod builder() {
+        return period -> new Builder.ReadyToBuild(period);
     }
 
     public static PeriodicalHealthChecksConfiguration from(Configuration configuration) {
         return builder()
-            .initialDelay(configuration.getLong(HEALTH_CHECK_INITIAL_DELAY, DEFAULT_HEALTH_CHECK_INITIAL_DELAY))
-            .period(configuration.getLong(HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD))
+            .period(Duration.parse(configuration.getString(HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD)))
             .build();
     }
 
-    private final long initialDelay;
-    private final long period;
+    private final Duration period;
 
     @VisibleForTesting
-    PeriodicalHealthChecksConfiguration(long initialDelay, long period) {
-        this.initialDelay = initialDelay;
+    PeriodicalHealthChecksConfiguration(Duration period) {
         this.period = period;
     }
 
-    public long getInitialDelay() {
-        return initialDelay;
-    }
-
-    public long getPeriod() {
+    public Duration getPeriod() {
         return period;
     }
 
@@ -101,14 +84,13 @@ public class PeriodicalHealthChecksConfiguration {
         if (o instanceof PeriodicalHealthChecksConfiguration) {
             PeriodicalHealthChecksConfiguration that = (PeriodicalHealthChecksConfiguration) o;
 
-            return Objects.equals(this.initialDelay, that.initialDelay)
-                && Objects.equals(this.period, that.period);
+            return Objects.equals(this.period, that.period);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hash(initialDelay, period);
+        return Objects.hash(period);
     }
 }
\ No newline at end of file
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
index a51945a..d7830cf 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
@@ -19,24 +19,24 @@
 
 package org.apache.james;
 
-import static org.apache.james.PeriodicalHealthChecksConfiguration.DEFAULT_HEALTH_CHECK_INITIAL_DELAY;
-import static org.apache.james.PeriodicalHealthChecksConfiguration.DEFAULT_HEALTH_CHECK_PERIOD;
+import static org.apache.james.PeriodicalHealthChecksConfiguration.DEFAULT_CONFIGURATION;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
+
 import org.apache.commons.configuration2.PropertiesConfiguration;
-import org.apache.commons.configuration2.ex.ConversionException;
 import org.junit.jupiter.api.Test;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
 
 public class PeriodicalHealthChecksConfigurationTest {
 
+    private static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
+    private static final String PERIOD = "PT5s";
     private static final String EMPTY_STRING = "";
     private static final String RANDOM_STRING = "abcdsfsfs";
-    private static final long NEGATIVE_NUMBER = -1;
-    private static final long INITIAL_DELAY = 10;
-    private static final long PERIOD = 5;
 
     @Test
     void shouldMatchBeanContract() {
@@ -45,120 +45,90 @@ public class PeriodicalHealthChecksConfigurationTest {
     }
 
     @Test
-    void fromShouldThrowWhenInitialDelayIsEmpty() {
-        PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, EMPTY_STRING);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD);
-
-        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration. from(configuration))
-            .isInstanceOf(ConversionException.class);
-    }
-
-    @Test
-    void fromShouldThrowWhenPeriodIsEmpty() {
-        PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, DEFAULT_HEALTH_CHECK_INITIAL_DELAY);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, EMPTY_STRING);
-
-        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(ConversionException.class);
+    void builderShouldThrowWhenPeriodIsNull() {
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
+            .period(null)
+            .build())
+            .isInstanceOf(NullPointerException.class);
     }
 
     @Test
-    void fromShouldReturnConfigurationWithDefaultValueWhenInitialDelayIsMissing() {
-        PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
-
-        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
-            .initialDelay(DEFAULT_HEALTH_CHECK_INITIAL_DELAY)
-            .period(PERIOD)
-            .build());
+    void builderShouldThrowWhenPeriodHasIncorrectFormat() {
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
+            .period(Duration.parse(RANDOM_STRING))
+            .build())
+            .isInstanceOf(DateTimeParseException.class);
     }
 
     @Test
-    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsMissing() {
-        PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
-
-        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
-            .initialDelay(INITIAL_DELAY)
-            .period(DEFAULT_HEALTH_CHECK_PERIOD)
-            .build());
+    void builderShouldThrowWhenPeriodIsNegative() {
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
+            .period(Duration.parse("-" + PERIOD))
+            .build())
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
-    void fromShouldReturnConfigurationWithDefaultValueWhenInitialDelayIsNull() {
-        PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, null);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
-
-        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
-            .initialDelay(DEFAULT_HEALTH_CHECK_INITIAL_DELAY)
-            .period(PERIOD)
-            .build());
+    void builderShouldThrowWhenPeriodIsZero() {
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
+            .period(Duration.ZERO)
+            .build())
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
-    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsNull() {
-        PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, null);
+    void builderShouldReturnCorrectConfiguration() {
+        PeriodicalHealthChecksConfiguration configuration = PeriodicalHealthChecksConfiguration.builder()
+            .period(Duration.parse(PERIOD))
+            .build();
 
-        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
-            .initialDelay(INITIAL_DELAY)
-            .period(DEFAULT_HEALTH_CHECK_PERIOD)
-            .build());
+        assertThat(configuration.getPeriod()).isEqualTo(Duration.parse(PERIOD));
     }
 
     @Test
-    void fromShouldThrowWhenInitialDelayIsNotANumber() {
+    void fromShouldThrowWhenPeriodIsEmpty() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, RANDOM_STRING);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+        configuration.addProperty(HEALTH_CHECK_PERIOD, EMPTY_STRING);
 
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(ConversionException.class);
+            .isInstanceOf(DateTimeParseException.class);
     }
 
     @Test
-    void fromShouldThrowWhenInitialDelayIsNegative() {
+    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsMissing() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, NEGATIVE_NUMBER);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
 
-        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(IllegalArgumentException.class);
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .period(DEFAULT_CONFIGURATION.getPeriod())
+            .build());
     }
 
     @Test
-    void fromShouldThrowWhenPeriodIsNegative() {
+    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsNull() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, NEGATIVE_NUMBER);
+        configuration.addProperty(HEALTH_CHECK_PERIOD, null);
 
-        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(IllegalArgumentException.class);
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .period(DEFAULT_CONFIGURATION.getPeriod())
+            .build());
     }
 
     @Test
-    void fromShouldThrowWhenPeriodIsNotANumber() {
+    void fromShouldThrowWhenPeriodHasIncorrectFormat() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, RANDOM_STRING);
+        configuration.addProperty(HEALTH_CHECK_PERIOD, RANDOM_STRING);
 
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(ConversionException.class);
+            .isInstanceOf(DateTimeParseException.class);
     }
 
     @Test
     void fromShouldReturnProvidedConfiguration() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
-        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+        configuration.addProperty(HEALTH_CHECK_PERIOD, PERIOD);
 
         assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
-            .initialDelay(INITIAL_DELAY)
-            .period(PERIOD)
+            .period(Duration.parse(PERIOD))
             .build());
     }
-}
+}
\ No newline at end of file
diff --git a/src/site/xdoc/server/config-healthcheck.xml b/src/site/xdoc/server/config-healthcheck.xml
index e7604bd..c53635a 100644
--- a/src/site/xdoc/server/config-healthcheck.xml
+++ b/src/site/xdoc/server/config-healthcheck.xml
@@ -24,7 +24,6 @@
     </properties>
 
     <body>
-
         <section name="PeriodicalHealthChecks Configuration">
 
             <p>Consult <a href="https://github.com/apache/james-project/blob/master/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties">healthcheck.properties</a> in GIT to get some examples and hints.</p>
@@ -34,8 +33,6 @@
             </p>
 
             <dl>
-                <dt><strong>healthcheck.initial.delay</strong></dt>
-                <dd>Define the delay time before first health check starts (default: 60)</dd>
                 <dt><strong>healthcheck.period</strong></dt>
                 <dd>Define the period between two periodical health checks (default: 60)</dd>
             </dl>


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


[james-project] 26/39: JAMES-3134 Update RabbitMQ related documentation

Posted by bt...@apache.org.
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 f16dff57929761183fdfe6fac7f851b4d349d03c
Author: Gautier DI FOLCO <gd...@linagora.com>
AuthorDate: Mon Apr 6 17:53:56 2020 +0200

    JAMES-3134 Update RabbitMQ related documentation
---
 CHANGELOG.md            |  2 +-
 upgrade-instructions.md | 10 +++++++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0996a0d..32b6ff1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -71,7 +71,7 @@ More details about the property is at [java mail doc](https://javaee.github.io/j
  - JAMES-3122 LogEnabled API in Spring product had been removed for Log4J2 adoption for Java 9+ runtime compatibility. 
  
 ### Third party softwares
- - The distributed James server product (relying on Guice, Cassandra, ElasticSearch, RabbitMQ and optionally Swift) now needs at least RabbitMQ 3.8.
+ - The distributed James server product (relying on Guice, Cassandra, ElasticSearch, RabbitMQ and optionally Swift) now needs at least RabbitMQ 3.8.1.
  - Tika prior 1.24 is subject to multiple CVEs. We recommend the upgrade.
 
 ## [3.4.0] - 2019-09-05
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index 4f7c11d..2065028 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -270,7 +270,15 @@ The distributed James project (relying on Guice, Cassandra, ElasticSearch, Rabbi
 
 In order to enforce task sequential processing at the cluster level, we rely on a single active consumer, which is a feature introduced in RabbitMQ 3.8.
 
-Users of distributed James product thus need to upgrade their RabbitMQ server to be at least version 3.8.
+Users of distributed James product thus need to upgrade their RabbitMQ server to be at least version 3.8.1.
+
+#### Upgrade procedure
+
+Ensure [no task is running or scheduled](http://james.apache.org/server/manage-webadmin.html#Listing_tasks).
+
+ - Stop James nodes
+ - drop `taskManagerWorkQueue` queue in RabbitMQ
+ - Start new James nodes
 
 #### Enforce usernames to be lower cased
 


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


[james-project] 10/39: JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test

Posted by bt...@apache.org.
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 3ba85a34cb3830a3dfc31c49b56b77eda8b5deaf
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Thu Mar 26 20:48:03 2020 +0700

    JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test
---
 .../james/PeriodicalHealthChecksConfiguration.java   |  9 +++++----
 .../PeriodicalHealthChecksConfigurationTest.java     | 20 ++++++++++----------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
index aa8bbfb..444ef4e 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
@@ -23,6 +23,7 @@ import java.time.Duration;
 import java.util.Objects;
 
 import org.apache.commons.configuration2.Configuration;
+import org.apache.james.util.DurationParser;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
@@ -30,9 +31,9 @@ import com.google.common.base.Preconditions;
 public class PeriodicalHealthChecksConfiguration {
 
     private static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
-    private static final String DEFAULT_HEALTH_CHECK_PERIOD = "PT60s";
+    private static final String DEFAULT_HEALTH_CHECK_PERIOD = "60s";
     public static final PeriodicalHealthChecksConfiguration DEFAULT_CONFIGURATION = builder()
-        .period(Duration.parse(DEFAULT_HEALTH_CHECK_PERIOD))
+        .period(DurationParser.parse(DEFAULT_HEALTH_CHECK_PERIOD))
         .build();
 
     public interface Builder {
@@ -59,12 +60,12 @@ public class PeriodicalHealthChecksConfiguration {
     }
 
     public static Builder.RequiredPeriod builder() {
-        return period -> new Builder.ReadyToBuild(period);
+        return Builder.ReadyToBuild::new;
     }
 
     public static PeriodicalHealthChecksConfiguration from(Configuration configuration) {
         return builder()
-            .period(Duration.parse(configuration.getString(HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD)))
+            .period(DurationParser.parse(configuration.getString(HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD)))
             .build();
     }
 
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
index d7830cf..10fdcf5 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
@@ -24,9 +24,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.time.Duration;
-import java.time.format.DateTimeParseException;
 
 import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.apache.james.util.DurationParser;
 import org.junit.jupiter.api.Test;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
@@ -34,7 +34,7 @@ import nl.jqno.equalsverifier.EqualsVerifier;
 public class PeriodicalHealthChecksConfigurationTest {
 
     private static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
-    private static final String PERIOD = "PT5s";
+    private static final String PERIOD = "5s";
     private static final String EMPTY_STRING = "";
     private static final String RANDOM_STRING = "abcdsfsfs";
 
@@ -55,15 +55,15 @@ public class PeriodicalHealthChecksConfigurationTest {
     @Test
     void builderShouldThrowWhenPeriodHasIncorrectFormat() {
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
-            .period(Duration.parse(RANDOM_STRING))
+            .period(DurationParser.parse(RANDOM_STRING))
             .build())
-            .isInstanceOf(DateTimeParseException.class);
+            .isInstanceOf(NumberFormatException.class);
     }
 
     @Test
     void builderShouldThrowWhenPeriodIsNegative() {
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
-            .period(Duration.parse("-" + PERIOD))
+            .period(DurationParser.parse("-" + PERIOD))
             .build())
             .isInstanceOf(IllegalArgumentException.class);
     }
@@ -79,10 +79,10 @@ public class PeriodicalHealthChecksConfigurationTest {
     @Test
     void builderShouldReturnCorrectConfiguration() {
         PeriodicalHealthChecksConfiguration configuration = PeriodicalHealthChecksConfiguration.builder()
-            .period(Duration.parse(PERIOD))
+            .period(DurationParser.parse(PERIOD))
             .build();
 
-        assertThat(configuration.getPeriod()).isEqualTo(Duration.parse(PERIOD));
+        assertThat(configuration.getPeriod()).isEqualTo(DurationParser.parse(PERIOD));
     }
 
     @Test
@@ -91,7 +91,7 @@ public class PeriodicalHealthChecksConfigurationTest {
         configuration.addProperty(HEALTH_CHECK_PERIOD, EMPTY_STRING);
 
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(DateTimeParseException.class);
+            .isInstanceOf(NumberFormatException.class);
     }
 
     @Test
@@ -119,7 +119,7 @@ public class PeriodicalHealthChecksConfigurationTest {
         configuration.addProperty(HEALTH_CHECK_PERIOD, RANDOM_STRING);
 
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(DateTimeParseException.class);
+            .isInstanceOf(NumberFormatException.class);
     }
 
     @Test
@@ -128,7 +128,7 @@ public class PeriodicalHealthChecksConfigurationTest {
         configuration.addProperty(HEALTH_CHECK_PERIOD, PERIOD);
 
         assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
-            .period(Duration.parse(PERIOD))
+            .period(DurationParser.parse(PERIOD))
             .build());
     }
 }
\ No newline at end of file


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


[james-project] 37/39: [REFACTORING] remove duplicate reactor scala extension

Posted by bt...@apache.org.
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 f9b3122492b4ab69c13f6c5d937400d7d1b5a1c6
Author: Rémi Kowalski <rk...@linagora.com>
AuthorDate: Wed Apr 15 16:42:03 2020 +0200

    [REFACTORING] remove duplicate reactor scala extension
---
 pom.xml | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/pom.xml b/pom.xml
index d39bd79..15cb0d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2194,11 +2194,6 @@
                 <version>${feign-form.version}</version>
             </dependency>
             <dependency>
-                <groupId>io.projectreactor</groupId>
-                <artifactId>reactor-scala-extensions_${scala.base}</artifactId>
-                <version>0.5.0</version>
-            </dependency>
-            <dependency>
                 <groupId>io.netty</groupId>
                 <artifactId>netty</artifactId>
                 <version>${netty.version}</version>


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


[james-project] 11/39: JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test

Posted by bt...@apache.org.
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 fbd6fc64147d345ff570676be0d9e2bf83b8f20e
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Wed Apr 1 02:18:22 2020 +0700

    JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test
---
 .../james/PeriodicalHealthChecksConfiguration.java | 20 +++++++++++----
 .../PeriodicalHealthChecksConfigurationTest.java   | 29 ++++++++--------------
 2 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
index 444ef4e..beac23b 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
@@ -23,6 +23,7 @@ import java.time.Duration;
 import java.util.Objects;
 
 import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.james.util.DurationParser;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -31,9 +32,10 @@ import com.google.common.base.Preconditions;
 public class PeriodicalHealthChecksConfiguration {
 
     private static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
-    private static final String DEFAULT_HEALTH_CHECK_PERIOD = "60s";
+    private static final Duration DEFAULT_HEALTH_CHECK_PERIOD = Duration.ofSeconds(60);
+    private static final Duration MINIMAL_HEALTH_CHECK_PERIOD = Duration.ofSeconds(10);
     public static final PeriodicalHealthChecksConfiguration DEFAULT_CONFIGURATION = builder()
-        .period(DurationParser.parse(DEFAULT_HEALTH_CHECK_PERIOD))
+        .period(DEFAULT_HEALTH_CHECK_PERIOD)
         .build();
 
     public interface Builder {
@@ -51,8 +53,8 @@ public class PeriodicalHealthChecksConfiguration {
             }
 
             PeriodicalHealthChecksConfiguration build() {
-                Preconditions.checkArgument(!period.isNegative(), "'period' must be positive");
-                Preconditions.checkArgument(!period.isZero(), "'period' must be greater than zero");
+                Preconditions.checkArgument(period.compareTo(MINIMAL_HEALTH_CHECK_PERIOD) >= 0,
+                    "'period' must be equal or greater than " + MINIMAL_HEALTH_CHECK_PERIOD.toMillis() + "ms");
 
                 return new PeriodicalHealthChecksConfiguration(period);
             }
@@ -65,7 +67,7 @@ public class PeriodicalHealthChecksConfiguration {
 
     public static PeriodicalHealthChecksConfiguration from(Configuration configuration) {
         return builder()
-            .period(DurationParser.parse(configuration.getString(HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD)))
+            .period(getDurationFromConfiguration(configuration))
             .build();
     }
 
@@ -94,4 +96,12 @@ public class PeriodicalHealthChecksConfiguration {
     public final int hashCode() {
         return Objects.hash(period);
     }
+
+    private static Duration getDurationFromConfiguration(Configuration configuration) {
+        if (StringUtils.isEmpty(configuration.getString(HEALTH_CHECK_PERIOD))) {
+           return DEFAULT_HEALTH_CHECK_PERIOD;
+        }
+
+        return DurationParser.parse(configuration.getString(HEALTH_CHECK_PERIOD));
+    }
 }
\ No newline at end of file
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
index 10fdcf5..6fc8cb7 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
@@ -34,7 +34,7 @@ import nl.jqno.equalsverifier.EqualsVerifier;
 public class PeriodicalHealthChecksConfigurationTest {
 
     private static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
-    private static final String PERIOD = "5s";
+    private static final String PERIOD = "10s";
     private static final String EMPTY_STRING = "";
     private static final String RANDOM_STRING = "abcdsfsfs";
 
@@ -61,17 +61,9 @@ public class PeriodicalHealthChecksConfigurationTest {
     }
 
     @Test
-    void builderShouldThrowWhenPeriodIsNegative() {
+    void builderShouldThrowWhenPeriodIsLessThanMinimalValue() {
         assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
-            .period(DurationParser.parse("-" + PERIOD))
-            .build())
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    void builderShouldThrowWhenPeriodIsZero() {
-        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.builder()
-            .period(Duration.ZERO)
+            .period(Duration.ofSeconds(1))
             .build())
             .isInstanceOf(IllegalArgumentException.class);
     }
@@ -86,17 +78,18 @@ public class PeriodicalHealthChecksConfigurationTest {
     }
 
     @Test
-    void fromShouldThrowWhenPeriodIsEmpty() {
+    void fromShouldReturnDefaultConfigurationWhenPeriodIsMissing() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(HEALTH_CHECK_PERIOD, EMPTY_STRING);
 
-        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
-            .isInstanceOf(NumberFormatException.class);
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .period(DEFAULT_CONFIGURATION.getPeriod())
+            .build());
     }
 
     @Test
-    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsMissing() {
+    void fromShouldReturnDefaultConfigurationWhenPeriodIsNull() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(HEALTH_CHECK_PERIOD, null);
 
         assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
             .period(DEFAULT_CONFIGURATION.getPeriod())
@@ -104,9 +97,9 @@ public class PeriodicalHealthChecksConfigurationTest {
     }
 
     @Test
-    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsNull() {
+    void fromShouldReturnDefaultConfigurationWhenPeriodIsEmpty() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty(HEALTH_CHECK_PERIOD, null);
+        configuration.addProperty(HEALTH_CHECK_PERIOD, EMPTY_STRING);
 
         assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
             .period(DEFAULT_CONFIGURATION.getPeriod())


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


[james-project] 28/39: JAMES-3139 RabbitMQEventBus error dispatching handling

Posted by bt...@apache.org.
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 ee069651973f2af439c4e5de64533975113d56ef
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Wed Apr 8 17:32:20 2020 +0700

    JAMES-3139 RabbitMQEventBus error dispatching handling
---
 .../james/mailbox/events/EventDispatcher.java      | 60 ++++++++++++++++---
 .../james/mailbox/events/RabbitMQEventBus.java     |  4 +-
 .../james/mailbox/events/RabbitMQEventBusTest.java | 70 +++++++++++++++-------
 3 files changed, 101 insertions(+), 33 deletions(-)

diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/EventDispatcher.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/EventDispatcher.java
index 239d716..c56e02b 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/EventDispatcher.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/EventDispatcher.java
@@ -26,8 +26,9 @@ import static org.apache.james.mailbox.events.RabbitMQEventBus.EVENT_BUS_ID;
 import static org.apache.james.mailbox.events.RabbitMQEventBus.MAILBOX_EVENT_EXCHANGE_NAME;
 
 import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
-import java.util.stream.Stream;
 
 import org.apache.james.event.json.EventSerializer;
 import org.apache.james.mailbox.events.RoutingKeyConverter.RoutingKey;
@@ -37,6 +38,7 @@ import org.apache.james.util.StructuredLogger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.github.steveash.guavate.Guavate;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.rabbitmq.client.AMQP;
@@ -50,7 +52,11 @@ import reactor.rabbitmq.OutboundMessage;
 import reactor.rabbitmq.Sender;
 import reactor.util.function.Tuples;
 
-class EventDispatcher {
+public class EventDispatcher {
+    public static class DispatchingFailureGroup extends Group {
+        public static DispatchingFailureGroup INSTANCE = new DispatchingFailureGroup();
+    }
+
     private static final Logger LOGGER = LoggerFactory.getLogger(EventDispatcher.class);
 
     private final EventSerializer eventSerializer;
@@ -58,8 +64,12 @@ class EventDispatcher {
     private final LocalListenerRegistry localListenerRegistry;
     private final AMQP.BasicProperties basicProperties;
     private final MailboxListenerExecutor mailboxListenerExecutor;
+    private final EventDeadLetters deadLetters;
 
-    EventDispatcher(EventBusId eventBusId, EventSerializer eventSerializer, Sender sender, LocalListenerRegistry localListenerRegistry, MailboxListenerExecutor mailboxListenerExecutor) {
+    EventDispatcher(EventBusId eventBusId, EventSerializer eventSerializer, Sender sender,
+                    LocalListenerRegistry localListenerRegistry,
+                    MailboxListenerExecutor mailboxListenerExecutor,
+                    EventDeadLetters deadLetters) {
         this.eventSerializer = eventSerializer;
         this.sender = sender;
         this.localListenerRegistry = localListenerRegistry;
@@ -70,6 +80,7 @@ class EventDispatcher {
             .contentType(PERSISTENT_TEXT_PLAIN.getContentType())
             .build();
         this.mailboxListenerExecutor = mailboxListenerExecutor;
+        this.deadLetters = deadLetters;
     }
 
     void start() {
@@ -83,7 +94,7 @@ class EventDispatcher {
         return Flux
             .concat(
                 dispatchToLocalListeners(event, keys),
-                dispatchToRemoteListeners(serializeEvent(event), keys))
+                dispatchToRemoteListeners(event, keys))
             .subscribeOn(Schedulers.elastic())
             .doOnError(throwable -> LOGGER.error("error while dispatching event", throwable))
             .then()
@@ -123,13 +134,44 @@ class EventDispatcher {
             .addField(EventBus.StructuredLoggingFields.REGISTRATION_KEYS, keys);
     }
 
-    private Mono<Void> dispatchToRemoteListeners(byte[] serializedEvent, Set<RegistrationKey> keys) {
-        Stream<RoutingKey> routingKeys = Stream.concat(Stream.of(RoutingKey.empty()), keys.stream().map(RoutingKey::of));
+    private Mono<Void> dispatchToRemoteListeners(Event event, Set<RegistrationKey> keys) {
+        return Mono.fromCallable(() -> serializeEvent(event))
+            .flatMap(serializedEvent -> Mono.zipDelayError(
+                remoteGroupsDispatch(serializedEvent, event),
+                remoteKeysDispatch(serializedEvent, keys)))
+            .then();
+    }
 
-        Stream<OutboundMessage> outboundMessages = routingKeys
-            .map(routingKey -> new OutboundMessage(MAILBOX_EVENT_EXCHANGE_NAME, routingKey.asString(), basicProperties, serializedEvent));
+    private Mono<Void> remoteGroupsDispatch(byte[] serializedEvent, Event event) {
+        return remoteDispatch(serializedEvent, Collections.singletonList(RoutingKey.empty()))
+            .doOnError(ex -> LOGGER.error(
+                "cannot dispatch event of type '{}' belonging '{}' with id '{}' to remote groups, store it into dead letter",
+                event.getClass().getSimpleName(),
+                event.getUsername().asString(),
+                event.getEventId().getId(),
+                ex))
+            .onErrorResume(ex -> deadLetters.store(DispatchingFailureGroup.INSTANCE, event)
+                .then(Mono.error(ex)));
+    }
+
+    private Mono<Void> remoteKeysDispatch(byte[] serializedEvent, Set<RegistrationKey> keys) {
+        return remoteDispatch(serializedEvent,
+            keys.stream()
+                .map(RoutingKey::of)
+                .collect(Guavate.toImmutableList()));
+    }
+
+    private Mono<Void> remoteDispatch(byte[] serializedEvent, Collection<RoutingKey> routingKeys) {
+        if (routingKeys.isEmpty()) {
+            return Mono.empty();
+        }
+        return sender.send(toMessages(serializedEvent, routingKeys))
+            .subscribeOn(Schedulers.elastic());
+    }
 
-        return sender.send(Flux.fromStream(outboundMessages));
+    private Flux<OutboundMessage> toMessages(byte[] serializedEvent, Collection<RoutingKey> routingKeys) {
+        return Flux.fromIterable(routingKeys)
+                .map(routingKey -> new OutboundMessage(MAILBOX_EVENT_EXCHANGE_NAME, routingKey.asString(), basicProperties, serializedEvent));
     }
 
     private byte[] serializeEvent(Event event) {
diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
index f823ff1..311e789 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
@@ -79,7 +79,7 @@ public class RabbitMQEventBus implements EventBus, Startable {
             LocalListenerRegistry localListenerRegistry = new LocalListenerRegistry();
             keyRegistrationHandler = new KeyRegistrationHandler(eventBusId, eventSerializer, sender, receiverProvider, routingKeyConverter, localListenerRegistry, mailboxListenerExecutor, retryBackoff);
             groupRegistrationHandler = new GroupRegistrationHandler(eventSerializer, sender, receiverProvider, retryBackoff, eventDeadLetters, mailboxListenerExecutor);
-            eventDispatcher = new EventDispatcher(eventBusId, eventSerializer, sender, localListenerRegistry, mailboxListenerExecutor);
+            eventDispatcher = new EventDispatcher(eventBusId, eventSerializer, sender, localListenerRegistry, mailboxListenerExecutor, eventDeadLetters);
 
             eventDispatcher.start();
             keyRegistrationHandler.start();
@@ -94,7 +94,7 @@ public class RabbitMQEventBus implements EventBus, Startable {
             LocalListenerRegistry localListenerRegistry = new LocalListenerRegistry();
             keyRegistrationHandler = new KeyRegistrationHandler(eventBusId, eventSerializer, sender, receiverProvider, routingKeyConverter, localListenerRegistry, mailboxListenerExecutor, retryBackoff);
             groupRegistrationHandler = new GroupRegistrationHandler(eventSerializer, sender, receiverProvider, retryBackoff, eventDeadLetters, mailboxListenerExecutor);
-            eventDispatcher = new EventDispatcher(eventBusId, eventSerializer, sender, localListenerRegistry, mailboxListenerExecutor);
+            eventDispatcher = new EventDispatcher(eventBusId, eventSerializer, sender, localListenerRegistry, mailboxListenerExecutor, eventDeadLetters);
 
             keyRegistrationHandler.declareQueue();
 
diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
index f462fba..abbfdf7 100644
--- a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
@@ -28,7 +28,9 @@ import static org.apache.james.backends.rabbitmq.Constants.NO_ARGUMENTS;
 import static org.apache.james.mailbox.events.EventBusConcurrentTestContract.newCountingListener;
 import static org.apache.james.mailbox.events.EventBusTestFixture.ALL_GROUPS;
 import static org.apache.james.mailbox.events.EventBusTestFixture.EVENT;
+import static org.apache.james.mailbox.events.EventBusTestFixture.EVENT_2;
 import static org.apache.james.mailbox.events.EventBusTestFixture.GROUP_A;
+import static org.apache.james.mailbox.events.EventBusTestFixture.GROUP_B;
 import static org.apache.james.mailbox.events.EventBusTestFixture.KEY_1;
 import static org.apache.james.mailbox.events.EventBusTestFixture.NO_KEYS;
 import static org.apache.james.mailbox.events.EventBusTestFixture.newAsyncListener;
@@ -51,6 +53,7 @@ import java.io.Closeable;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
 
 import org.apache.james.backends.rabbitmq.RabbitMQExtension;
 import org.apache.james.backends.rabbitmq.RabbitMQExtension.DockerRestartPolicy;
@@ -60,6 +63,7 @@ import org.apache.james.backends.rabbitmq.ReceiverProvider;
 import org.apache.james.event.json.EventSerializer;
 import org.apache.james.mailbox.events.EventBusTestFixture.GroupA;
 import org.apache.james.mailbox.events.EventBusTestFixture.MailboxListenerCountingSuccessfulExecution;
+import org.apache.james.mailbox.events.EventDispatcher.DispatchingFailureGroup;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.model.TestMessageId;
 import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
@@ -732,51 +736,73 @@ class RabbitMQEventBusTest implements GroupContract.SingleEventBusGroupContract,
 
             rabbitMQExtension.getRabbitMQ().pause();
 
-            try {
-                eventBus().dispatch(EVENT, NO_KEYS).block();
-            } catch (Exception e) {
-                // ignore
-            }
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
 
-            getSpeedProfile().longWaitCondition()
-                .untilAsserted(() -> assertThat(eventCollector.getEvents()).isEmpty());
+            assertThat(eventCollector.getEvents()).isEmpty();
         }
 
         @Test
-        void dispatchShouldNotPersistEventWhenDispatchingNoKeyGetError() {
+        void dispatchShouldPersistEventWhenDispatchingNoKeyGetError() {
             EventCollector eventCollector = eventCollector();
             eventBus().register(eventCollector, GROUP_A);
 
             rabbitMQExtension.getRabbitMQ().pause();
 
-            try {
-                eventBus().dispatch(EVENT, NO_KEYS).block();
-            } catch (Exception e) {
-                // ignore
-            }
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
 
-            getSpeedProfile().longWaitCondition()
-                .untilAsserted(() ->
-                    assertThat(deadLetter().containEvents().block()).isFalse());
+            assertThat(dispatchingFailureEvents()).containsOnly(EVENT);
         }
 
         @Test
-        void dispatchShouldNotPersistEventWhenDispatchingWithKeysGetError() {
+        void dispatchShouldPersistEventWhenDispatchingWithKeysGetError() {
             EventCollector eventCollector = eventCollector();
             eventBus().register(eventCollector, GROUP_A);
             eventBus().register(eventCollector, KEY_1);
 
             rabbitMQExtension.getRabbitMQ().pause();
 
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+
+            assertThat(dispatchingFailureEvents()).containsOnly(EVENT);
+        }
+
+        @Test
+        void dispatchShouldPersistOnlyOneEventWhenDispatchingMultiGroupsGetError() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+            eventBus().register(eventCollector, GROUP_B);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+
+            assertThat(dispatchingFailureEvents()).containsOnly(EVENT);
+        }
+
+        @Test
+        void dispatchShouldPersistEventsWhenDispatchingGroupsGetErrorMultipleTimes() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+            doQuietly(() -> eventBus().dispatch(EVENT_2, NO_KEYS).block());
+
+            assertThat(dispatchingFailureEvents()).containsExactly(EVENT, EVENT_2);
+        }
+
+        private Stream<Event> dispatchingFailureEvents() {
+            return deadLetter().failedIds(DispatchingFailureGroup.INSTANCE)
+                .flatMap(insertionId -> deadLetter().failedEvent(DispatchingFailureGroup.INSTANCE, insertionId))
+                .toStream();
+        }
+
+        private void doQuietly(Runnable runnable) {
             try {
-                eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+                runnable.run();
             } catch (Exception e) {
                 // ignore
             }
-
-            getSpeedProfile().longWaitCondition()
-                .untilAsserted(() ->
-                    assertThat(deadLetter().containEvents().block()).isFalse());
         }
     }
 


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


[james-project] 17/39: JAMES-3117 Add PeriodicalHealthChecks/Test

Posted by bt...@apache.org.
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 75a2b670c52c697f56414cbd996ca1d4b8853f26
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Mar 30 09:06:46 2020 +0700

    JAMES-3117 Add PeriodicalHealthChecks/Test
---
 .../org/apache/james/PeriodicalHealthChecks.java   | 30 +++++++++++++---------
 .../apache/james/PeriodicalHealthChecksTest.java   | 25 ++++++++++++++----
 2 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
index 2d80593..11a82d6 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -19,7 +19,6 @@
 
 package org.apache.james;
 
-import java.time.Duration;
 import java.util.Set;
 
 import javax.annotation.PreDestroy;
@@ -42,28 +41,38 @@ public class PeriodicalHealthChecks implements Startable {
     private static final Logger LOGGER = LoggerFactory.getLogger(PeriodicalHealthChecks.class);
     private final Set<HealthCheck> healthChecks;
     private final Scheduler scheduler;
-    private final Duration period;
+    private final PeriodicalHealthChecksConfiguration configuration;
     private Disposable disposable;
 
     @Inject
-    PeriodicalHealthChecks(Set<HealthCheck> healthChecks, Scheduler scheduler, PeriodicalHealthChecksConfiguration config) {
+    PeriodicalHealthChecks(Set<HealthCheck> healthChecks, PeriodicalHealthChecksConfiguration configuration) {
+        this.healthChecks = healthChecks;
+        this.scheduler = Schedulers.elastic();
+        this.configuration = configuration;
+    }
+
+    PeriodicalHealthChecks(Set<HealthCheck> healthChecks, Scheduler scheduler, PeriodicalHealthChecksConfiguration configuration) {
         this.healthChecks = healthChecks;
         this.scheduler = scheduler;
-        this.period = config.getPeriod();
+        this.configuration = configuration;
     }
 
     public void start() {
-        disposable = Flux.interval(period, scheduler)
+        disposable = Flux.interval(configuration.getPeriod(), scheduler)
             .flatMap(any -> Flux.fromIterable(healthChecks)
                 .flatMap(healthCheck ->
                     Mono.fromCallable(healthCheck::check)))
-                .flatMap(result ->
-                    Mono.fromRunnable(() -> logResult(result)))
+            .doOnNext(this::logResult)
             .onErrorContinue(this::logError)
             .subscribeOn(Schedulers.elastic())
             .subscribe();
     }
 
+    @PreDestroy
+    public void stop() {
+        disposable.dispose();
+    }
+
     private void logResult(Result result) {
         switch (result.getStatus()) {
             case HEALTHY:
@@ -86,11 +95,8 @@ public class PeriodicalHealthChecks implements Startable {
         if (triggeringValue instanceof Result) {
             Result result = (Result) triggeringValue;
             LOGGER.error("HealthCheck error for: {}, Cause: {}", result.getComponentName(), error);
+            return;
         }
-    }
-
-    @PreDestroy
-    public void stop() {
-        disposable.dispose();
+        LOGGER.error("HealthCheck error. Triggering value: {}, Cause: {}", triggeringValue, error);
     }
 }
\ No newline at end of file
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
index dfc241f..315540b 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
@@ -59,8 +59,7 @@ public class PeriodicalHealthChecksTest {
         scheduler = VirtualTimeScheduler.getOrSet();
         testee = new PeriodicalHealthChecks(ImmutableSet.of(mockHealthCheck1, mockHealthCheck2),
             scheduler,
-            new PeriodicalHealthChecksConfiguration(Duration.ofMillis(PERIOD)));
-        testee.start();
+            new PeriodicalHealthChecksConfiguration(Duration.ofSeconds(PERIOD)));
     }
 
     @AfterEach
@@ -70,20 +69,36 @@ public class PeriodicalHealthChecksTest {
     
     @Test
     void startShouldCallHealthCheckAtLeastOnce() {
-        scheduler.advanceTimeBy(Duration.ofMillis(PERIOD));
+        testee.start();
+
+        scheduler.advanceTimeBy(Duration.ofSeconds(PERIOD));
         verify(mockHealthCheck1, atLeast(1)).check();
     }
 
     @Test
     void startShouldCallHealthCheckMultipleTimes() {
-        scheduler.advanceTimeBy(Duration.ofMillis(PERIOD * EXPECTED_INVOKED_TIME));
+        testee.start();
+
+        scheduler.advanceTimeBy(Duration.ofSeconds(PERIOD * EXPECTED_INVOKED_TIME));
         verify(mockHealthCheck1, times(EXPECTED_INVOKED_TIME)).check();
     }
 
     @Test
     void startShouldCallAllHealthChecks() {
-        scheduler.advanceTimeBy(Duration.ofMillis(PERIOD * EXPECTED_INVOKED_TIME));
+        testee.start();
+
+        scheduler.advanceTimeBy(Duration.ofSeconds(PERIOD * EXPECTED_INVOKED_TIME));
         verify(mockHealthCheck1, times(EXPECTED_INVOKED_TIME)).check();
         verify(mockHealthCheck2, times(EXPECTED_INVOKED_TIME)).check();
     }
+
+    @Test
+    void startShouldCallRemainingHealthChecksWhenAHealthCheckThrows() {
+        when(mockHealthCheck1.check()).thenThrow(new RuntimeException());
+
+        testee.start();
+
+        scheduler.advanceTimeBy(Duration.ofSeconds(PERIOD * EXPECTED_INVOKED_TIME));
+        verify(mockHealthCheck2, times(EXPECTED_INVOKED_TIME)).check();
+    }
 }
\ No newline at end of file


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


[james-project] 36/39: JAMES-3149 Reactive getMailboxes

Posted by bt...@apache.org.
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 cb1179e5d9e4ea7f3810bbc7266406b7de51389a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Apr 14 10:59:30 2020 +0700

    JAMES-3149 Reactive getMailboxes
---
 .../org/apache/james/mailbox/MailboxManager.java   |   4 +
 .../james/mailbox/store/StoreMailboxManager.java   |  22 ++++-
 .../jmap/draft/methods/GetMailboxesMethod.java     | 109 +++++++++++----------
 .../jmap/draft/methods/GetMailboxesMethodTest.java |   4 +
 4 files changed, 84 insertions(+), 55 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
index 9f31163..d5ac9d7 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
@@ -36,6 +36,8 @@ import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.reactivestreams.Publisher;
 
+import reactor.core.publisher.Flux;
+
 /**
  * <p>
  * Central MailboxManager which creates, lists, provides, renames and deletes
@@ -243,6 +245,8 @@ public interface MailboxManager extends RequestAware, RightManager, MailboxAnnot
      */
     List<MailboxMetaData> search(MailboxQuery expression, MailboxSession session) throws MailboxException;
 
+    Flux<MailboxMetaData> searchReactive(MailboxQuery expression, MailboxSession session);
+
     /**
      * Searches for messages matching the given query.
      * 
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index e9ad7da..f32d83e 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -596,6 +596,15 @@ public class StoreMailboxManager implements MailboxManager {
             .block();
     }
 
+    @Override
+    public Flux<MailboxMetaData> searchReactive(MailboxQuery expression, MailboxSession session) {
+        try {
+            return searchMailboxesMetadata(expression, session, Right.Lookup);
+        } catch (MailboxException e) {
+            return Flux.error(e);
+        }
+    }
+
     private Flux<MailboxMetaData> searchMailboxesMetadata(MailboxQuery mailboxQuery, MailboxSession session, Right right) throws MailboxException {
         Mono<List<Mailbox>> mailboxesMono = searchMailboxes(mailboxQuery, session, right).collectList();
         MessageMapper messageMapper = mailboxSessionMapperFactory.getMessageMapper(session);
@@ -603,13 +612,24 @@ public class StoreMailboxManager implements MailboxManager {
         return mailboxesMono
             .flatMapMany(mailboxes -> Flux.fromIterable(mailboxes)
                 .filter(mailboxQuery::matches)
-                .flatMap(mailbox -> messageMapper.getMailboxCountersReactive(mailbox)
+                .flatMap(mailbox -> retrieveCounters(messageMapper, mailbox, session)
                     .map(Throwing.<MailboxCounters, MailboxMetaData>function(
                         counters -> toMailboxMetadata(session, mailboxes, mailbox, counters))
                         .sneakyThrow())))
             .sort(MailboxMetaData.COMPARATOR);
     }
 
+    private Mono<MailboxCounters> retrieveCounters(MessageMapper messageMapper, Mailbox mailbox, MailboxSession session) {
+        return messageMapper.getMailboxCountersReactive(mailbox)
+            .filter(Throwing.<MailboxCounters>predicate(counter -> storeRightManager.hasRight(mailbox, Right.Read, session)).sneakyThrow())
+            .switchIfEmpty(Mono.just(MailboxCounters
+                .builder()
+                .mailboxId(mailbox.getMailboxId())
+                .count(0)
+                .unseen(0)
+                .build()));
+    }
+
     private Flux<Mailbox> searchMailboxes(MailboxQuery mailboxQuery, MailboxSession session, Right right) throws MailboxException {
         MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session);
         Flux<Mailbox> baseMailboxes = mailboxMapper.findMailboxWithPathLike(toSingleUserQuery(mailboxQuery, session));
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
index da0ad86..f74b243 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMailboxesMethod.java
@@ -19,11 +19,12 @@
 
 package org.apache.james.jmap.draft.methods;
 
+import static org.apache.james.util.ReactorUtils.context;
+
 import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
@@ -33,11 +34,9 @@ import org.apache.james.jmap.draft.model.MailboxFactory;
 import org.apache.james.jmap.draft.model.MailboxProperty;
 import org.apache.james.jmap.draft.model.MethodCallId;
 import org.apache.james.jmap.draft.model.mailbox.Mailbox;
-import org.apache.james.jmap.draft.utils.quotas.QuotaLoader;
 import org.apache.james.jmap.draft.utils.quotas.QuotaLoaderWithDefaultPreloaded;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxMetaData;
 import org.apache.james.mailbox.model.search.MailboxQuery;
@@ -45,21 +44,24 @@ import org.apache.james.mailbox.quota.QuotaManager;
 import org.apache.james.mailbox.quota.QuotaRootResolver;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
-import org.apache.james.util.OptionalUtils;
 
 import com.github.fge.lambdas.Throwing;
-import com.github.steveash.guavate.Guavate;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
 public class GetMailboxesMethod implements Method {
 
     private static final Method.Request.Name METHOD_NAME = Method.Request.name("getMailboxes");
     private static final Method.Response.Name RESPONSE_NAME = Method.Response.name("mailboxes");
     private static final Optional<List<MailboxMetaData>> NO_PRELOADED_METADATA = Optional.empty();
+    private static final String ACTION = "GET_MAILBOXES";
 
     private final MailboxManager mailboxManager;
     private final MailboxFactory mailboxFactory;
@@ -88,83 +90,82 @@ public class GetMailboxesMethod implements Method {
     }
 
     @Override
-    public Stream<JmapResponse> processToStream(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
+    public Flux<JmapResponse> process(JmapRequest request, MethodCallId methodCallId, MailboxSession mailboxSession) {
         Preconditions.checkArgument(request instanceof GetMailboxesRequest);
         GetMailboxesRequest mailboxesRequest = (GetMailboxesRequest) request;
 
+        return metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
+            () -> process(methodCallId, mailboxSession, mailboxesRequest)
+            .subscriberContext(context(ACTION, mdc(mailboxesRequest))));
+    }
+
+    private MDCBuilder mdc(GetMailboxesRequest mailboxesRequest) {
         return MDCBuilder.create()
-            .addContext(MDCBuilder.ACTION, "GET_MAILBOXES")
+            .addContext(MDCBuilder.ACTION, ACTION)
             .addContext("accountId", mailboxesRequest.getAccountId())
             .addContext("mailboxIds", mailboxesRequest.getIds())
-            .addContext("properties", mailboxesRequest.getProperties())
-            .wrapArround(
-                () -> metricFactory.runPublishingTimerMetricLogP99(JMAP_PREFIX + METHOD_NAME.getName(),
-                    () -> process(methodCallId, mailboxSession, mailboxesRequest)))
-            .get();
+            .addContext("properties", mailboxesRequest.getProperties());
     }
 
-    private Stream<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, GetMailboxesRequest mailboxesRequest) {
-        return Stream.of(
-            JmapResponse.builder().methodCallId(methodCallId)
-                .response(getMailboxesResponse(mailboxesRequest, mailboxSession))
+    private Flux<JmapResponse> process(MethodCallId methodCallId, MailboxSession mailboxSession, GetMailboxesRequest mailboxesRequest) {
+        return Flux.from(getMailboxesResponse(mailboxesRequest, mailboxSession)
+            .map(response -> JmapResponse.builder().methodCallId(methodCallId)
+                .response(response)
                 .properties(mailboxesRequest.getProperties().map(this::ensureContainsId))
                 .responseName(RESPONSE_NAME)
-                .build());
+                .build()));
     }
 
     private Set<MailboxProperty> ensureContainsId(Set<MailboxProperty> input) {
         return Sets.union(input, ImmutableSet.of(MailboxProperty.ID)).immutableCopy();
     }
 
-    private GetMailboxesResponse getMailboxesResponse(GetMailboxesRequest mailboxesRequest, MailboxSession mailboxSession) {
-        GetMailboxesResponse.Builder builder = GetMailboxesResponse.builder();
-        try {
-            Optional<ImmutableList<MailboxId>> mailboxIds = mailboxesRequest.getIds();
-            List<Mailbox> mailboxes = retrieveMailboxes(mailboxIds, mailboxSession)
-                .sorted(Comparator.comparing(Mailbox::getSortOrder))
-                .collect(Guavate.toImmutableList());
-            return builder.addAll(mailboxes).build();
-        } catch (MailboxException e) {
-            throw new RuntimeException(e);
-        }
+    private Mono<GetMailboxesResponse> getMailboxesResponse(GetMailboxesRequest mailboxesRequest, MailboxSession mailboxSession) {
+        Optional<ImmutableList<MailboxId>> mailboxIds = mailboxesRequest.getIds();
+        return retrieveMailboxes(mailboxIds, mailboxSession)
+            .sort(Comparator.comparing(Mailbox::getSortOrder))
+            .reduce(GetMailboxesResponse.builder(), GetMailboxesResponse.Builder::add)
+            .map(GetMailboxesResponse.Builder::build);
     }
 
-    private Stream<Mailbox> retrieveMailboxes(Optional<ImmutableList<MailboxId>> mailboxIds, MailboxSession mailboxSession) throws MailboxException {
+    private Flux<Mailbox> retrieveMailboxes(Optional<ImmutableList<MailboxId>> mailboxIds, MailboxSession mailboxSession) {
         return mailboxIds
             .map(ids -> retrieveSpecificMailboxes(mailboxSession, ids))
             .orElseGet(Throwing.supplier(() -> retrieveAllMailboxes(mailboxSession)).sneakyThrow());
     }
 
 
-    private Stream<Mailbox> retrieveSpecificMailboxes(MailboxSession mailboxSession, ImmutableList<MailboxId> mailboxIds) {
-        return mailboxIds
-            .stream()
-            .map(mailboxId -> mailboxFactory.builder()
-                .id(mailboxId)
-                .session(mailboxSession)
-                .usingPreloadedMailboxesMetadata(NO_PRELOADED_METADATA)
-                .build()
-            )
-            .flatMap(OptionalUtils::toStream);
+    private Flux<Mailbox> retrieveSpecificMailboxes(MailboxSession mailboxSession, ImmutableList<MailboxId> mailboxIds) {
+        return Flux.fromIterable(mailboxIds)
+            .flatMap(mailboxId -> Mono.fromCallable(() ->
+                mailboxFactory.builder()
+                    .id(mailboxId)
+                    .session(mailboxSession)
+                    .usingPreloadedMailboxesMetadata(NO_PRELOADED_METADATA)
+                    .build())
+                .subscribeOn(Schedulers.elastic()))
+            .handle((element, sink) -> element.ifPresent(sink::next));
     }
 
-    private Stream<Mailbox> retrieveAllMailboxes(MailboxSession mailboxSession) throws MailboxException {
-        List<MailboxMetaData> userMailboxes = getAllMailboxesMetaData(mailboxSession);
-        QuotaLoader quotaLoader = new QuotaLoaderWithDefaultPreloaded(quotaRootResolver, quotaManager, mailboxSession);
-
-        return userMailboxes
-            .stream()
-            .map(mailboxMetaData -> mailboxFactory.builder()
-                .mailboxMetadata(mailboxMetaData)
-                .session(mailboxSession)
-                .usingPreloadedMailboxesMetadata(Optional.of(userMailboxes))
-                .quotaLoader(quotaLoader)
-                .build())
-            .flatMap(OptionalUtils::toStream);
+    private Flux<Mailbox> retrieveAllMailboxes(MailboxSession mailboxSession) {
+        Mono<List<MailboxMetaData>> userMailboxesMono = getAllMailboxesMetaData(mailboxSession).collectList();
+        Mono<QuotaLoaderWithDefaultPreloaded> quotaLoaderMono = Mono.fromCallable(() ->
+            new QuotaLoaderWithDefaultPreloaded(quotaRootResolver, quotaManager, mailboxSession))
+            .subscribeOn(Schedulers.elastic());
+
+        return userMailboxesMono.zipWith(quotaLoaderMono)
+            .flatMapMany(
+                tuple -> Flux.fromIterable(tuple.getT1())
+                    .flatMap(mailboxMetaData -> Mono.justOrEmpty(mailboxFactory.builder()
+                        .mailboxMetadata(mailboxMetaData)
+                        .session(mailboxSession)
+                        .usingPreloadedMailboxesMetadata(Optional.of(tuple.getT1()))
+                        .quotaLoader(tuple.getT2())
+                        .build())));
     }
 
-    private List<MailboxMetaData> getAllMailboxesMetaData(MailboxSession mailboxSession) throws MailboxException {
-        return mailboxManager.search(
+    private Flux<MailboxMetaData> getAllMailboxesMetaData(MailboxSession mailboxSession) {
+        return mailboxManager.searchReactive(
             MailboxQuery.builder()
                 .matchesAllMailboxNames()
                 .build(),
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java
index f0b9cca..f51721b 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/GetMailboxesMethodTest.java
@@ -57,6 +57,8 @@ import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
 
+import reactor.core.publisher.Flux;
+
 public class GetMailboxesMethodTest {
 
     private static final Username USERNAME = Username.of("username@domain.tld");
@@ -106,6 +108,8 @@ public class GetMailboxesMethodTest {
             .thenReturn(ImmutableList.of(new MailboxPath("namespace", Username.of("user"), "name")));
         when(mockedMailboxManager.getMailbox(any(MailboxPath.class), any()))
             .thenThrow(new MailboxException());
+        when(mockedMailboxManager.searchReactive(any(), any()))
+            .thenReturn(Flux.empty());
         GetMailboxesMethod testee = new GetMailboxesMethod(mockedMailboxManager, quotaRootResolver, quotaManager, mailboxFactory, new DefaultMetricFactory());
 
         GetMailboxesRequest getMailboxesRequest = GetMailboxesRequest.builder()


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


[james-project] 23/39: JAMES-3137 Split responsibilities between guice & backend

Posted by bt...@apache.org.
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 9c27d702c7681c1ded61e784943b10060b4c72b9
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Apr 7 17:39:35 2020 +0700

    JAMES-3137 Split responsibilities between guice & backend
    
     - ResilientClusterProvider is responsible of establishing a usable connection
     to a running Cassandra Cluster
     - KeyspaceFactory allows to create a given keyspace but should not enforce
     application specific decisions
    
    This means that:
     - Keyspace creation decisions (and relevant testing) needs to be
     performed at the Guice level
     - ResilientClusterProvider needs to be tested for handling not-yet
     available connection.
     - ResilientClusterProvider needs to play a sample query instead of creating keyspaces
---
 .../backends/cassandra/init/KeyspaceFactory.java   |  31 +----
 .../cassandra/init/ResilientClusterProvider.java   |   4 +-
 .../init/SessionWithInitializedTablesFactory.java  |  30 +---
 .../init/configuration/ClusterConfiguration.java   |  88 +-----------
 .../init/configuration/KeyspaceConfiguration.java  |  98 +++++++++++++
 .../james/backends/cassandra/CassandraCluster.java |  20 ++-
 .../cassandra/CassandraClusterExtension.java       |  13 ++
 .../james/backends/cassandra/DockerCassandra.java  |  63 ++++-----
 .../cassandra/DockerCassandraExtension.java        |   6 +
 .../backends/cassandra/DockerCassandraRule.java    |   5 +
 .../init/ResilientClusterProviderTest.java         | 135 +++++-------------
 .../SessionWithInitializedTablesFactoryTest.java   |   8 +-
 .../modules/mailbox/CassandraSessionModule.java    |  81 +++++++++--
 .../modules/mailbox/KeyspacesConfiguration.java    | 155 +++++++++++++++++++++
 .../org/apache/james/server/CassandraProbe.java    |   9 +-
 .../java/org/apache/james/DockerCassandraRule.java |  10 +-
 .../org/apache/james/KeyspaceCreationTest.java     | 143 +++++++++++++++++++
 .../mailbox/KeyspacesConfigurationTest.java        |  61 ++++++++
 .../rabbitmq/FixingGhostMailboxTest.java           |   5 +-
 19 files changed, 670 insertions(+), 295 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java
index b931272..5f23aad 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java
@@ -22,7 +22,7 @@ package org.apache.james.backends.cassandra.init;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
 
-import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 
 import com.datastax.driver.core.Cluster;
 import com.datastax.driver.core.Session;
@@ -31,43 +31,24 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
 
 public class KeyspaceFactory {
-
     private static final String SYSTEM_SCHEMA = "system_schema";
     private static final String KEYSPACES = "keyspaces";
     private static final String KEYSPACE_NAME = "keyspace_name";
-    private static final int CACHE_REPLICATION_FACTOR = 1;
-
-    public static void createKeyspace(ClusterConfiguration clusterConfiguration, Cluster cluster) {
-        if (clusterConfiguration.shouldCreateKeyspace()) {
-            doCreateKeyspace(clusterConfiguration, cluster);
-            doCreateCacheKeyspace(clusterConfiguration, cluster);
-        }
-    }
 
-    private static void doCreateKeyspace(ClusterConfiguration clusterConfiguration, Cluster cluster) {
-        createKeyspace(cluster, clusterConfiguration.getKeyspace(),
-            clusterConfiguration.getReplicationFactor(),
-            clusterConfiguration.isDurableWrites());
-    }
-
-    private static void createKeyspace(Cluster cluster, String keyspace, int replicationFactor, boolean durableWrites) {
+    public static void createKeyspace(KeyspaceConfiguration configuration, Cluster cluster) {
         try (Session session = cluster.connect()) {
-            if (!keyspaceExist(cluster, keyspace)) {
-                session.execute(SchemaBuilder.createKeyspace(keyspace)
+            if (!keyspaceExist(cluster, configuration.getKeyspace())) {
+                session.execute(SchemaBuilder.createKeyspace(configuration.getKeyspace())
                     .with()
                     .replication(ImmutableMap.<String, Object>builder()
                         .put("class", "SimpleStrategy")
-                        .put("replication_factor", replicationFactor)
+                        .put("replication_factor", configuration.getReplicationFactor())
                         .build())
-                    .durableWrites(durableWrites));
+                    .durableWrites(configuration.isDurableWrites()));
             }
         }
     }
 
-    private static void doCreateCacheKeyspace(ClusterConfiguration clusterConfiguration, Cluster cluster) {
-        createKeyspace(cluster, clusterConfiguration.getCacheKeyspace(), CACHE_REPLICATION_FACTOR, clusterConfiguration.isDurableWrites());
-    }
-
     @VisibleForTesting
     public static boolean keyspaceExist(Cluster cluster, String keyspaceName) {
         try (Session session = cluster.connect(SYSTEM_SCHEMA)) {
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/ResilientClusterProvider.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/ResilientClusterProvider.java
index 5b823bf..f91e68d 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/ResilientClusterProvider.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/ResilientClusterProvider.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.backends.cassandra.init;
 
+import static org.apache.james.backends.cassandra.init.KeyspaceFactory.keyspaceExist;
+
 import java.time.Duration;
 import java.time.LocalDateTime;
 import java.util.concurrent.Callable;
@@ -64,7 +66,7 @@ public class ResilientClusterProvider implements Provider<Cluster> {
         return () -> {
             Cluster cluster = ClusterFactory.create(configuration);
             try {
-                KeyspaceFactory.createKeyspace(configuration, cluster);
+                keyspaceExist(cluster, "any"); // plays a sample query to ensure we can contact the cluster
                 return cluster;
             } catch (Exception e) {
                 cluster.close();
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
index b3d18f4..a996966 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
@@ -23,15 +23,13 @@ import java.util.stream.Stream;
 
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
-import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.components.CassandraTable;
 import org.apache.james.backends.cassandra.components.CassandraType;
-import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
-import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManager;
 
@@ -41,19 +39,14 @@ import com.datastax.driver.core.Session;
 @Singleton
 public class SessionWithInitializedTablesFactory implements Provider<Session> {
     private final CassandraModule module;
-    private final CassandraModule cacheModule;
     private final Session session;
-    private final Session cacheSession;
 
     @Inject
-    public SessionWithInitializedTablesFactory(ClusterConfiguration clusterConfiguration,
+    public SessionWithInitializedTablesFactory(KeyspaceConfiguration keyspaceConfiguration,
                                                Cluster cluster,
-                                               CassandraModule module,
-                                               @Named(InjectionNames.CACHE) CassandraModule cacheModule) {
+                                               CassandraModule module) {
         this.module = module;
-        this.cacheModule = cacheModule;
-        this.session = createSession(cluster, clusterConfiguration.getKeyspace());
-        this.cacheSession = createCacheSession(cluster, clusterConfiguration.getKeyspace());
+        this.session = createSession(cluster, keyspaceConfiguration.getKeyspace());
     }
 
     private Session createSession(Cluster cluster, String keyspace) {
@@ -71,17 +64,6 @@ public class SessionWithInitializedTablesFactory implements Provider<Session> {
         }
     }
 
-    private Session createCacheSession(Cluster cluster, String keyspace) {
-        Session session = cluster.connect(keyspace);
-        try {
-            allOperationsAreFullyPerformed(session, cacheModule);
-            return session;
-        } catch (Exception e) {
-            session.close();
-            throw e;
-        }
-    }
-
     private boolean allOperationsAreFullyPerformed(Session session, CassandraModule module) {
         Stream<Boolean> operations = Stream.of(createTypes(session, module), createTables(session, module));
         return operations.allMatch(updated -> updated);
@@ -102,10 +84,6 @@ public class SessionWithInitializedTablesFactory implements Provider<Session> {
         return session;
     }
 
-    public Session getCacheSession() {
-        return cacheSession;
-    }
-
     @PreDestroy
     public synchronized void destroy() {
         session.close();
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java
index cb56e8a..eb9eeee 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java
@@ -38,9 +38,6 @@ public class ClusterConfiguration {
     public static class Builder {
         private ImmutableList.Builder<Host> hosts;
         private boolean createKeyspace;
-        private Optional<String> keyspace;
-        private Optional<String> cacheKeyspace;
-        private Optional<Integer> replicationFactor;
         private Optional<Integer> minDelay;
         private Optional<Integer> maxRetry;
         private Optional<QueryLoggerConfiguration> queryLoggerConfiguration;
@@ -48,16 +45,12 @@ public class ClusterConfiguration {
         private Optional<Integer> readTimeoutMillis;
         private Optional<Integer> connectTimeoutMillis;
         private Optional<Boolean> useSsl;
-        private Optional<Boolean> durableWrites;
         private Optional<String> username;
         private Optional<String> password;
 
         public Builder() {
             hosts = ImmutableList.builder();
             createKeyspace = false;
-            keyspace = Optional.empty();
-            cacheKeyspace = Optional.empty();
-            replicationFactor = Optional.empty();
             minDelay = Optional.empty();
             maxRetry = Optional.empty();
             queryLoggerConfiguration = Optional.empty();
@@ -67,7 +60,6 @@ public class ClusterConfiguration {
             username = Optional.empty();
             password = Optional.empty();
             useSsl = Optional.empty();
-            durableWrites = Optional.empty();
         }
 
         public Builder host(Host host) {
@@ -90,33 +82,6 @@ public class ClusterConfiguration {
             return this;
         }
 
-        public Builder keyspace(Optional<String> keyspace) {
-            this.keyspace = keyspace;
-            return this;
-        }
-
-        public Builder cacheKeyspace(Optional<String> cacheKeyspace) {
-            this.keyspace = keyspace;
-            return this;
-        }
-
-        public Builder cacheKeyspace(String cacheKeyspace) {
-            return cacheKeyspace(Optional.of(cacheKeyspace));
-        }
-
-        public Builder keyspace(String keyspace) {
-            return keyspace(Optional.of(keyspace));
-        }
-
-        public Builder replicationFactor(Optional<Integer> replicationFactor) {
-            this.replicationFactor = replicationFactor;
-            return this;
-        }
-
-        public Builder replicationFactor(int replicationFactor) {
-            return replicationFactor(Optional.of(replicationFactor));
-        }
-
         public Builder minDelay(Optional<Integer> minDelay) {
             this.minDelay = minDelay;
             return this;
@@ -198,19 +163,10 @@ public class ClusterConfiguration {
             return connectTimeoutMillis(Optional.of(connectTimeoutMillis));
         }
 
-        public Builder disableDurableWrites() {
-            this.durableWrites = Optional.of(false);
-
-            return this;
-        }
-
         public ClusterConfiguration build() {
             return new ClusterConfiguration(
                 hosts.build(),
                 createKeyspace,
-                keyspace.orElse(DEFAULT_KEYSPACE),
-                cacheKeyspace.orElse(DEFAULT_CACHE_KEYSPACE),
-                replicationFactor.orElse(DEFAULT_REPLICATION_FACTOR),
                 minDelay.orElse(DEFAULT_CONNECTION_MIN_DELAY),
                 maxRetry.orElse(DEFAULT_CONNECTION_MAX_RETRIES),
                 queryLoggerConfiguration,
@@ -219,27 +175,20 @@ public class ClusterConfiguration {
                 connectTimeoutMillis.orElse(DEFAULT_CONNECT_TIMEOUT_MILLIS),
                 useSsl.orElse(false),
                 username,
-                password,
-                durableWrites.orElse(true));
+                password);
         }
     }
 
     private static final String CASSANDRA_NODES = "cassandra.nodes";
     public static final String CASSANDRA_CREATE_KEYSPACE = "cassandra.keyspace.create";
-    public static final String CASSANDRA_KEYSPACE = "cassandra.keyspace";
-    public static final String CASSANDRA_CACHE_KEYSPACE = "cassandra.keyspace.cache";
     public static final String CASSANDRA_USER = "cassandra.user";
     public static final String CASSANDRA_PASSWORD = "cassandra.password";
     public static final String CASSANDRA_SSL = "cassandra.ssl";
-    public static final String REPLICATION_FACTOR = "cassandra.replication.factor";
     public static final String READ_TIMEOUT_MILLIS = "cassandra.readTimeoutMillis";
     public static final String CONNECT_TIMEOUT_MILLIS = "cassandra.connectTimeoutMillis";
     public static final String CONNECTION_MAX_RETRY = "cassandra.retryConnection.maxRetries";
     public static final String CONNECTION_RETRY_MIN_DELAY = "cassandra.retryConnection.minDelay";
 
-    private static final String DEFAULT_KEYSPACE = "apache_james";
-    private static final String DEFAULT_CACHE_KEYSPACE = "apache_james_cache";
-    private static final int DEFAULT_REPLICATION_FACTOR = 1;
     private static final int DEFAULT_CONNECTION_MAX_RETRIES = 10;
     private static final int DEFAULT_CONNECTION_MIN_DELAY = 5000;
     private static final int DEFAULT_READ_TIMEOUT_MILLIS = 5000;
@@ -259,9 +208,6 @@ public class ClusterConfiguration {
 
         ClusterConfiguration.Builder builder = ClusterConfiguration.builder()
             .hosts(listCassandraServers(configuration))
-            .keyspace(Optional.ofNullable(configuration.getString(CASSANDRA_KEYSPACE, null)))
-            .cacheKeyspace(Optional.ofNullable(configuration.getString(CASSANDRA_CACHE_KEYSPACE, null)))
-            .replicationFactor(Optional.ofNullable(configuration.getInteger(REPLICATION_FACTOR, null)))
             .minDelay(Optional.ofNullable(configuration.getInteger(CONNECTION_RETRY_MIN_DELAY, null)))
             .maxRetry(Optional.ofNullable(configuration.getInteger(CONNECTION_MAX_RETRY, null)))
             .queryLoggerConfiguration(QueryLoggerConfiguration.from(configuration))
@@ -318,9 +264,6 @@ public class ClusterConfiguration {
 
     private final List<Host> hosts;
     private final boolean createKeyspace;
-    private final String keyspace;
-    private final String cacheKeyspace;
-    private final int replicationFactor;
     private final int minDelay;
     private final int maxRetry;
     private final Optional<QueryLoggerConfiguration> queryLoggerConfiguration;
@@ -330,17 +273,13 @@ public class ClusterConfiguration {
     private final boolean useSsl;
     private final Optional<String> username;
     private final Optional<String> password;
-    private final boolean durableWrites;
 
-    public ClusterConfiguration(List<Host> hosts, boolean createKeyspace, String keyspace, String cacheKeyspace, int replicationFactor, int minDelay, int maxRetry,
+    public ClusterConfiguration(List<Host> hosts, boolean createKeyspace, int minDelay, int maxRetry,
                                 Optional<QueryLoggerConfiguration> queryLoggerConfiguration, Optional<PoolingOptions> poolingOptions,
                                 int readTimeoutMillis, int connectTimeoutMillis, boolean useSsl, Optional<String> username,
-                                Optional<String> password, boolean durableWrites) {
+                                Optional<String> password) {
         this.hosts = hosts;
         this.createKeyspace = createKeyspace;
-        this.keyspace = keyspace;
-        this.cacheKeyspace = cacheKeyspace;
-        this.replicationFactor = replicationFactor;
         this.minDelay = minDelay;
         this.maxRetry = maxRetry;
         this.queryLoggerConfiguration = queryLoggerConfiguration;
@@ -350,11 +289,6 @@ public class ClusterConfiguration {
         this.useSsl = useSsl;
         this.username = username;
         this.password = password;
-        this.durableWrites = durableWrites;
-    }
-
-    public boolean isDurableWrites() {
-        return durableWrites;
     }
 
     public List<Host> getHosts() {
@@ -365,18 +299,6 @@ public class ClusterConfiguration {
         return createKeyspace;
     }
 
-    public String getKeyspace() {
-        return keyspace;
-    }
-
-    public String getCacheKeyspace() {
-        return cacheKeyspace;
-    }
-
-    public int getReplicationFactor() {
-        return replicationFactor;
-    }
-
     public int getMinDelay() {
         return minDelay;
     }
@@ -422,8 +344,6 @@ public class ClusterConfiguration {
                 && Objects.equals(this.maxRetry, that.maxRetry)
                 && Objects.equals(this.hosts, that.hosts)
                 && Objects.equals(this.createKeyspace, that.createKeyspace)
-                && Objects.equals(this.keyspace, that.keyspace)
-                && Objects.equals(this.replicationFactor, that.replicationFactor)
                 && Objects.equals(this.queryLoggerConfiguration, that.queryLoggerConfiguration)
                 && Objects.equals(this.poolingOptions, that.poolingOptions)
                 && Objects.equals(this.readTimeoutMillis, that.readTimeoutMillis)
@@ -437,7 +357,7 @@ public class ClusterConfiguration {
 
     @Override
     public final int hashCode() {
-        return Objects.hash(hosts, createKeyspace, keyspace, replicationFactor, minDelay, maxRetry, queryLoggerConfiguration, poolingOptions,
+        return Objects.hash(hosts, createKeyspace, minDelay, maxRetry, queryLoggerConfiguration, poolingOptions,
             readTimeoutMillis, connectTimeoutMillis, username, useSsl, password);
     }
 }
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/KeyspaceConfiguration.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/KeyspaceConfiguration.java
new file mode 100644
index 0000000..3464336
--- /dev/null
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/KeyspaceConfiguration.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.backends.cassandra.init.configuration;
+
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
+public class KeyspaceConfiguration {
+
+    public interface Builder {
+        @FunctionalInterface
+        interface RequireKeyspace {
+            RequireReplicationFactor keyspace(String name);
+        }
+
+        @FunctionalInterface
+        interface RequireReplicationFactor {
+            RequireDurableWrites replicationFactor(int replicationFactor);
+        }
+
+        @FunctionalInterface
+        interface RequireDurableWrites {
+            KeyspaceConfiguration durableWrites(boolean durableWrites);
+
+            default KeyspaceConfiguration disableDurableWrites() {
+                return durableWrites(false);
+            }
+        }
+    }
+
+    private static final String DEFAULT_KEYSPACE = "apache_james";
+    private static final int DEFAULT_REPLICATION_FACTOR = 1;
+
+    private static final boolean DEFAULT_SSL = false;
+
+    public static Builder.RequireKeyspace builder() {
+        return name -> replicationFactor -> durableWrites -> new KeyspaceConfiguration(name, replicationFactor, durableWrites);
+    }
+
+    private final String keyspace;
+    private final int replicationFactor;
+    private final boolean durableWrites;
+
+    public KeyspaceConfiguration(String keyspace, int replicationFactor, boolean durableWrites) {
+        Preconditions.checkArgument(replicationFactor > 0, "'' needs to be strictly positive");
+
+        this.keyspace = keyspace;
+        this.replicationFactor = replicationFactor;
+        this.durableWrites = durableWrites;
+    }
+
+    public boolean isDurableWrites() {
+        return durableWrites;
+    }
+
+    public String getKeyspace() {
+        return keyspace;
+    }
+
+    public int getReplicationFactor() {
+        return replicationFactor;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof KeyspaceConfiguration) {
+            KeyspaceConfiguration that = (KeyspaceConfiguration) o;
+
+            return Objects.equals(this.keyspace, that.keyspace)
+                && Objects.equals(this.replicationFactor, that.replicationFactor)
+                && Objects.equals(this.durableWrites, that.durableWrites);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(keyspace, replicationFactor, durableWrites);
+    }
+}
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
index 3be615e..f4b576c 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
@@ -28,11 +28,13 @@ import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
 import org.apache.james.backends.cassandra.init.ClusterFactory;
 import org.apache.james.backends.cassandra.init.SessionWithInitializedTablesFactory;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.util.Host;
 
 import com.datastax.driver.core.Cluster;
 
 public final class CassandraCluster implements AutoCloseable {
+    private static final String KEYSPACE = "testing";
 
     public static CassandraCluster create(CassandraModule module, Host host) {
         assertClusterNotRunning();
@@ -53,18 +55,26 @@ public final class CassandraCluster implements AutoCloseable {
     private final Cluster nonPrivilegedCluster;
     private final TestingSession nonPrivilegedSession;
     private final CassandraTypesProvider typesProvider;
+    private final ClusterConfiguration clusterConfiguration;
 
     private CassandraCluster(CassandraModule module, Host host) throws RuntimeException {
         this.module = module;
 
-        ClusterConfiguration configuration = DockerCassandra.configurationBuilder(host)
-            .build();
-        this.nonPrivilegedCluster = ClusterFactory.create(configuration);
-        this.nonPrivilegedSession = new TestingSession(new SessionWithInitializedTablesFactory(configuration,
-            nonPrivilegedCluster, module, CassandraModule.EMPTY_MODULE).get());
+        this.clusterConfiguration = DockerCassandra.configurationBuilder(host).build();
+        this.nonPrivilegedCluster = ClusterFactory.create(clusterConfiguration);
+        KeyspaceConfiguration keyspaceConfiguration = KeyspaceConfiguration.builder()
+            .keyspace(KEYSPACE)
+            .replicationFactor(1)
+            .disableDurableWrites();
+        this.nonPrivilegedSession = new TestingSession(new SessionWithInitializedTablesFactory(keyspaceConfiguration,
+            nonPrivilegedCluster, module).get());
         this.typesProvider = new CassandraTypesProvider(module, nonPrivilegedSession);
     }
 
+    public ClusterConfiguration getClusterConfiguration() {
+        return clusterConfiguration;
+    }
+
     public TestingSession getConf() {
         return nonPrivilegedSession;
     }
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraClusterExtension.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraClusterExtension.java
index 35fc63a..c5ffbe5 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraClusterExtension.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraClusterExtension.java
@@ -20,6 +20,7 @@
 package org.apache.james.backends.cassandra;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
 import org.junit.jupiter.api.extension.AfterAllCallback;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
@@ -48,6 +49,18 @@ public class CassandraClusterExtension implements BeforeAllCallback, BeforeEachC
         }
     }
 
+    public ClusterConfiguration.Builder clusterConfiguration() {
+        return cassandraExtension.clusterConfiguration();
+    }
+
+    public void pause() {
+        cassandraExtension.getDockerCassandra().getContainer().pause();
+    }
+
+    public void unpause() {
+        cassandraExtension.getDockerCassandra().getContainer().unpause();
+    }
+
     private void start() {
         cassandraCluster = CassandraCluster.create(cassandraModule, cassandraExtension.getDockerCassandra().getHost());
     }
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java
index b4db209..e11113d 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java
@@ -22,6 +22,7 @@ package org.apache.james.backends.cassandra;
 import org.apache.james.backends.cassandra.init.ClusterFactory;
 import org.apache.james.backends.cassandra.init.KeyspaceFactory;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.util.Host;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -57,18 +58,16 @@ public class DockerCassandra {
             this.cassandra = cassandra;
         }
 
-        public void initializeKeyspace(String keyspace) {
-            ClusterConfiguration configuration = configurationBuilder(keyspace).build();
-
-            try (Cluster privilegedCluster = ClusterFactory.create(configuration)) {
+        public void initializeKeyspace(KeyspaceConfiguration configuration) {
+            try (Cluster privilegedCluster = ClusterFactory.create(cassandra.superUserConfigurationBuilder().build())) {
                 provisionNonPrivilegedUser(privilegedCluster);
                 KeyspaceFactory.createKeyspace(configuration, privilegedCluster);
-                grantPermissionToTestingUser(privilegedCluster, keyspace);
+                grantPermissionToTestingUser(privilegedCluster, configuration.getKeyspace());
             }
         }
 
         public void dropKeyspace(String keyspace) {
-            try (Cluster cluster = ClusterFactory.create(configurationBuilder(keyspace).build())) {
+            try (Cluster cluster = ClusterFactory.create(cassandra.superUserConfigurationBuilder().build())) {
                 try (Session cassandraSession = cluster.newSession()) {
                     boolean applied = cassandraSession.execute(
                         SchemaBuilder.dropKeyspace(keyspace)
@@ -80,13 +79,6 @@ public class DockerCassandra {
                     }
                 }
             }
-
-        }
-
-        public boolean keyspaceExist(String keyspace) {
-            try (Cluster cluster = ClusterFactory.create(configurationBuilder(keyspace).build())) {
-                return KeyspaceFactory.keyspaceExist(cluster, keyspace);
-            }
         }
 
         private void provisionNonPrivilegedUser(Cluster privilegedCluster) {
@@ -104,33 +96,19 @@ public class DockerCassandra {
                 session.execute("GRANT DROP ON KEYSPACE " + keyspace + " TO " + CASSANDRA_TESTING_USER);
             }
         }
-
-        private ClusterConfiguration.Builder configurationBuilder(String keyspace) {
-            return ClusterConfiguration.builder()
-                .host(cassandra.getHost())
-                .username(CASSANDRA_SUPER_USER)
-                .password(CASSANDRA_SUPER_USER_PASSWORD)
-                .keyspace(keyspace)
-                .createKeyspace()
-                .disableDurableWrites()
-                .replicationFactor(1)
-                .maxRetry(RELAXED_RETRIES);
-        }
     }
 
     public static ClusterConfiguration.Builder configurationBuilder(Host... hosts) {
         return ClusterConfiguration.builder()
             .hosts(hosts)
-            .keyspace(KEYSPACE)
             .username(CASSANDRA_TESTING_USER)
             .password(CASSANDRA_TESTING_PASSWORD)
-            .disableDurableWrites()
-            .replicationFactor(1)
             .maxRetry(RELAXED_RETRIES);
     }
 
     private static final Logger logger = LoggerFactory.getLogger(DockerCassandra.class);
-    private static final String KEYSPACE = "testing";
+    public static final String KEYSPACE = "testing";
+    public static final String CACHE_KEYSPACE = "testing_cache";
     private static final int RELAXED_RETRIES = 2;
 
     public static final String CASSANDRA_TESTING_USER = "james_testing";
@@ -186,7 +164,8 @@ public class DockerCassandra {
     public void start() {
         if (!cassandraContainer.isRunning()) {
             cassandraContainer.start();
-            administrator().initializeKeyspace(KEYSPACE);
+            administrator().initializeKeyspace(mainKeyspaceConfiguration());
+            administrator().initializeKeyspace(cacheKeyspaceConfiguration());
         }
     }
 
@@ -239,8 +218,26 @@ public class DockerCassandra {
         return configurationBuilder(getHost());
     }
 
-    public ClusterConfiguration.Builder configurationBuilderForSuperUser() {
-        return administrator()
-            .configurationBuilder(KEYSPACE);
+    public ClusterConfiguration.Builder superUserConfigurationBuilder() {
+        return ClusterConfiguration.builder()
+            .host(getHost())
+            .username(CassandraResourcesManager.CASSANDRA_SUPER_USER)
+            .password(CassandraResourcesManager.CASSANDRA_SUPER_USER_PASSWORD)
+            .createKeyspace()
+            .maxRetry(RELAXED_RETRIES);
+    }
+
+    public static KeyspaceConfiguration mainKeyspaceConfiguration() {
+        return KeyspaceConfiguration.builder()
+            .keyspace(KEYSPACE)
+            .replicationFactor(1)
+            .disableDurableWrites();
+    }
+
+    public static KeyspaceConfiguration cacheKeyspaceConfiguration() {
+        return KeyspaceConfiguration.builder()
+            .keyspace(CACHE_KEYSPACE)
+            .replicationFactor(1)
+            .disableDurableWrites();
     }
 }
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
index fdf585c..5bd77e4 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraExtension.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.backends.cassandra;
 
+import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
 import org.apache.james.util.Host;
 import org.junit.jupiter.api.extension.AfterAllCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
@@ -37,6 +38,11 @@ public class DockerCassandraExtension implements BeforeAllCallback, AfterAllCall
         cassandraContainer = new DockerCassandraRule();
     }
 
+
+    ClusterConfiguration.Builder clusterConfiguration() {
+        return cassandraContainer.clusterConfiguration();
+    }
+
     @Override
     public void beforeAll(ExtensionContext context) {
         cassandraContainer.start();
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraRule.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraRule.java
index aa67a4c..44c8e7f 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraRule.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandraRule.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.backends.cassandra;
 
+import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
 import org.apache.james.util.Host;
 import org.junit.rules.ExternalResource;
 import org.testcontainers.containers.GenericContainer;
@@ -73,4 +74,8 @@ public class DockerCassandraRule extends ExternalResource {
         DockerCassandraSingleton.singleton.unpause();
     }
 
+    ClusterConfiguration.Builder clusterConfiguration() {
+        return DockerCassandraSingleton.singleton.configurationBuilder();
+    }
+
 }
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/ResilientClusterProviderTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/ResilientClusterProviderTest.java
index 5614391..29142d0 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/ResilientClusterProviderTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/ResilientClusterProviderTest.java
@@ -19,127 +19,60 @@
 
 package org.apache.james.backends.cassandra.init;
 
-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 java.time.Duration;
+
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
-import org.apache.james.backends.cassandra.DockerCassandra;
 import org.apache.james.backends.cassandra.components.CassandraModule;
-import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
-class ResilientClusterProviderTest {
-
-    private static final String KEYSPACE = "my_keyspace";
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
 
+class ResilientClusterProviderTest {
     @RegisterExtension
     static CassandraClusterExtension cassandraExtension = new CassandraClusterExtension(CassandraModule.EMPTY_MODULE);
 
-    @AfterEach
-    void tearDown(DockerCassandra dockerCassandra) {
-        dockerCassandra.administrator()
-            .dropKeyspace(KEYSPACE);
+    @Test
+    void getShouldNotThrowWhenHealthyCassandra() {
+        assertThatCode(() -> new ResilientClusterProvider(cassandraExtension.clusterConfiguration().build())
+                .get())
+            .doesNotThrowAnyException();
     }
 
-    @Nested
-    class WhenAllowCreatingKeySpace {
-
-        @Test
-        void initializationShouldThrowWhenKeyspaceDoesntExist(DockerCassandra dockerCassandra) {
-            assertThatThrownBy(() -> new ResilientClusterProvider(
-                    dockerCassandra.configurationBuilder()
-                        .keyspace(KEYSPACE)
-                        .createKeyspace()
-                        .build()))
-                .isInstanceOf(IllegalStateException.class)
-                .hasStackTraceContaining("User james_testing has no CREATE permission on <all keyspaces> or any of its parents");
-        }
-
-        @Test
-        void initializationWithPrivilegedUserShouldCreateKeySpaceWhenNotExisted(DockerCassandra dockerCassandra) {
-            new ResilientClusterProvider(dockerCassandra.configurationBuilderForSuperUser()
-                .keyspace(KEYSPACE)
-                .createKeyspace()
-                .build());
-
-            assertThat(dockerCassandra.administrator()
-                    .keyspaceExist(KEYSPACE))
-                .isTrue();
-        }
-
-        @Test
-        void initializationShouldNotThrowWhenKeyspaceAlreadyExisted(DockerCassandra dockerCassandra) {
-            ClusterConfiguration configuration = dockerCassandra.configurationBuilder()
-                .keyspace(KEYSPACE)
-                .createKeyspace()
-                .build();
-            dockerCassandra.administrator()
-                .initializeKeyspace(KEYSPACE);
-
-            assertThatCode(() -> new ResilientClusterProvider(configuration))
-                .doesNotThrowAnyException();
-        }
-
-        @Test
-        void initializationShouldNotImpactKeyspaceExistenceWhenItAlreadyExisted(DockerCassandra dockerCassandra) {
-            ClusterConfiguration configuration = dockerCassandra.configurationBuilder()
-                .keyspace(KEYSPACE)
-                .createKeyspace()
-                .build();
-            dockerCassandra.administrator()
-                .initializeKeyspace(KEYSPACE);
-
-            new ResilientClusterProvider(configuration);
-
-            assertThat(dockerCassandra.administrator()
-                    .keyspaceExist(KEYSPACE))
-                .isTrue();
+    @Test
+    void getShouldThrowWhenNotHealthyCassandra() {
+        cassandraExtension.pause();
+        try {
+            assertThatThrownBy(() -> new ResilientClusterProvider(cassandraExtension.clusterConfiguration()
+                    .maxRetry(1)
+                    .minDelay(1)
+                    .build())
+                .get())
+                .isInstanceOf(Exception.class);
+        } finally {
+            cassandraExtension.unpause();
         }
     }
 
-    @Nested
-    class WhenProhibitCreatingKeySpace {
-
-        @Test
-        void initializationShouldNotCreateWhenKeyspaceDoesntExist(DockerCassandra dockerCassandra) {
-            new ResilientClusterProvider(dockerCassandra.configurationBuilder()
-                .keyspace(KEYSPACE)
-                .build());
+    @Test
+    void getShouldRecoverFromTemporaryOutage() {
+        cassandraExtension.pause();
 
-            assertThat(dockerCassandra.administrator()
-                    .keyspaceExist(KEYSPACE))
-                .isFalse();
-        }
+        try {
+            Mono.delay(Duration.ofMillis(200))
+                .then(Mono.fromRunnable(cassandraExtension::unpause))
+                .subscribeOn(Schedulers.elastic())
+                .subscribe();
 
-        @Test
-        void initializationShouldNotThrowWhenKeyspaceAlreadyExisted(DockerCassandra dockerCassandra) {
-            ClusterConfiguration configuration = dockerCassandra.configurationBuilder()
-                .keyspace(KEYSPACE)
-                .build();
-            dockerCassandra.administrator()
-                .initializeKeyspace(KEYSPACE);
-
-            assertThatCode(() -> new ResilientClusterProvider(configuration))
+            assertThatCode(() -> new ResilientClusterProvider(cassandraExtension.clusterConfiguration().build())
+                .get())
                 .doesNotThrowAnyException();
-        }
-
-        @Test
-        void initializationShouldNotImpactKeyspaceExistenceWhenItAlreadyExisted(DockerCassandra dockerCassandra) {
-            ClusterConfiguration configuration = dockerCassandra.configurationBuilder()
-                .keyspace(KEYSPACE)
-                .build();
-            dockerCassandra.administrator()
-                .initializeKeyspace(KEYSPACE);
-
-            new ResilientClusterProvider(configuration);
-
-            assertThat(dockerCassandra.administrator()
-                    .keyspaceExist(KEYSPACE))
-                .isTrue();
+        } finally {
+            cassandraExtension.unpause();
         }
     }
 }
\ No newline at end of file
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
index c971fea..84a1a71 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
@@ -30,6 +30,7 @@ import org.apache.james.backends.cassandra.DockerCassandra;
 import org.apache.james.backends.cassandra.DockerCassandraExtension;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManager;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
@@ -123,11 +124,12 @@ class SessionWithInitializedTablesFactoryTest {
         ClusterConfiguration clusterConfiguration = DockerCassandra.configurationBuilder(cassandraServer.getHost())
             .build();
         Cluster cluster = ClusterFactory.create(clusterConfiguration);
+        KeyspaceConfiguration keyspaceConfiguration = DockerCassandra.mainKeyspaceConfiguration();
+        KeyspaceFactory.createKeyspace(keyspaceConfiguration, cluster);
         return () -> new SessionWithInitializedTablesFactory(
-                clusterConfiguration,
+                keyspaceConfiguration,
                 cluster,
-                MODULE,
-                CassandraModule.EMPTY_MODULE)
+                MODULE)
             .get();
     }
 
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
index dcf3842..e68713e 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
@@ -25,11 +25,13 @@ import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.init.CassandraZonedDateTimeModule;
+import org.apache.james.backends.cassandra.init.KeyspaceFactory;
 import org.apache.james.backends.cassandra.init.ResilientClusterProvider;
 import org.apache.james.backends.cassandra.init.SessionWithInitializedTablesFactory;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
 import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.backends.cassandra.utils.CassandraHealthCheck;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
@@ -37,6 +39,7 @@ import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManage
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.lifecycle.api.StartUpCheck;
+import org.apache.james.lifecycle.api.Startable;
 import org.apache.james.mailbox.store.BatchSizes;
 import org.apache.james.server.CassandraProbe;
 import org.apache.james.util.Host;
@@ -49,6 +52,7 @@ import com.datastax.driver.core.Cluster;
 import com.datastax.driver.core.Session;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
@@ -70,6 +74,14 @@ public class CassandraSessionModule extends AbstractModule {
         bind(CassandraUtils.class).in(Scopes.SINGLETON);
         bind(Cluster.class).toProvider(ResilientClusterProvider.class);
 
+        bind(InitializedCluster.class).in(Scopes.SINGLETON);
+        bind(MainSessionWithInitializedTablesFactory.class).in(Scopes.SINGLETON);
+        bind(CacheSessionWithInitializedTablesFactory.class).in(Scopes.SINGLETON);
+
+        bind(Session.class).toProvider(MainSessionWithInitializedTablesFactory.class);
+        bind(Session.class).annotatedWith(Names.named(InjectionNames.CACHE))
+            .toProvider(CacheSessionWithInitializedTablesFactory.class);
+
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().toInstance(CassandraZonedDateTimeModule.MODULE);
         cassandraDataDefinitions.addBinding().toInstance(CassandraSchemaVersionModule.MODULE);
@@ -89,19 +101,6 @@ public class CassandraSessionModule extends AbstractModule {
 
     @Provides
     @Singleton
-    Session provideSession(SessionWithInitializedTablesFactory sessionFactory) {
-        return sessionFactory.get();
-    }
-
-    @Named(InjectionNames.CACHE)
-    @Provides
-    @Singleton
-    Session provideCacheSession(SessionWithInitializedTablesFactory sessionFactory) {
-        return sessionFactory.getCacheSession();
-    }
-
-    @Provides
-    @Singleton
     CassandraModule composeDataDefinitions(Set<CassandraModule> modules) {
         return CassandraModule.aggregateModules(modules);
     }
@@ -157,4 +156,60 @@ public class CassandraSessionModule extends AbstractModule {
                 .build();
         }
     }
+
+    @Provides
+    @Singleton
+    KeyspacesConfiguration provideKeyspacesConfiguration(PropertiesProvider propertiesProvider) throws ConfigurationException {
+        try {
+            return KeyspacesConfiguration.from(propertiesProvider.getConfiguration(CASSANDRA_FILE_NAME));
+        } catch (FileNotFoundException e) {
+            LOGGER.warn("Could not locate cassandra configuration file. Using default keyspaces configuration instead");
+            return KeyspacesConfiguration.builder().build();
+        }
+    }
+
+    @Provides
+    @Singleton
+    KeyspaceConfiguration provideMainKeyspaceConfiguration(KeyspacesConfiguration keyspacesConfiguration) {
+        return keyspacesConfiguration.mainKeyspaceConfiguration();
+    }
+
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    KeyspaceConfiguration provideCacheKeyspaceConfiguration(KeyspacesConfiguration keyspacesConfiguration) {
+        return keyspacesConfiguration.cacheKeyspaceConfiguration();
+    }
+
+    private static class MainSessionWithInitializedTablesFactory extends SessionWithInitializedTablesFactory {
+        @Inject
+        public MainSessionWithInitializedTablesFactory(KeyspaceConfiguration keyspaceConfiguration,
+                                                       InitializedCluster cluster,
+                                                       CassandraModule module) {
+            super(keyspaceConfiguration, cluster.cluster, module);
+        }
+    }
+
+    private static class CacheSessionWithInitializedTablesFactory extends SessionWithInitializedTablesFactory {
+        @Inject
+        public CacheSessionWithInitializedTablesFactory(@Named(InjectionNames.CACHE) KeyspaceConfiguration keyspaceConfiguration,
+                                                        InitializedCluster cluster,
+                                                        @Named(InjectionNames.CACHE) CassandraModule module) {
+            super(keyspaceConfiguration, cluster.cluster, module);
+        }
+    }
+
+    private static class InitializedCluster implements Startable {
+        private final Cluster cluster;
+
+        @Inject
+        private InitializedCluster(Cluster cluster, ClusterConfiguration clusterConfiguration, KeyspacesConfiguration keyspacesConfiguration) {
+            this.cluster = cluster;
+
+            if (clusterConfiguration.shouldCreateKeyspace()) {
+                KeyspaceFactory.createKeyspace(keyspacesConfiguration.mainKeyspaceConfiguration(), cluster);
+                KeyspaceFactory.createKeyspace(keyspacesConfiguration.cacheKeyspaceConfiguration(), cluster);
+            }
+        }
+    }
 }
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/KeyspacesConfiguration.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/KeyspacesConfiguration.java
new file mode 100644
index 0000000..e1dd2e2
--- /dev/null
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/KeyspacesConfiguration.java
@@ -0,0 +1,155 @@
+/****************************************************************
+ * 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.mailbox;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
+
+import com.google.common.base.Preconditions;
+
+public class KeyspacesConfiguration {
+    public static class Builder {
+        private Optional<String> keyspace;
+        private Optional<String> cacheKeyspace;
+        private Optional<Integer> replicationFactor;
+        private Optional<Boolean> durableWrites;
+
+        public Builder() {
+            keyspace = Optional.empty();
+            cacheKeyspace = Optional.empty();
+            replicationFactor = Optional.empty();
+            durableWrites = Optional.empty();
+        }
+
+        public Builder keyspace(Optional<String> keyspace) {
+            this.keyspace = keyspace;
+            return this;
+        }
+
+        public Builder cacheKeyspace(Optional<String> cacheKeyspace) {
+            this.cacheKeyspace = cacheKeyspace;
+            return this;
+        }
+
+        public Builder cacheKeyspace(String cacheKeyspace) {
+            return cacheKeyspace(Optional.of(cacheKeyspace));
+        }
+
+        public Builder keyspace(String keyspace) {
+            return keyspace(Optional.of(keyspace));
+        }
+
+        public Builder replicationFactor(Optional<Integer> replicationFactor) {
+            this.replicationFactor = replicationFactor;
+            return this;
+        }
+
+        public Builder replicationFactor(int replicationFactor) {
+            return replicationFactor(Optional.of(replicationFactor));
+        }
+
+        public Builder disableDurableWrites() {
+            this.durableWrites = Optional.of(false);
+
+            return this;
+        }
+
+        public KeyspacesConfiguration build() {
+            String keyspace = this.keyspace.orElse(DEFAULT_KEYSPACE);
+            String cacheKeyspace = this.cacheKeyspace.orElse(DEFAULT_CACHE_KEYSPACE);
+            Preconditions.checkState(!keyspace.equals(cacheKeyspace),
+                "'cassandra.keyspace' and 'cassandra.keyspace.cache' needs to have distinct values");
+
+            return new KeyspacesConfiguration(
+                keyspace,
+                cacheKeyspace,
+                replicationFactor.orElse(DEFAULT_REPLICATION_FACTOR),
+                durableWrites.orElse(true));
+        }
+    }
+
+    public static final String CASSANDRA_KEYSPACE = "cassandra.keyspace";
+    public static final String CASSANDRA_CACHE_KEYSPACE = "cassandra.keyspace.cache";
+    public static final String REPLICATION_FACTOR = "cassandra.replication.factor";
+
+    private static final String DEFAULT_KEYSPACE = "apache_james";
+    private static final String DEFAULT_CACHE_KEYSPACE = "apache_james_cache";
+    private static final int DEFAULT_REPLICATION_FACTOR = 1;
+
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static KeyspacesConfiguration from(Configuration configuration) {
+        return KeyspacesConfiguration.builder()
+            .keyspace(Optional.ofNullable(configuration.getString(CASSANDRA_KEYSPACE, null)))
+            .cacheKeyspace(Optional.ofNullable(configuration.getString(CASSANDRA_CACHE_KEYSPACE, null)))
+            .replicationFactor(Optional.ofNullable(configuration.getInteger(REPLICATION_FACTOR, null)))
+            .build();
+    }
+
+    private final String keyspace;
+    private final String cacheKeyspace;
+    private final int replicationFactor;
+    private final boolean durableWrites;
+
+    public KeyspacesConfiguration(String keyspace, String cacheKeyspace, int replicationFactor, boolean durableWrites) {
+        this.keyspace = keyspace;
+        this.cacheKeyspace = cacheKeyspace;
+        this.replicationFactor = replicationFactor;
+        this.durableWrites = durableWrites;
+    }
+
+    public KeyspaceConfiguration mainKeyspaceConfiguration() {
+        return KeyspaceConfiguration.builder()
+            .keyspace(keyspace)
+            .replicationFactor(replicationFactor)
+            .durableWrites(durableWrites);
+    }
+
+    public KeyspaceConfiguration cacheKeyspaceConfiguration() {
+        return KeyspaceConfiguration.builder()
+            .keyspace(cacheKeyspace)
+            .replicationFactor(1)
+            .durableWrites(durableWrites);
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof KeyspacesConfiguration) {
+            KeyspacesConfiguration that = (KeyspacesConfiguration) o;
+
+            return Objects.equals(this.replicationFactor, that.replicationFactor)
+                && Objects.equals(this.durableWrites, that.durableWrites)
+                && Objects.equals(this.keyspace, that.keyspace)
+                && Objects.equals(this.cacheKeyspace, that.cacheKeyspace);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(keyspace, cacheKeyspace, replicationFactor, durableWrites);
+    }
+}
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/server/CassandraProbe.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/server/CassandraProbe.java
index 6483b16..a0f17e0 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/server/CassandraProbe.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/server/CassandraProbe.java
@@ -22,17 +22,24 @@ package org.apache.james.server;
 import javax.inject.Inject;
 
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.KeyspaceConfiguration;
 import org.apache.james.utils.GuiceProbe;
 
 public class CassandraProbe implements GuiceProbe {
     private final ClusterConfiguration clusterConfiguration;
+    private final KeyspaceConfiguration mainKeyspaceConfiguration;
 
     @Inject
-    public CassandraProbe(ClusterConfiguration configuration) {
+    public CassandraProbe(ClusterConfiguration configuration, KeyspaceConfiguration mainKeyspaceConfiguration) {
         this.clusterConfiguration = configuration;
+        this.mainKeyspaceConfiguration = mainKeyspaceConfiguration;
     }
 
     public ClusterConfiguration getConfiguration() {
         return clusterConfiguration;
     }
+
+    public KeyspaceConfiguration getMainKeyspaceConfiguration() {
+        return mainKeyspaceConfiguration;
+    }
 }
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/DockerCassandraRule.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/DockerCassandraRule.java
index d003410..6ed7d39 100644
--- a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/DockerCassandraRule.java
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/DockerCassandraRule.java
@@ -21,6 +21,7 @@ package org.apache.james;
 
 import org.apache.james.backends.cassandra.DockerCassandra;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.modules.mailbox.KeyspacesConfiguration;
 import org.apache.james.server.CassandraTruncateTableTask;
 import org.apache.james.util.Host;
 import org.junit.runner.Description;
@@ -46,11 +47,18 @@ public class DockerCassandraRule implements GuiceModuleTestRule {
 
     @Override
     public Module getModule() {
-        return Modules.combine((binder) -> binder.bind(ClusterConfiguration.class)
+        return Modules.combine(binder -> binder.bind(ClusterConfiguration.class)
             .toInstance(DockerCassandra.configurationBuilder(cassandraContainer.getHost())
                 .maxRetry(20)
                 .minDelay(5000)
                 .build()),
+            binder -> binder.bind(KeyspacesConfiguration.class)
+                .toInstance(KeyspacesConfiguration.builder()
+                    .keyspace(DockerCassandra.KEYSPACE)
+                    .cacheKeyspace(DockerCassandra.CACHE_KEYSPACE)
+                    .replicationFactor(1)
+                    .disableDurableWrites()
+                    .build()),
             binder -> Multibinder.newSetBinder(binder, CleanupTasksPerformer.CleanupTask.class)
                 .addBinding()
                 .to(CassandraTruncateTableTask.class));
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/KeyspaceCreationTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/KeyspaceCreationTest.java
new file mode 100644
index 0000000..728c733
--- /dev/null
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/KeyspaceCreationTest.java
@@ -0,0 +1,143 @@
+/****************************************************************
+ * 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;
+
+import static org.apache.james.CassandraJamesServerMain.ALL_BUT_JMX_CASSANDRA_MODULE;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.backends.cassandra.DockerCassandraSingleton;
+import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.apache.james.modules.mailbox.KeyspacesConfiguration;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class KeyspaceCreationTest {
+    @Nested
+    class CreateWhenKeyspaceExists {
+        @RegisterExtension
+        JamesServerExtension testExtension = new JamesServerBuilder()
+            .extension(new DockerElasticSearchExtension())
+            .extension(new CassandraExtension())
+            .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+                .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+                .overrideWith(TestJMAPServerModule.limitToTenMessages()))
+            .overrideServerModule(binder -> binder.bind(ClusterConfiguration.class)
+                .toInstance(DockerCassandraSingleton.singleton.configurationBuilder()
+                    .createKeyspace()
+                    .build()))
+            .disableAutoStart()
+            .build();
+
+        @Test
+        void startShouldNotThrowWhenKeyspaceExists(GuiceJamesServer jamesServer) {
+            assertThatCode(jamesServer::start)
+                .doesNotThrowAnyException();
+        }
+    }
+
+    @Nested
+    class CreateWhenDoesNotExistAndHasRights {
+        @RegisterExtension
+        JamesServerExtension testExtension = new JamesServerBuilder()
+            .extension(new DockerElasticSearchExtension())
+            .extension(new CassandraExtension())
+            .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+                .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+                .overrideWith(TestJMAPServerModule.limitToTenMessages()))
+            .overrideServerModule(binder -> binder.bind(KeyspacesConfiguration.class).toInstance(KeyspacesConfiguration.builder()
+                .keyspace("non_existing_keyspace")
+                .cacheKeyspace("cache_non_existing_keyspace")
+                .replicationFactor(1)
+                .disableDurableWrites()
+                .build()))
+            .overrideServerModule(binder -> binder.bind(ClusterConfiguration.class)
+                    .toInstance(DockerCassandraSingleton.singleton.superUserConfigurationBuilder()
+                        .createKeyspace()
+                        .build()))
+            .disableAutoStart()
+            .build();
+
+        @Test
+        void startShouldNotThrowWhenCreateAKeyspaceWithAuthorizedSession(GuiceJamesServer jamesServer) {
+            assertThatCode(jamesServer::start)
+                .doesNotThrowAnyException();
+        }
+    }
+
+    @Nested
+    class CreateWhenDoesNotExistAndDoNotHaveRights {
+        @RegisterExtension
+        JamesServerExtension testExtension = new JamesServerBuilder()
+            .extension(new DockerElasticSearchExtension())
+            .extension(new CassandraExtension())
+            .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+                .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+                .overrideWith(TestJMAPServerModule.limitToTenMessages()))
+            .overrideServerModule(binder -> binder.bind(ClusterConfiguration.class)
+                .toInstance(DockerCassandraSingleton.singleton.configurationBuilder()
+                    .createKeyspace()
+                    .build()))
+            .overrideServerModule(binder -> binder.bind(KeyspacesConfiguration.class).toInstance(KeyspacesConfiguration.builder()
+                .keyspace("non_existing_keyspace")
+                .cacheKeyspace("cache_non_existing_keyspace")
+                .replicationFactor(1)
+                .disableDurableWrites()
+                .build()))
+            .disableAutoStart()
+            .build();
+
+        @Test
+        void startShouldThrowWhenAttemptToCreateAKeyspace(GuiceJamesServer jamesServer) {
+            assertThatThrownBy(jamesServer::start)
+                .isInstanceOf(Exception.class);
+        }
+    }
+
+    @Nested
+    class StartWhenKeyspaceDoesNotExist {
+        @RegisterExtension
+        JamesServerExtension testExtension = new JamesServerBuilder()
+            .extension(new DockerElasticSearchExtension())
+            .extension(new CassandraExtension())
+            .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+                .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+                .overrideWith(TestJMAPServerModule.limitToTenMessages()))
+            .overrideServerModule(binder -> binder.bind(ClusterConfiguration.class)
+                .toInstance(DockerCassandraSingleton.singleton.configurationBuilder()
+                    .build()))
+            .overrideServerModule(binder -> binder.bind(KeyspacesConfiguration.class).toInstance(KeyspacesConfiguration.builder()
+                .keyspace("non_existing_keyspace")
+                .cacheKeyspace("cache_non_existing_keyspace")
+                .replicationFactor(1)
+                .disableDurableWrites()
+                .build()))
+            .disableAutoStart()
+            .build();
+
+        @Test
+        void startShouldThrowWhenAttemptToUseANonExistingKeyspace(GuiceJamesServer jamesServer) {
+            assertThatThrownBy(jamesServer::start)
+                .isInstanceOf(Exception.class);
+        }
+    }
+}
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/KeyspacesConfigurationTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/KeyspacesConfigurationTest.java
new file mode 100644
index 0000000..27206be
--- /dev/null
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/KeyspacesConfigurationTest.java
@@ -0,0 +1,61 @@
+/****************************************************************
+ * 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.mailbox;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class KeyspacesConfigurationTest {
+    @Test
+    void buildShouldThrowWhenSameValues() {
+        assertThatThrownBy(() -> KeyspacesConfiguration.builder()
+                .keyspace("keyspace")
+                .cacheKeyspace("keyspace")
+                .build())
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    void buildShouldThrowWhenSameValuesDefaultKeyspace() {
+        assertThatThrownBy(() -> KeyspacesConfiguration.builder()
+                .cacheKeyspace("apache_james")
+                .build())
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    void buildShouldThrowWhenSameValuesDefaultCacheKeyspace() {
+        assertThatThrownBy(() -> KeyspacesConfiguration.builder()
+                .keyspace("apache_james_cache")
+                .build())
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    void buildShouldNotThrowWhenDifferentValues() {
+        assertThatCode(() -> KeyspacesConfiguration.builder()
+                .keyspace("keyspace")
+                .cacheKeyspace("keyspace2")
+                .build())
+            .doesNotThrowAnyException();
+    }
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
index 49685df..f545818 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
@@ -151,9 +151,10 @@ class FixingGhostMailboxTest {
             .addUser(BOB, BOB_SECRET);
         accessToken = authenticateJamesUser(baseUri(jmapPort), Username.of(ALICE), ALICE_SECRET);
 
-        ClusterConfiguration cassandraConfiguration = server.getProbe(CassandraProbe.class).getConfiguration();
+        CassandraProbe probe = server.getProbe(CassandraProbe.class);
+        ClusterConfiguration cassandraConfiguration = probe.getConfiguration();
         try (Cluster cluster = ClusterFactory.create(cassandraConfiguration)) {
-            try (Session session = cluster.connect(cassandraConfiguration.getKeyspace())) {
+            try (Session session = cluster.connect(probe.getMainKeyspaceConfiguration().getKeyspace())) {
                 simulateGhostMailboxBug(session);
             }
         }


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


[james-project] 09/39: JAMES-3117 Use reactor test library for testing with virtual time

Posted by bt...@apache.org.
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 9c0e636cdfe9bf96f5f48ae709c50f57e06a47af
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Wed Mar 25 18:07:35 2020 +0700

    JAMES-3117 Use reactor test library for testing with virtual time
---
 server/container/guice/guice-common/pom.xml        |  5 ++-
 .../org/apache/james/PeriodicalHealthChecks.java   | 22 ++++++-----
 .../apache/james/PeriodicalHealthChecksTest.java   | 46 ++++++++++------------
 3 files changed, 36 insertions(+), 37 deletions(-)

diff --git a/server/container/guice/guice-common/pom.xml b/server/container/guice/guice-common/pom.xml
index 4433636..e24d450 100644
--- a/server/container/guice/guice-common/pom.xml
+++ b/server/container/guice/guice-common/pom.xml
@@ -152,8 +152,9 @@
             <artifactId>guice</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.awaitility</groupId>
-            <artifactId>awaitility</artifactId>
+            <groupId>io.projectreactor</groupId>
+            <artifactId>reactor-test</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
index a18998c..86d5806 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -31,26 +31,28 @@ import org.apache.james.lifecycle.api.Startable;
 import reactor.core.Disposable;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Scheduler;
 import reactor.core.scheduler.Schedulers;
 
 public class PeriodicalHealthChecks implements Startable {
 
-    private final Flux<HealthCheck> healthChecks;
-    private final long initialDelay;
-    private final long period;
+    private final Set<HealthCheck> healthChecks;
+    private final Scheduler scheduler;
+    private final Duration period;
     private Disposable disposable;
 
     @Inject
-    PeriodicalHealthChecks(Set<HealthCheck> healthChecks, PeriodicalHealthChecksConfiguration config) {
-        this.healthChecks = Flux.fromIterable(healthChecks);
-        this.initialDelay = config.getInitialDelay();
+    PeriodicalHealthChecks(Set<HealthCheck> healthChecks, Scheduler scheduler, PeriodicalHealthChecksConfiguration config) {
+        this.healthChecks = healthChecks;
+        this.scheduler = scheduler;
         this.period = config.getPeriod();
     }
 
     public void start() {
-        disposable = Flux.interval(Duration.ofSeconds(initialDelay), Duration.ofSeconds(period))
-            .flatMap(any ->
-                healthChecks.flatMap(healthCheck -> Mono.just(healthCheck.check())))
+        disposable = Flux.interval(period, scheduler)
+            .flatMap(any -> Flux.fromIterable(healthChecks)
+                .flatMap(healthCheck ->
+                    Mono.fromCallable(healthCheck::check)))
             .subscribeOn(Schedulers.elastic())
             .subscribe();
     }
@@ -59,4 +61,4 @@ public class PeriodicalHealthChecks implements Startable {
     public void stop() {
         disposable.dispose();
     }
-}
+}
\ No newline at end of file
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
index 8253899..dfc241f 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
@@ -25,32 +25,28 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.time.Duration;
 
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
 import org.apache.james.mailbox.events.EventDeadLettersHealthCheck;
-import org.awaitility.Awaitility;
-import org.awaitility.Duration;
-import org.awaitility.core.ConditionFactory;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
-public class PeriodicalHealthChecksTest {
+import com.google.common.collect.ImmutableSet;
+
+import reactor.test.scheduler.VirtualTimeScheduler;
 
-    private static final long INITIAL_DELAY = 1;
-    private static final long PERIOD = 1;
-    private static final ConditionFactory AWAIT = Awaitility.await()
-        .atMost(Duration.TEN_SECONDS)
-        .with()
-        .pollInterval(Duration.ONE_SECOND);
+public class PeriodicalHealthChecksTest {
 
+    private static final long PERIOD = 10;
+    private static final int EXPECTED_INVOKED_TIME = 10;
     private HealthCheck mockHealthCheck1;
     private HealthCheck mockHealthCheck2;
+    private VirtualTimeScheduler scheduler;
     private PeriodicalHealthChecks testee;
 
     @BeforeEach
@@ -60,11 +56,10 @@ public class PeriodicalHealthChecksTest {
         when(mockHealthCheck1.check()).thenReturn(Result.healthy(new ComponentName("mockHealthCheck1")));
         when(mockHealthCheck2.check()).thenReturn(Result.healthy(new ComponentName("mockHealthCheck2")));
 
-        Set<HealthCheck> healthCheckSet = new HashSet<>();
-        healthCheckSet.add(mockHealthCheck1);
-        healthCheckSet.add(mockHealthCheck2);
-
-        testee = new PeriodicalHealthChecks(healthCheckSet, new PeriodicalHealthChecksConfiguration(INITIAL_DELAY, PERIOD));
+        scheduler = VirtualTimeScheduler.getOrSet();
+        testee = new PeriodicalHealthChecks(ImmutableSet.of(mockHealthCheck1, mockHealthCheck2),
+            scheduler,
+            new PeriodicalHealthChecksConfiguration(Duration.ofMillis(PERIOD)));
         testee.start();
     }
 
@@ -72,22 +67,23 @@ public class PeriodicalHealthChecksTest {
     void tearDown() {
         testee.stop();
     }
-
+    
     @Test
     void startShouldCallHealthCheckAtLeastOnce() {
-        AWAIT.untilAsserted(() -> verify(mockHealthCheck1, atLeast(1)).check());
+        scheduler.advanceTimeBy(Duration.ofMillis(PERIOD));
+        verify(mockHealthCheck1, atLeast(1)).check();
     }
 
     @Test
     void startShouldCallHealthCheckMultipleTimes() {
-        AWAIT.untilAsserted(() -> verify(mockHealthCheck1, times(5)).check());
+        scheduler.advanceTimeBy(Duration.ofMillis(PERIOD * EXPECTED_INVOKED_TIME));
+        verify(mockHealthCheck1, times(EXPECTED_INVOKED_TIME)).check();
     }
 
     @Test
     void startShouldCallAllHealthChecks() {
-        AWAIT.untilAsserted(() -> {
-            verify(mockHealthCheck1, atLeast(5)).check();
-            verify(mockHealthCheck2, atLeast(5)).check();
-        });
+        scheduler.advanceTimeBy(Duration.ofMillis(PERIOD * EXPECTED_INVOKED_TIME));
+        verify(mockHealthCheck1, times(EXPECTED_INVOKED_TIME)).check();
+        verify(mockHealthCheck2, times(EXPECTED_INVOKED_TIME)).check();
     }
-}
+}
\ No newline at end of file


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


[james-project] 05/39: JAMES-3117 Refactor PeriodicalHealthChecks/Test for using configuration

Posted by bt...@apache.org.
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 b7fb64afc136471745beec7d814c9ba43f4f4cf4
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Mar 23 10:03:06 2020 +0700

    JAMES-3117 Refactor PeriodicalHealthChecks/Test for using configuration
---
 .../main/java/org/apache/james/PeriodicalHealthChecks.java | 14 ++++++--------
 .../java/org/apache/james/PeriodicalHealthChecksTest.java  |  8 +++++---
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
index 91d0872..a18998c 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -27,8 +27,6 @@ import javax.inject.Inject;
 
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.lifecycle.api.Startable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import reactor.core.Disposable;
 import reactor.core.publisher.Flux;
@@ -37,20 +35,20 @@ import reactor.core.scheduler.Schedulers;
 
 public class PeriodicalHealthChecks implements Startable {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(PeriodicalHealthChecks.class);
-    private static final long INITIAL_DELAY = 1;
-    private static final long PERIOD = 1;
-
     private final Flux<HealthCheck> healthChecks;
+    private final long initialDelay;
+    private final long period;
     private Disposable disposable;
 
     @Inject
-    PeriodicalHealthChecks(Set<HealthCheck> healthChecks) {
+    PeriodicalHealthChecks(Set<HealthCheck> healthChecks, PeriodicalHealthChecksConfiguration config) {
         this.healthChecks = Flux.fromIterable(healthChecks);
+        this.initialDelay = config.getInitialDelay();
+        this.period = config.getPeriod();
     }
 
     public void start() {
-        disposable = Flux.interval(Duration.ofSeconds(INITIAL_DELAY), Duration.ofSeconds(PERIOD))
+        disposable = Flux.interval(Duration.ofSeconds(initialDelay), Duration.ofSeconds(period))
             .flatMap(any ->
                 healthChecks.flatMap(healthCheck -> Mono.just(healthCheck.check())))
             .subscribeOn(Schedulers.elastic())
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
index f029333..8253899 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
@@ -42,6 +42,8 @@ import org.mockito.Mockito;
 
 public class PeriodicalHealthChecksTest {
 
+    private static final long INITIAL_DELAY = 1;
+    private static final long PERIOD = 1;
     private static final ConditionFactory AWAIT = Awaitility.await()
         .atMost(Duration.TEN_SECONDS)
         .with()
@@ -55,14 +57,14 @@ public class PeriodicalHealthChecksTest {
     void setUp() {
         mockHealthCheck1 = Mockito.mock(EventDeadLettersHealthCheck.class);
         mockHealthCheck2 = Mockito.mock(GuiceLifecycleHealthCheck.class);
-        when(mockHealthCheck1.check()).thenReturn(Result.healthy(new ComponentName("mock1")));
-        when(mockHealthCheck2.check()).thenReturn(Result.healthy(new ComponentName("mock2")));
+        when(mockHealthCheck1.check()).thenReturn(Result.healthy(new ComponentName("mockHealthCheck1")));
+        when(mockHealthCheck2.check()).thenReturn(Result.healthy(new ComponentName("mockHealthCheck2")));
 
         Set<HealthCheck> healthCheckSet = new HashSet<>();
         healthCheckSet.add(mockHealthCheck1);
         healthCheckSet.add(mockHealthCheck2);
 
-        testee = new PeriodicalHealthChecks(healthCheckSet);
+        testee = new PeriodicalHealthChecks(healthCheckSet, new PeriodicalHealthChecksConfiguration(INITIAL_DELAY, PERIOD));
         testee.start();
     }
 


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


[james-project] 18/39: JAMES-3117 Add logging for callers of HealthChecks

Posted by bt...@apache.org.
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 f98c58f7f7f7dad2c20654e5367f8462917aae33
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Wed Apr 1 02:16:37 2020 +0700

    JAMES-3117 Add logging for callers of HealthChecks
---
 .../org/apache/james/PeriodicalHealthChecks.java   | 32 ++++++++++++++++------
 .../james/webadmin/routes/HealthCheckRoutes.java   |  2 +-
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
index 11a82d6..acede14 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -78,25 +78,39 @@ public class PeriodicalHealthChecks implements Startable {
             case HEALTHY:
                 break;
             case DEGRADED:
-                LOGGER.warn("DEGRADED: {} : {}", result.getComponentName().getName(), result.getCause());
+                LOGGER.warn("DEGRADED: {} : {}",
+                    result.getComponentName().getName(),
+                    result.getCause().orElse(""));
                 break;
             case UNHEALTHY:
-                if (result.getError().isPresent()) {
-                    LOGGER.error("UNHEALTHY: {} : {} : {}", result.getComponentName().getName(), result.getCause(), result.getError().get());
-                    break;
-                }
-
-                LOGGER.error("UNHEALTHY: {} : {}", result.getComponentName().getName(), result.getCause());
+                logUnhealthy(result);
                 break;
         }
     }
 
+    private void logUnhealthy(Result result) {
+        if (result.getError().isPresent()) {
+            LOGGER.error("UNHEALTHY: {} : {}",
+                result.getComponentName().getName(),
+                result.getCause(),
+                result.getError().get());
+        } else {
+            LOGGER.error("UNHEALTHY: {} : {}",
+                result.getComponentName().getName(),
+                result.getCause());
+        }
+    }
+
     private void logError(Throwable error, Object triggeringValue) {
         if (triggeringValue instanceof Result) {
             Result result = (Result) triggeringValue;
-            LOGGER.error("HealthCheck error for: {}, Cause: {}", result.getComponentName(), error);
+            LOGGER.error("HealthCheck error for: {}, Cause: {}",
+                result.getComponentName(),
+                error);
             return;
         }
-        LOGGER.error("HealthCheck error. Triggering value: {}, Cause: {}", triggeringValue, error);
+        LOGGER.error("HealthCheck error. Triggering value: {}, Cause: {}",
+            triggeringValue,
+            error);
     }
 }
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
index 3f422ba..12051af 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
@@ -153,7 +153,7 @@ public class HealthCheckRoutes implements PublicRoutes {
         switch (result.getStatus()) {
         case UNHEALTHY:
             if (result.getError().isPresent()) {
-                LOGGER.error("HealthCheck failed for {} : {} : {}",
+                LOGGER.error("HealthCheck failed for {} : {}",
                     result.getComponentName().getName(),
                     result.getCause().orElse(""),
                     result.getError().get());


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


[james-project] 21/39: JAMES-3137 Create a cache Keyspace

Posted by bt...@apache.org.
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 a6ada75cdf365b611eec1a8933e05ade18557ec9
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 27 10:15:45 2020 +0700

    JAMES-3137 Create a cache Keyspace
---
 .../backends/cassandra/init/KeyspaceFactory.java   | 20 +++++++++++++++----
 .../init/configuration/ClusterConfiguration.java   | 23 +++++++++++++++++++++-
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java
index ce6a227..b931272 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/KeyspaceFactory.java
@@ -35,27 +35,39 @@ public class KeyspaceFactory {
     private static final String SYSTEM_SCHEMA = "system_schema";
     private static final String KEYSPACES = "keyspaces";
     private static final String KEYSPACE_NAME = "keyspace_name";
+    private static final int CACHE_REPLICATION_FACTOR = 1;
 
     public static void createKeyspace(ClusterConfiguration clusterConfiguration, Cluster cluster) {
         if (clusterConfiguration.shouldCreateKeyspace()) {
             doCreateKeyspace(clusterConfiguration, cluster);
+            doCreateCacheKeyspace(clusterConfiguration, cluster);
         }
     }
 
     private static void doCreateKeyspace(ClusterConfiguration clusterConfiguration, Cluster cluster) {
+        createKeyspace(cluster, clusterConfiguration.getKeyspace(),
+            clusterConfiguration.getReplicationFactor(),
+            clusterConfiguration.isDurableWrites());
+    }
+
+    private static void createKeyspace(Cluster cluster, String keyspace, int replicationFactor, boolean durableWrites) {
         try (Session session = cluster.connect()) {
-            if (!keyspaceExist(cluster, clusterConfiguration.getKeyspace())) {
-                session.execute(SchemaBuilder.createKeyspace(clusterConfiguration.getKeyspace())
+            if (!keyspaceExist(cluster, keyspace)) {
+                session.execute(SchemaBuilder.createKeyspace(keyspace)
                     .with()
                     .replication(ImmutableMap.<String, Object>builder()
                         .put("class", "SimpleStrategy")
-                        .put("replication_factor", clusterConfiguration.getReplicationFactor())
+                        .put("replication_factor", replicationFactor)
                         .build())
-                    .durableWrites(clusterConfiguration.isDurableWrites()));
+                    .durableWrites(durableWrites));
             }
         }
     }
 
+    private static void doCreateCacheKeyspace(ClusterConfiguration clusterConfiguration, Cluster cluster) {
+        createKeyspace(cluster, clusterConfiguration.getCacheKeyspace(), CACHE_REPLICATION_FACTOR, clusterConfiguration.isDurableWrites());
+    }
+
     @VisibleForTesting
     public static boolean keyspaceExist(Cluster cluster, String keyspaceName) {
         try (Session session = cluster.connect(SYSTEM_SCHEMA)) {
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java
index 09a10b0..cb56e8a 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/ClusterConfiguration.java
@@ -39,6 +39,7 @@ public class ClusterConfiguration {
         private ImmutableList.Builder<Host> hosts;
         private boolean createKeyspace;
         private Optional<String> keyspace;
+        private Optional<String> cacheKeyspace;
         private Optional<Integer> replicationFactor;
         private Optional<Integer> minDelay;
         private Optional<Integer> maxRetry;
@@ -55,6 +56,7 @@ public class ClusterConfiguration {
             hosts = ImmutableList.builder();
             createKeyspace = false;
             keyspace = Optional.empty();
+            cacheKeyspace = Optional.empty();
             replicationFactor = Optional.empty();
             minDelay = Optional.empty();
             maxRetry = Optional.empty();
@@ -93,6 +95,15 @@ public class ClusterConfiguration {
             return this;
         }
 
+        public Builder cacheKeyspace(Optional<String> cacheKeyspace) {
+            this.keyspace = keyspace;
+            return this;
+        }
+
+        public Builder cacheKeyspace(String cacheKeyspace) {
+            return cacheKeyspace(Optional.of(cacheKeyspace));
+        }
+
         public Builder keyspace(String keyspace) {
             return keyspace(Optional.of(keyspace));
         }
@@ -198,6 +209,7 @@ public class ClusterConfiguration {
                 hosts.build(),
                 createKeyspace,
                 keyspace.orElse(DEFAULT_KEYSPACE),
+                cacheKeyspace.orElse(DEFAULT_CACHE_KEYSPACE),
                 replicationFactor.orElse(DEFAULT_REPLICATION_FACTOR),
                 minDelay.orElse(DEFAULT_CONNECTION_MIN_DELAY),
                 maxRetry.orElse(DEFAULT_CONNECTION_MAX_RETRIES),
@@ -215,6 +227,7 @@ public class ClusterConfiguration {
     private static final String CASSANDRA_NODES = "cassandra.nodes";
     public static final String CASSANDRA_CREATE_KEYSPACE = "cassandra.keyspace.create";
     public static final String CASSANDRA_KEYSPACE = "cassandra.keyspace";
+    public static final String CASSANDRA_CACHE_KEYSPACE = "cassandra.keyspace.cache";
     public static final String CASSANDRA_USER = "cassandra.user";
     public static final String CASSANDRA_PASSWORD = "cassandra.password";
     public static final String CASSANDRA_SSL = "cassandra.ssl";
@@ -225,6 +238,7 @@ public class ClusterConfiguration {
     public static final String CONNECTION_RETRY_MIN_DELAY = "cassandra.retryConnection.minDelay";
 
     private static final String DEFAULT_KEYSPACE = "apache_james";
+    private static final String DEFAULT_CACHE_KEYSPACE = "apache_james_cache";
     private static final int DEFAULT_REPLICATION_FACTOR = 1;
     private static final int DEFAULT_CONNECTION_MAX_RETRIES = 10;
     private static final int DEFAULT_CONNECTION_MIN_DELAY = 5000;
@@ -246,6 +260,7 @@ public class ClusterConfiguration {
         ClusterConfiguration.Builder builder = ClusterConfiguration.builder()
             .hosts(listCassandraServers(configuration))
             .keyspace(Optional.ofNullable(configuration.getString(CASSANDRA_KEYSPACE, null)))
+            .cacheKeyspace(Optional.ofNullable(configuration.getString(CASSANDRA_CACHE_KEYSPACE, null)))
             .replicationFactor(Optional.ofNullable(configuration.getInteger(REPLICATION_FACTOR, null)))
             .minDelay(Optional.ofNullable(configuration.getInteger(CONNECTION_RETRY_MIN_DELAY, null)))
             .maxRetry(Optional.ofNullable(configuration.getInteger(CONNECTION_MAX_RETRY, null)))
@@ -304,6 +319,7 @@ public class ClusterConfiguration {
     private final List<Host> hosts;
     private final boolean createKeyspace;
     private final String keyspace;
+    private final String cacheKeyspace;
     private final int replicationFactor;
     private final int minDelay;
     private final int maxRetry;
@@ -316,13 +332,14 @@ public class ClusterConfiguration {
     private final Optional<String> password;
     private final boolean durableWrites;
 
-    public ClusterConfiguration(List<Host> hosts, boolean createKeyspace, String keyspace, int replicationFactor, int minDelay, int maxRetry,
+    public ClusterConfiguration(List<Host> hosts, boolean createKeyspace, String keyspace, String cacheKeyspace, int replicationFactor, int minDelay, int maxRetry,
                                 Optional<QueryLoggerConfiguration> queryLoggerConfiguration, Optional<PoolingOptions> poolingOptions,
                                 int readTimeoutMillis, int connectTimeoutMillis, boolean useSsl, Optional<String> username,
                                 Optional<String> password, boolean durableWrites) {
         this.hosts = hosts;
         this.createKeyspace = createKeyspace;
         this.keyspace = keyspace;
+        this.cacheKeyspace = cacheKeyspace;
         this.replicationFactor = replicationFactor;
         this.minDelay = minDelay;
         this.maxRetry = maxRetry;
@@ -352,6 +369,10 @@ public class ClusterConfiguration {
         return keyspace;
     }
 
+    public String getCacheKeyspace() {
+        return cacheKeyspace;
+    }
+
     public int getReplicationFactor() {
         return replicationFactor;
     }


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


[james-project] 03/39: JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test

Posted by bt...@apache.org.
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 3b61bfa5c799c2ce04179a3d7f432da2f30920b4
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Mar 23 09:56:04 2020 +0700

    JAMES-3117 Add PeriodicalHealthChecksConfiguration/Test
---
 .../james/PeriodicalHealthChecksConfiguration.java | 114 ++++++++++++++
 .../PeriodicalHealthChecksConfigurationTest.java   | 164 +++++++++++++++++++++
 2 files changed, 278 insertions(+)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
new file mode 100644
index 0000000..e8e052f
--- /dev/null
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecksConfiguration.java
@@ -0,0 +1,114 @@
+/****************************************************************
+ * 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;
+
+import java.util.Objects;
+
+import org.apache.commons.configuration2.Configuration;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
+public class PeriodicalHealthChecksConfiguration {
+
+    static final String HEALTH_CHECK_INITIAL_DELAY = "healthcheck.initial.delay";
+    static final String HEALTH_CHECK_PERIOD = "healthcheck.period";
+    static final long DEFAULT_HEALTH_CHECK_INITIAL_DELAY = 60;
+    static final long DEFAULT_HEALTH_CHECK_PERIOD = 60;
+    static final long ZERO = 0;
+    public static final PeriodicalHealthChecksConfiguration DEFAULT_CONFIGURATION = builder()
+        .initialDelay(DEFAULT_HEALTH_CHECK_INITIAL_DELAY)
+        .period(DEFAULT_HEALTH_CHECK_PERIOD)
+        .build();
+
+    public interface Builder {
+
+        @FunctionalInterface
+        interface RequiredInitialDelay {
+            RequiredPeriod initialDelay(long initialDelay);
+        }
+
+        @FunctionalInterface
+        interface RequiredPeriod {
+            ReadyToBuild period(long period);
+        }
+
+        class ReadyToBuild {
+            private final long initialDelay;
+            private final long period;
+
+            ReadyToBuild(long initialDelay, long period) {
+                this.initialDelay = initialDelay;
+                this.period = period;
+            }
+
+            PeriodicalHealthChecksConfiguration build() {
+                Preconditions.checkArgument(initialDelay > ZERO, "'initialDelay' must be positive");
+                Preconditions.checkArgument(period > ZERO, "'period' must be positive");
+
+                return new PeriodicalHealthChecksConfiguration(initialDelay, period);
+            }
+        }
+    }
+
+    public static Builder.RequiredInitialDelay builder() {
+        return initialDelay -> period -> new Builder.ReadyToBuild(initialDelay, period);
+    }
+
+    public static PeriodicalHealthChecksConfiguration from(Configuration configuration) {
+        return builder()
+            .initialDelay(configuration.getLong(HEALTH_CHECK_INITIAL_DELAY, DEFAULT_HEALTH_CHECK_INITIAL_DELAY))
+            .period(configuration.getLong(HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD))
+            .build();
+    }
+
+    private final long initialDelay;
+    private final long period;
+
+    @VisibleForTesting
+    PeriodicalHealthChecksConfiguration(long initialDelay, long period) {
+        this.initialDelay = initialDelay;
+        this.period = period;
+    }
+
+    public long getInitialDelay() {
+        return initialDelay;
+    }
+
+    public long getPeriod() {
+        return period;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof PeriodicalHealthChecksConfiguration) {
+            PeriodicalHealthChecksConfiguration that = (PeriodicalHealthChecksConfiguration) o;
+
+            return Objects.equals(this.initialDelay, that.initialDelay)
+                && Objects.equals(this.period, that.period);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(initialDelay, period);
+    }
+}
\ No newline at end of file
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
new file mode 100644
index 0000000..a51945a
--- /dev/null
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksConfigurationTest.java
@@ -0,0 +1,164 @@
+/****************************************************************
+ * 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;
+
+import static org.apache.james.PeriodicalHealthChecksConfiguration.DEFAULT_HEALTH_CHECK_INITIAL_DELAY;
+import static org.apache.james.PeriodicalHealthChecksConfiguration.DEFAULT_HEALTH_CHECK_PERIOD;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.apache.commons.configuration2.ex.ConversionException;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class PeriodicalHealthChecksConfigurationTest {
+
+    private static final String EMPTY_STRING = "";
+    private static final String RANDOM_STRING = "abcdsfsfs";
+    private static final long NEGATIVE_NUMBER = -1;
+    private static final long INITIAL_DELAY = 10;
+    private static final long PERIOD = 5;
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(PeriodicalHealthChecksConfiguration.class)
+            .verify();
+    }
+
+    @Test
+    void fromShouldThrowWhenInitialDelayIsEmpty() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, EMPTY_STRING);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, DEFAULT_HEALTH_CHECK_PERIOD);
+
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration. from(configuration))
+            .isInstanceOf(ConversionException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenPeriodIsEmpty() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, DEFAULT_HEALTH_CHECK_INITIAL_DELAY);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, EMPTY_STRING);
+
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
+            .isInstanceOf(ConversionException.class);
+    }
+
+    @Test
+    void fromShouldReturnConfigurationWithDefaultValueWhenInitialDelayIsMissing() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .initialDelay(DEFAULT_HEALTH_CHECK_INITIAL_DELAY)
+            .period(PERIOD)
+            .build());
+    }
+
+    @Test
+    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsMissing() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
+
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .initialDelay(INITIAL_DELAY)
+            .period(DEFAULT_HEALTH_CHECK_PERIOD)
+            .build());
+    }
+
+    @Test
+    void fromShouldReturnConfigurationWithDefaultValueWhenInitialDelayIsNull() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, null);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .initialDelay(DEFAULT_HEALTH_CHECK_INITIAL_DELAY)
+            .period(PERIOD)
+            .build());
+    }
+
+    @Test
+    void fromShouldReturnConfigurationWithDefaultValueWhenPeriodIsNull() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, null);
+
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .initialDelay(INITIAL_DELAY)
+            .period(DEFAULT_HEALTH_CHECK_PERIOD)
+            .build());
+    }
+
+    @Test
+    void fromShouldThrowWhenInitialDelayIsNotANumber() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, RANDOM_STRING);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
+            .isInstanceOf(ConversionException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenInitialDelayIsNegative() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, NEGATIVE_NUMBER);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenPeriodIsNegative() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, NEGATIVE_NUMBER);
+
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenPeriodIsNotANumber() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, RANDOM_STRING);
+
+        assertThatThrownBy(() -> PeriodicalHealthChecksConfiguration.from(configuration))
+            .isInstanceOf(ConversionException.class);
+    }
+
+    @Test
+    void fromShouldReturnProvidedConfiguration() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_INITIAL_DELAY, INITIAL_DELAY);
+        configuration.addProperty(PeriodicalHealthChecksConfiguration.HEALTH_CHECK_PERIOD, PERIOD);
+
+        assertThat(PeriodicalHealthChecksConfiguration.from(configuration)).isEqualTo(PeriodicalHealthChecksConfiguration.builder()
+            .initialDelay(INITIAL_DELAY)
+            .period(PERIOD)
+            .build());
+    }
+}


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


[james-project] 39/39: JAMES-2897 Insert statements needs ifNotExist when LWT is used in inserts

Posted by bt...@apache.org.
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 ee0028e8bb15aab16315751836c31560c85681c5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Apr 15 22:31:33 2020 +0700

    JAMES-2897 Insert statements needs ifNotExist when LWT is used in inserts
    
    This is an example developped in
    https://docs.datastax.com/en/ddac/doc/datastax_enterprise/dbInternals/dbIntLtwtTransactions.html
    For example, the following series of operations can fail:
    
    ```
    DELETE ...
    INSERT .... IF NOT EXISTS
    SELECT ....
    ```
    
    The following series of operations will work:
    
    ```
    DELETE ... IF EXISTS
    INSERT .... IF NOT EXISTS
    SELECT .....
    ```
---
 .../james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java   | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
index 7eb2062..0725bcc 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
@@ -109,7 +109,8 @@ public class CassandraMessageIdToImapUidDAO {
                 .value(RECENT, bindMarker(RECENT))
                 .value(SEEN, bindMarker(SEEN))
                 .value(USER, bindMarker(USER))
-                .value(USER_FLAGS, bindMarker(USER_FLAGS)));
+                .value(USER_FLAGS, bindMarker(USER_FLAGS))
+                .ifNotExists());
     }
 
     private PreparedStatement prepareUpdate(Session session) {


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


[james-project] 25/39: JAMES-3134 Check RabbitMQ version at start up

Posted by bt...@apache.org.
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 4ad5014a92c16f79389013e8dec46abef828fc00
Author: Gautier DI FOLCO <gd...@linagora.com>
AuthorDate: Mon Apr 6 17:45:26 2020 +0200

    JAMES-3134 Check RabbitMQ version at start up
---
 .../backends/rabbitmq/RabbitMQHealthCheck.java     | 15 ++++
 .../backends/rabbitmq/RabbitMQServerVersion.java   | 70 ++++++++++++++++
 .../backends/rabbitmq/SimpleConnectionPool.java    | 13 +++
 .../rabbitmq/RabbitMQServerVersionTest.java        | 96 ++++++++++++++++++++++
 4 files changed, 194 insertions(+)

diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
index 09a03df..a5e6d00 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.backends.rabbitmq;
 
+import java.util.Optional;
+
 import javax.inject.Inject;
 
 import org.apache.james.core.healthcheck.ComponentName;
@@ -26,6 +28,7 @@ import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
 
 public class RabbitMQHealthCheck implements HealthCheck {
+    private static final RabbitMQServerVersion MINIMAL_VERSION = RabbitMQServerVersion.of("3.8.1");
     private static final ComponentName COMPONENT_NAME = new ComponentName("RabbitMQ backend");
 
     private final SimpleConnectionPool connectionPool;
@@ -46,6 +49,18 @@ public class RabbitMQHealthCheck implements HealthCheck {
     public Result check() {
         try {
             if (connectionPool.tryConnection() && rabbitChannelPoolImpl.tryChannel()) {
+                Optional<RabbitMQServerVersion> version = connectionPool.version();
+                boolean isCompatible = version
+                    .map(fetchedVersion -> fetchedVersion.isAtLeast(MINIMAL_VERSION))
+                    .orElse(false);
+                if (!isCompatible) {
+                    String versionCompatibilityError = String.format(
+                        "RabbitMQ version(%s) is not compatible with the required one(%s)",
+                        version.map(RabbitMQServerVersion::asString).orElse("no versions fetched"),
+                        MINIMAL_VERSION.asString());
+                    return Result.unhealthy(COMPONENT_NAME, versionCompatibilityError);
+                }
+
                 return Result.healthy(COMPONENT_NAME);
             } else {
                 return Result.unhealthy(COMPONENT_NAME, "The created connection was not opened");
diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersion.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersion.java
new file mode 100644
index 0000000..a0500c8
--- /dev/null
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersion.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * 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.backends.rabbitmq;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.util.VersionUtil;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+
+public class RabbitMQServerVersion {
+    public static RabbitMQServerVersion of(String input) {
+        return new RabbitMQServerVersion(VersionUtil.parseVersion(input, "rabbitmq", "version"));
+    }
+
+    private final Version version;
+
+    @VisibleForTesting
+    RabbitMQServerVersion(Version version) {
+        this.version = version;
+    }
+
+    public boolean isAtLeast(RabbitMQServerVersion other) {
+        return version.compareTo(other.version) >= 0;
+    }
+
+    public String asString() {
+        return version.toFullString();
+    }
+
+    @Override
+    public final boolean equals(Object other) {
+        if (other instanceof RabbitMQServerVersion) {
+            RabbitMQServerVersion that = (RabbitMQServerVersion) other;
+            return Objects.equals(version, that.version);
+        }
+
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(version);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("version", version)
+            .toString();
+    }
+}
diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java
index 0354002..6aa1ba5 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/SimpleConnectionPool.java
@@ -88,4 +88,17 @@ public class SimpleConnectionPool implements AutoCloseable {
             return false;
         }
     }
+
+    public Optional<RabbitMQServerVersion> version() {
+        try {
+            return getOpenConnection()
+                .map(Connection::getServerProperties)
+                .flatMap(serverProperties -> Mono.justOrEmpty(serverProperties.get("version")))
+                .map(Object::toString)
+                .map(RabbitMQServerVersion::of)
+                .blockOptional(Duration.ofSeconds(1));
+        } catch (Throwable t) {
+            return Optional.empty();
+        }
+    }
 }
diff --git a/backends-common/rabbitmq/src/test/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersionTest.java b/backends-common/rabbitmq/src/test/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersionTest.java
new file mode 100644
index 0000000..a90c9c9
--- /dev/null
+++ b/backends-common/rabbitmq/src/test/java/org/apache/james/backends/rabbitmq/RabbitMQServerVersionTest.java
@@ -0,0 +1,96 @@
+/****************************************************************
+ * 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.backends.rabbitmq;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import com.fasterxml.jackson.core.Version;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class RabbitMQServerVersionTest {
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(RabbitMQServerVersion.class)
+            .verify();
+    }
+
+    @ParameterizedTest
+    @MethodSource("versionsToParse")
+    void shouldParseVersion(String input, Version expected) {
+        assertThat(RabbitMQServerVersion.of(input)).isEqualTo(new RabbitMQServerVersion(expected));
+    }
+
+    @ParameterizedTest
+    @MethodSource("versionsComparison")
+    void shouldBeAtLeast(String lower, String upper) {
+        RabbitMQServerVersion lowerVersion = RabbitMQServerVersion.of(lower);
+        RabbitMQServerVersion upperVersion = RabbitMQServerVersion.of(upper);
+        assertThat(upperVersion.isAtLeast(lowerVersion)).isTrue();
+    }
+
+    @ParameterizedTest
+    @MethodSource("versionsReversedComparison")
+    void shouldNotBeAtLeastWhenReversed(String lower, String upper) {
+        RabbitMQServerVersion lowerVersion = RabbitMQServerVersion.of(lower);
+        RabbitMQServerVersion upperVersion = RabbitMQServerVersion.of(upper);
+        assertThat(lowerVersion.isAtLeast(upperVersion)).isFalse();
+    }
+
+    static Stream<Arguments> versionsToParse() {
+        return Stream.of(
+            Arguments.of("3.8.1", version(3, 8, 1)),
+            Arguments.of("3.18.1", version(3, 18, 1)),
+            Arguments.of("3.8.", version(3, 8, 0)),
+            Arguments.of("3.8.0+beta.4.38.g33a7f97", version(3, 8, 0)),
+            Arguments.of("3.7.1-alpha.40", version(3, 7, 1)),
+            Arguments.of("3.7.0~alpha.449-1", version(3, 7, 0))
+        );
+    }
+
+    static Stream<Arguments> versionsComparison() {
+        return Stream.of(
+            Arguments.of("3.8.1", "3.8.1"),
+            Arguments.of("3.18.1", "3.18.1"),
+            Arguments.of("3.8.", "3.8.0"),
+            Arguments.of("3.7.5", "3.8.1"),
+            Arguments.of("3.8", "3.8.1"),
+            Arguments.of("3.8.0", "4.0.0")
+        );
+    }
+
+    static Stream<Arguments> versionsReversedComparison() {
+        return Stream.of(
+            Arguments.of("3.7.5", "3.8.1"),
+            Arguments.of("3.8", "3.8.1"),
+            Arguments.of("3.8.0", "4.0.0")
+        );
+    }
+
+    private static Version version(int major, int minor, int patch) {
+        return new Version(major, minor, patch, "", "rabbitmq", "version");
+    }
+}
\ No newline at end of file


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


[james-project] 07/39: JAMES-3117 Update changelog

Posted by bt...@apache.org.
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 d3c90b45664599526b167d01e55b1d9ab7ba323c
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Mar 23 10:05:26 2020 +0700

    JAMES-3117 Update changelog
---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30261cb..0996a0d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ of tasks being currently executed.
 - JAMES-3058 WebAdmin offline task to correct mailbox inconsistencies on top of Cassandra products
 - JAMES-3105 WebAdmin offline task to recompute mailbox counters on top of Cassandra products
 - JAMES-3072 Webadmin endpoint to export mailbox backup
+- JAMES-3117 Add PeriodicalHealthChecks for periodical calling all health checks
 
 ### Changed
 - Multiple changes have been made to enhance ElasticSearch performance:


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


[james-project] 01/39: JAMES-3117 Add logs for HealthChecks for unhealthy/degraded status

Posted by bt...@apache.org.
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 58322e72b45fe3270810315881b2bf38ed3b7861
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Tue Mar 17 10:34:44 2020 +0700

    JAMES-3117 Add logs for HealthChecks for unhealthy/degraded status
---
 .../java/org/apache/james/backends/es/ElasticSearchHealthCheck.java   | 1 +
 .../java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java  | 2 +-
 .../org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java  | 3 ++-
 .../src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java     | 4 ++++
 .../jmap/api/projections/MessageFastViewProjectionHealthCheck.java    | 4 ++++
 .../main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java    | 4 +++-
 6 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
index 58cd813..6182f01 100644
--- a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
+++ b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchHealthCheck.java
@@ -81,6 +81,7 @@ public class ElasticSearchHealthCheck implements HealthCheck {
             case YELLOW:
                 return Result.healthy(COMPONENT_NAME);
             case RED:
+                LOGGER.error("ElasticSearchCluster return RED status");
                 return Result.unhealthy(COMPONENT_NAME, response.getClusterName() + " status is RED");
             default:
                 throw new NotImplementedException("Un-handled ElasticSearch cluster status");
diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
index 7946173..c50f99e 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQHealthCheck.java
@@ -52,7 +52,7 @@ public class RabbitMQHealthCheck implements HealthCheck {
                 return Result.healthy(COMPONENT_NAME);
             } else {
                 String message = "The created connection was not opened";
-                LOGGER.error(message);
+                LOGGER.error("Unhealthy RabbitMQ instances: {}", message);
                 return Result.unhealthy(COMPONENT_NAME, message);
             }
         } catch (Exception e) {
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java b/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
index 2d20af9..50a0a8b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/events/EventDeadLettersHealthCheck.java
@@ -49,12 +49,13 @@ public class EventDeadLettersHealthCheck implements HealthCheck {
             boolean containEvents = eventDeadLetters.containEvents().block();
 
             if (containEvents) {
+                LOGGER.warn("EventDeadLetters is not empty");
                 return Result.degraded(COMPONENT_NAME, "EventDeadLetters contain events. This might indicate transient failure on mailbox event processing.");
             }
 
             return Result.healthy(COMPONENT_NAME);
         } catch (Exception e) {
-            LOGGER.error("Error checking EventDeadLettersHealthCheck", e);
+            LOGGER.error("EventDeadLettersHealthCheck threw an exception", e);
             return Result.unhealthy(COMPONENT_NAME, e.getMessage());
         }
     }
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java b/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java
index b7a6fe4..2ee8e9e 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/GuiceLifecycleHealthCheck.java
@@ -24,8 +24,11 @@ import javax.inject.Inject;
 import org.apache.james.core.healthcheck.ComponentName;
 import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class GuiceLifecycleHealthCheck implements HealthCheck {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GuiceLifecycleHealthCheck.class);
     private final IsStartedProbe probe;
 
     @Inject
@@ -43,6 +46,7 @@ public class GuiceLifecycleHealthCheck implements HealthCheck {
         if (probe.isStarted()) {
             return Result.healthy(componentName());
         } else {
+            LOGGER.error("James server is not started");
             return Result.unhealthy(componentName(), "James server is not started.");
         }
     }
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java
index de18851..b2877ad 100644
--- a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/MessageFastViewProjectionHealthCheck.java
@@ -29,9 +29,12 @@ import org.apache.james.core.healthcheck.HealthCheck;
 import org.apache.james.core.healthcheck.Result;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class MessageFastViewProjectionHealthCheck implements HealthCheck {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(MessageFastViewProjectionHealthCheck.class);
     private static final ComponentName COMPONENT_NAME = new ComponentName("MessageFastViewProjection");
     private static final double MAXIMUM_MISS_PERCENTAGE_ACCEPTED = 10;
 
@@ -64,6 +67,7 @@ public class MessageFastViewProjectionHealthCheck implements HealthCheck {
         long totalCount = hitCount + missCount;
         double missCountPercentage = missCount * 100.0d / totalCount;
         if (missCountPercentage > MAXIMUM_MISS_PERCENTAGE_ACCEPTED) {
+            LOGGER.warn("MessageFastViewProjection missCountPercentage exceeded the threshold");
             return Result.degraded(COMPONENT_NAME,
                 String.format("retrieveMissCount percentage %s%% (%d/%d) is higher than the threshold %s%%",
                     missCountPercentage, missCount, totalCount, MAXIMUM_MISS_PERCENTAGE_ACCEPTED));
diff --git a/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java b/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java
index 2447427..81bc728 100644
--- a/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java
+++ b/server/data/data-jpa/src/main/java/org/apache/james/jpa/healthcheck/JPAHealthCheck.java
@@ -55,9 +55,11 @@ public class JPAHealthCheck implements HealthCheck {
                 return healthy(componentName());
             }
         } catch (IllegalStateException stateException) {
-            LOGGER.debug("EntityManagerFactory or EntityManager thrown an IllegalStateException, the connection is unhealthy");
+            LOGGER.debug("EntityManagerFactory or EntityManager threw an IllegalStateException, the connection is unhealthy");
             return unhealthy(componentName(), stateException.getMessage());
         }
+
+        LOGGER.error("EntityManager is not open, the connection is unhealthy");
         return unhealthy(componentName(), "entityManager is not open");
     }
 }


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


[james-project] 16/39: JAMES-3117 Remove Builder in 'Result' class

Posted by bt...@apache.org.
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 f3ec7e0403981278fd19b444de7973757f94519b
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Sun Mar 29 17:33:46 2020 +0700

    JAMES-3117 Remove Builder in 'Result' class
---
 .../org/apache/james/core/healthcheck/Result.java  | 54 ++++------------------
 .../dto/HealthCheckExecutionResultDto.java         |  4 +-
 .../james/webadmin/routes/HealthCheckRoutes.java   |  9 ++--
 3 files changed, 19 insertions(+), 48 deletions(-)

diff --git a/core/src/main/java/org/apache/james/core/healthcheck/Result.java b/core/src/main/java/org/apache/james/core/healthcheck/Result.java
index 9f2ffa7..c6aeb54 100644
--- a/core/src/main/java/org/apache/james/core/healthcheck/Result.java
+++ b/core/src/main/java/org/apache/james/core/healthcheck/Result.java
@@ -26,62 +26,27 @@ import com.google.common.base.MoreObjects;
 public class Result {
 
     public static Result healthy(ComponentName componentName) {
-        return Builder.builder().componentName(componentName).status(ResultStatus.HEALTHY).build();
+        return new Result(componentName, ResultStatus.HEALTHY, Optional.empty(), Optional.empty());
     }
 
     public static Result unhealthy(ComponentName componentName, String cause) {
-        return Builder.builder().componentName(componentName).status(ResultStatus.UNHEALTHY).cause(cause).build();
+        return new Result(componentName, ResultStatus.UNHEALTHY, Optional.of(cause), Optional.empty());
     }
 
     public static Result unhealthy(ComponentName componentName, String cause, Throwable error) {
-        return Builder.builder().componentName(componentName).status(ResultStatus.UNHEALTHY).cause(cause).error(error).build();
+        return new Result(componentName, ResultStatus.UNHEALTHY, Optional.of(cause), Optional.of(error));
     }
 
     public static Result degraded(ComponentName componentName, String cause) {
-        return Builder.builder().componentName(componentName).status(ResultStatus.DEGRADED).cause(cause).build();
-    }
-
-    public static class Builder {
-        private ComponentName componentName;
-        private ResultStatus status;
-        private String cause;
-        private Optional<Throwable> error = Optional.empty();
-
-        public Builder componentName(ComponentName componentName) {
-            this.componentName = componentName;
-            return this;
-        }
-
-        public Builder status(ResultStatus status) {
-            this.status = status;
-            return this;
-        }
-
-        public Builder cause(String cause) {
-            this.cause = cause;
-            return this;
-        }
-
-        public Builder error(Throwable error) {
-            this.error = Optional.of(error);
-            return this;
-        }
-
-        public Result build() {
-            return new Result(componentName, status, cause, error);
-        }
-
-        public static Builder builder() {
-            return new Builder();
-        }
+        return new Result(componentName, ResultStatus.DEGRADED, Optional.of(cause), Optional.empty());
     }
 
     private final ComponentName componentName;
     private final ResultStatus status;
-    private final String cause;
+    private final Optional<String> cause;
     private final Optional<Throwable> error;
 
-    public Result(ComponentName componentName, ResultStatus status, String cause, Optional<Throwable> error) {
+    private Result(ComponentName componentName, ResultStatus status, Optional<String> cause, Optional<Throwable> error) {
         this.componentName = componentName;
         this.status = status;
         this.cause = cause;
@@ -96,7 +61,7 @@ public class Result {
         return status;
     }
 
-    public String getCause() {
+    public Optional<String> getCause() {
         return cause;
     }
 
@@ -123,6 +88,7 @@ public class Result {
 
             return Objects.equals(this.componentName, result.componentName)
                 && Objects.equals(this.status, result.status)
+                && Objects.equals(this.cause, result.cause)
                 && Objects.equals(this.error, result.error);
         }
         return false;
@@ -130,7 +96,7 @@ public class Result {
 
     @Override
     public final int hashCode() {
-        return Objects.hash(componentName, status, error);
+        return Objects.hash(componentName, status, cause, error);
     }
 
     @Override
@@ -138,7 +104,7 @@ public class Result {
         return MoreObjects.toStringHelper(this)
             .add("componentName", componentName)
             .add("status", status)
-            .add("error", error)
+            .add("cause", cause)
             .toString();
     }
 }
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java
index d3bd774..2663b3e 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.webadmin.dto;
 
+import java.util.Optional;
+
 import org.apache.james.core.healthcheck.Result;
 
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@@ -46,7 +48,7 @@ public class HealthCheckExecutionResultDto {
         return healthCheckResult.getStatus().getValue();
     }
     
-    public String getCause() {
+    public Optional<String> getCause() {
         return healthCheckResult.getCause();
     }
     
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
index da339b9..3f422ba 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
@@ -153,18 +153,21 @@ public class HealthCheckRoutes implements PublicRoutes {
         switch (result.getStatus()) {
         case UNHEALTHY:
             if (result.getError().isPresent()) {
-                LOGGER.error("HealthCheck failed for {} : {} : {}", result.getComponentName().getName(), result.getCause(), result.getError().get());
+                LOGGER.error("HealthCheck failed for {} : {} : {}",
+                    result.getComponentName().getName(),
+                    result.getCause().orElse(""),
+                    result.getError().get());
                 break;
             }
 
             LOGGER.error("HealthCheck failed for {} : {}",
                     result.getComponentName().getName(),
-                    result.getCause());
+                    result.getCause().orElse(""));
             break;
         case DEGRADED:
             LOGGER.warn("HealthCheck is unstable for {} : {}",
                     result.getComponentName().getName(),
-                    result.getCause());
+                    result.getCause().orElse(""));
             break;
         case HEALTHY:
             // Here only to fix a warning, such cases are already filtered


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


[james-project] 29/39: JAMES-3139 reDeliver() DispatchingFailureGroup should deliver events to all groups

Posted by bt...@apache.org.
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 5801a7947c58ec6d8d7a2f7755fd86b1d4ad0929
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Apr 9 12:41:53 2020 +0700

    JAMES-3139 reDeliver() DispatchingFailureGroup should deliver events to all groups
---
 .../james/mailbox/events/RabbitMQEventBus.java     | 14 +++++
 .../james/mailbox/events/RabbitMQEventBusTest.java | 68 ++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
index 311e789..5cc92e8 100644
--- a/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
+++ b/mailbox/event/event-rabbitmq/src/main/java/org/apache/james/mailbox/events/RabbitMQEventBus.java
@@ -31,11 +31,13 @@ import org.apache.james.metrics.api.MetricFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
 
 import reactor.core.publisher.Mono;
 import reactor.rabbitmq.Sender;
 
 public class RabbitMQEventBus implements EventBus, Startable {
+    private static final Set<RegistrationKey> NO_KEY = ImmutableSet.of();
     private static final String NOT_RUNNING_ERROR_MESSAGE = "Event Bus is not running";
     static final String MAILBOX_EVENT = "mailboxEvent";
     static final String MAILBOX_EVENT_EXCHANGE_NAME = MAILBOX_EVENT + "-exchange";
@@ -143,6 +145,18 @@ public class RabbitMQEventBus implements EventBus, Startable {
     public Mono<Void> reDeliver(Group group, Event event) {
         Preconditions.checkState(isRunning, NOT_RUNNING_ERROR_MESSAGE);
         if (!event.isNoop()) {
+            /*
+            if the eventBus.dispatch() gets error while dispatching an event (rabbitMQ network outage maybe),
+            which means all the group consumers will not be receiving that event.
+
+            We store the that event in the dead letter and expecting in the future, it will be dispatched
+            again not only for a specific consumer but all.
+
+            That's why it is special, and we need to check event type before processing further.
+            */
+            if (group instanceof EventDispatcher.DispatchingFailureGroup) {
+                return eventDispatcher.dispatch(event, NO_KEY);
+            }
             return groupRegistrationHandler.retrieveGroupRegistration(group).reDeliver(event);
         }
         return Mono.empty();
diff --git a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
index abbfdf7..3dbbb80 100644
--- a/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
+++ b/mailbox/event/event-rabbitmq/src/test/java/org/apache/james/mailbox/events/RabbitMQEventBusTest.java
@@ -791,6 +791,74 @@ class RabbitMQEventBusTest implements GroupContract.SingleEventBusGroupContract,
             assertThat(dispatchingFailureEvents()).containsExactly(EVENT, EVENT_2);
         }
 
+        @Test
+        void dispatchShouldPersistEventsWhenDispatchingTheSameEventGetErrorMultipleTimes() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+
+            assertThat(dispatchingFailureEvents()).containsExactly(EVENT, EVENT);
+        }
+
+        @Test
+        void reDeliverShouldDeliverToAllGroupsWhenDispatchingFailure() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            EventCollector eventCollector2 = eventCollector();
+            eventBus().register(eventCollector2, GROUP_B);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+            rabbitMQExtension.getRabbitMQ().unpause();
+            dispatchingFailureEvents()
+                .forEach(event -> eventBus().reDeliver(DispatchingFailureGroup.INSTANCE, event).block());
+
+            getSpeedProfile().shortWaitCondition()
+                .untilAsserted(() -> assertThat(eventCollector.getEvents())
+                    .hasSameElementsAs(eventCollector2.getEvents())
+                    .containsExactly(EVENT));
+        }
+
+        @Test
+        void reDeliverShouldAddEventInDeadLetterWhenGettingError() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+            getSpeedProfile().longWaitCondition()
+                .until(() -> deadLetter().containEvents().block());
+
+            doQuietly(() -> eventBus().reDeliver(DispatchingFailureGroup.INSTANCE, EVENT).block());
+            rabbitMQExtension.getRabbitMQ().unpause();
+
+            getSpeedProfile().shortWaitCondition()
+                .untilAsserted(() -> assertThat(dispatchingFailureEvents())
+                    .containsExactly(EVENT, EVENT));
+        }
+
+        @Test
+        void reDeliverShouldNotStoreEventInAnotherGroupWhenGettingError() {
+            EventCollector eventCollector = eventCollector();
+            eventBus().register(eventCollector, GROUP_A);
+
+            rabbitMQExtension.getRabbitMQ().pause();
+            doQuietly(() -> eventBus().dispatch(EVENT, NO_KEYS).block());
+            getSpeedProfile().longWaitCondition()
+                .until(() -> deadLetter().containEvents().block());
+
+            doQuietly(() -> eventBus().reDeliver(DispatchingFailureGroup.INSTANCE, EVENT).block());
+            rabbitMQExtension.getRabbitMQ().unpause();
+
+            getSpeedProfile().shortWaitCondition()
+                .untilAsserted(() -> assertThat(deadLetter().groupsWithFailedEvents().toStream())
+                    .hasOnlyElementsOfType(DispatchingFailureGroup.class));
+        }
+
         private Stream<Event> dispatchingFailureEvents() {
             return deadLetter().failedIds(DispatchingFailureGroup.INSTANCE)
                 .flatMap(insertionId -> deadLetter().failedEvent(DispatchingFailureGroup.INSTANCE, insertionId))


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


[james-project] 15/39: JAMES-3117 Add logging for callers of HealthChecks

Posted by bt...@apache.org.
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 215eca85e120deaf46c2886dc025919f0b0aeefe
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Fri Mar 27 15:47:41 2020 +0700

    JAMES-3117 Add logging for callers of HealthChecks
---
 .../org/apache/james/PeriodicalHealthChecks.java   | 32 ++++++++++++++++++++++
 .../dto/HealthCheckExecutionResultDto.java         |  4 +--
 .../james/webadmin/routes/HealthCheckRoutes.java   |  9 ++++--
 3 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
index 86d5806..2d80593 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -26,7 +26,10 @@ import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 
 import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.core.healthcheck.Result;
 import org.apache.james.lifecycle.api.Startable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import reactor.core.Disposable;
 import reactor.core.publisher.Flux;
@@ -36,6 +39,7 @@ import reactor.core.scheduler.Schedulers;
 
 public class PeriodicalHealthChecks implements Startable {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(PeriodicalHealthChecks.class);
     private final Set<HealthCheck> healthChecks;
     private final Scheduler scheduler;
     private final Duration period;
@@ -53,10 +57,38 @@ public class PeriodicalHealthChecks implements Startable {
             .flatMap(any -> Flux.fromIterable(healthChecks)
                 .flatMap(healthCheck ->
                     Mono.fromCallable(healthCheck::check)))
+                .flatMap(result ->
+                    Mono.fromRunnable(() -> logResult(result)))
+            .onErrorContinue(this::logError)
             .subscribeOn(Schedulers.elastic())
             .subscribe();
     }
 
+    private void logResult(Result result) {
+        switch (result.getStatus()) {
+            case HEALTHY:
+                break;
+            case DEGRADED:
+                LOGGER.warn("DEGRADED: {} : {}", result.getComponentName().getName(), result.getCause());
+                break;
+            case UNHEALTHY:
+                if (result.getError().isPresent()) {
+                    LOGGER.error("UNHEALTHY: {} : {} : {}", result.getComponentName().getName(), result.getCause(), result.getError().get());
+                    break;
+                }
+
+                LOGGER.error("UNHEALTHY: {} : {}", result.getComponentName().getName(), result.getCause());
+                break;
+        }
+    }
+
+    private void logError(Throwable error, Object triggeringValue) {
+        if (triggeringValue instanceof Result) {
+            Result result = (Result) triggeringValue;
+            LOGGER.error("HealthCheck error for: {}, Cause: {}", result.getComponentName(), error);
+        }
+    }
+
     @PreDestroy
     public void stop() {
         disposable.dispose();
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java
index 2663b3e..d3bd774 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/dto/HealthCheckExecutionResultDto.java
@@ -19,8 +19,6 @@
 
 package org.apache.james.webadmin.dto;
 
-import java.util.Optional;
-
 import org.apache.james.core.healthcheck.Result;
 
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@@ -48,7 +46,7 @@ public class HealthCheckExecutionResultDto {
         return healthCheckResult.getStatus().getValue();
     }
     
-    public Optional<String> getCause() {
+    public String getCause() {
         return healthCheckResult.getCause();
     }
     
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
index 520a7a5..da339b9 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/routes/HealthCheckRoutes.java
@@ -152,14 +152,19 @@ public class HealthCheckRoutes implements PublicRoutes {
     private void logFailedCheck(Result result) {
         switch (result.getStatus()) {
         case UNHEALTHY:
+            if (result.getError().isPresent()) {
+                LOGGER.error("HealthCheck failed for {} : {} : {}", result.getComponentName().getName(), result.getCause(), result.getError().get());
+                break;
+            }
+
             LOGGER.error("HealthCheck failed for {} : {}",
                     result.getComponentName().getName(),
-                    result.getCause().orElse(""));
+                    result.getCause());
             break;
         case DEGRADED:
             LOGGER.warn("HealthCheck is unstable for {} : {}",
                     result.getComponentName().getName(),
-                    result.getCause().orElse(""));
+                    result.getCause());
             break;
         case HEALTHY:
             // Here only to fix a warning, such cases are already filtered


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


[james-project] 14/39: JAMES-3117 Add Builder in 'Result' class

Posted by bt...@apache.org.
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 f8fe440120ed1f3c7b3d38e737af697da8ae9b75
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Fri Mar 27 15:46:00 2020 +0700

    JAMES-3117 Add Builder in 'Result' class
---
 .../org/apache/james/core/healthcheck/Result.java  | 69 ++++++++++++++++++----
 1 file changed, 57 insertions(+), 12 deletions(-)

diff --git a/core/src/main/java/org/apache/james/core/healthcheck/Result.java b/core/src/main/java/org/apache/james/core/healthcheck/Result.java
index 6345cda..9f2ffa7 100644
--- a/core/src/main/java/org/apache/james/core/healthcheck/Result.java
+++ b/core/src/main/java/org/apache/james/core/healthcheck/Result.java
@@ -26,25 +26,66 @@ import com.google.common.base.MoreObjects;
 public class Result {
 
     public static Result healthy(ComponentName componentName) {
-        return new Result(componentName, ResultStatus.HEALTHY, Optional.empty());
+        return Builder.builder().componentName(componentName).status(ResultStatus.HEALTHY).build();
     }
 
     public static Result unhealthy(ComponentName componentName, String cause) {
-        return new Result(componentName, ResultStatus.UNHEALTHY, Optional.of(cause));
+        return Builder.builder().componentName(componentName).status(ResultStatus.UNHEALTHY).cause(cause).build();
+    }
+
+    public static Result unhealthy(ComponentName componentName, String cause, Throwable error) {
+        return Builder.builder().componentName(componentName).status(ResultStatus.UNHEALTHY).cause(cause).error(error).build();
     }
 
     public static Result degraded(ComponentName componentName, String cause) {
-        return new Result(componentName, ResultStatus.DEGRADED, Optional.of(cause));
+        return Builder.builder().componentName(componentName).status(ResultStatus.DEGRADED).cause(cause).build();
+    }
+
+    public static class Builder {
+        private ComponentName componentName;
+        private ResultStatus status;
+        private String cause;
+        private Optional<Throwable> error = Optional.empty();
+
+        public Builder componentName(ComponentName componentName) {
+            this.componentName = componentName;
+            return this;
+        }
+
+        public Builder status(ResultStatus status) {
+            this.status = status;
+            return this;
+        }
+
+        public Builder cause(String cause) {
+            this.cause = cause;
+            return this;
+        }
+
+        public Builder error(Throwable error) {
+            this.error = Optional.of(error);
+            return this;
+        }
+
+        public Result build() {
+            return new Result(componentName, status, cause, error);
+        }
+
+        public static Builder builder() {
+            return new Builder();
+        }
     }
 
     private final ComponentName componentName;
     private final ResultStatus status;
-    private final Optional<String> cause;
+    private final String cause;
+    private final Optional<Throwable> error;
 
-    private Result(ComponentName componentName, ResultStatus status, Optional<String> cause) {
+    public Result(ComponentName componentName, ResultStatus status, String cause, Optional<Throwable> error) {
         this.componentName = componentName;
         this.status = status;
         this.cause = cause;
+        this.error = error;
     }
 
     public ComponentName getComponentName() {
@@ -55,6 +96,14 @@ public class Result {
         return status;
     }
 
+    public String getCause() {
+        return cause;
+    }
+
+    public Optional<Throwable> getError() {
+        return error;
+    }
+
     public boolean isHealthy() {
         return status == ResultStatus.HEALTHY;
     }
@@ -67,10 +116,6 @@ public class Result {
         return status == ResultStatus.UNHEALTHY;
     }
 
-    public Optional<String> getCause() {
-        return cause;
-    }
-
     @Override
     public final boolean equals(Object o) {
         if (o instanceof Result) {
@@ -78,14 +123,14 @@ public class Result {
 
             return Objects.equals(this.componentName, result.componentName)
                 && Objects.equals(this.status, result.status)
-                && Objects.equals(this.cause, result.cause);
+                && Objects.equals(this.error, result.error);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hash(componentName, status, cause);
+        return Objects.hash(componentName, status, error);
     }
 
     @Override
@@ -93,7 +138,7 @@ public class Result {
         return MoreObjects.toStringHelper(this)
             .add("componentName", componentName)
             .add("status", status)
-            .add("cause", cause)
+            .add("error", error)
             .toString();
     }
 }
\ No newline at end of file


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


[james-project] 34/39: JAMES-3149 default method for mailbox counters

Posted by bt...@apache.org.
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 2feeea7f94768aaa0404453a4b1ebec75fb9eb05
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Apr 14 10:16:21 2020 +0700

    JAMES-3149 default method for mailbox counters
---
 .../apache/james/mailbox/jpa/mail/JPAMessageMapper.java    |  8 --------
 .../james/mailbox/jpa/mail/TransactionalMessageMapper.java |  6 ------
 .../james/mailbox/maildir/mail/MaildirMessageMapper.java   |  9 ---------
 .../james/mailbox/inmemory/mail/InMemoryMessageMapper.java | 14 --------------
 .../org/apache/james/mailbox/store/mail/MessageMapper.java |  9 ++++++++-
 .../store/StoreMailboxMessageResultIteratorTest.java       |  6 ------
 6 files changed, 8 insertions(+), 44 deletions(-)

diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
index 996cd6b..9ef0bbc 100644
--- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
+++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
@@ -18,7 +18,6 @@
  ****************************************************************/
 package org.apache.james.mailbox.jpa.mail;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -153,13 +152,6 @@ public class JPAMessageMapper extends JPATransactionalMapper implements MessageM
     }
 
     @Override
-    public List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) throws MailboxException {
-        return mailboxes.stream()
-            .map(Throwing.<Mailbox, MailboxCounters>function(this::getMailboxCounters).sneakyThrow())
-            .collect(Guavate.toImmutableList());
-    }
-
-    @Override
     public void delete(Mailbox mailbox, MailboxMessage message) throws MailboxException {
         try {
             AbstractJPAMailboxMessage jpaMessage = getEntityManager().find(AbstractJPAMailboxMessage.class, buildKey(mailbox, message));
diff --git a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/TransactionalMessageMapper.java b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/TransactionalMessageMapper.java
index e932fc8..c6f8f6d 100644
--- a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/TransactionalMessageMapper.java
+++ b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/TransactionalMessageMapper.java
@@ -19,7 +19,6 @@
 
 package org.apache.james.mailbox.jpa.mail;
 
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -153,9 +152,4 @@ public class TransactionalMessageMapper implements MessageMapper {
     public Flags getApplicableFlag(Mailbox mailbox) throws MailboxException {
         return messageMapper.getApplicableFlag(mailbox);
     }
-
-    @Override
-    public List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) throws MailboxException {
-        return messageMapper.getMailboxCounters(mailboxes);
-    }
 }
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
index 029cae5..30ca42f 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
@@ -24,7 +24,6 @@ import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -46,7 +45,6 @@ import org.apache.james.mailbox.maildir.MaildirMessageName;
 import org.apache.james.mailbox.maildir.MaildirStore;
 import org.apache.james.mailbox.maildir.mail.model.MaildirMailboxMessage;
 import org.apache.james.mailbox.model.Mailbox;
-import org.apache.james.mailbox.model.MailboxCounters;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageRange.Type;
@@ -153,13 +151,6 @@ public class MaildirMessageMapper extends AbstractMessageMapper {
     }
 
     @Override
-    public List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) throws MailboxException {
-        return mailboxes.stream()
-            .map(Throwing.<Mailbox, MailboxCounters>function(this::getMailboxCounters).sneakyThrow())
-            .collect(Guavate.toImmutableList());
-    }
-
-    @Override
     public Iterator<UpdatedFlags> updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagsUpdateCalculator, MessageRange set) throws MailboxException {
         final List<UpdatedFlags> updatedFlags = new ArrayList<>();
         final MaildirFolder folder = maildirStore.createMaildirFolder(mailbox);
diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java
index 191266f..4bf22c1 100644
--- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java
+++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java
@@ -20,7 +20,6 @@
 package org.apache.james.mailbox.inmemory.mail;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -36,7 +35,6 @@ import org.apache.james.mailbox.ModSeq;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.inmemory.InMemoryId;
 import org.apache.james.mailbox.model.Mailbox;
-import org.apache.james.mailbox.model.MailboxCounters;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageRange;
@@ -100,18 +98,6 @@ public class InMemoryMessageMapper extends AbstractMessageMapper {
     }
 
     @Override
-    public List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) {
-        return mailboxes.stream()
-            .map(Mailbox::getMailboxId)
-            .map(id -> MailboxCounters.builder()
-                .mailboxId(id)
-                .count(countMessagesInMailbox(id))
-                .unseen(countUnseenMessagesInMailbox(id))
-                .build())
-            .collect(Guavate.toImmutableList());
-    }
-
-    @Override
     public void delete(Mailbox mailbox, MailboxMessage message) {
         getMembershipByUidForMailbox(mailbox).remove(message.getUid());
     }
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
index ff1faf0..c0d34d4 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
@@ -39,6 +39,9 @@ import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.apache.james.mailbox.store.mail.model.Property;
 import org.apache.james.mailbox.store.transaction.Mapper;
 
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
+
 /**
  * Maps {@link MailboxMessage} in a {@link org.apache.james.mailbox.MessageManager}. A {@link MessageMapper} has a lifecycle from the start of a request
  * to the end of the request.
@@ -70,7 +73,11 @@ public interface MessageMapper extends Mapper {
 
     MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException;
 
-    List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) throws MailboxException;
+    default List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) throws MailboxException {
+        return mailboxes.stream()
+            .map(Throwing.<Mailbox, MailboxCounters>function(this::getMailboxCounters).sneakyThrow())
+            .collect(Guavate.toImmutableList());
+    }
 
     /**
      * Delete the given {@link MailboxMessage}
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxMessageResultIteratorTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxMessageResultIteratorTest.java
index 25067ca..9f56121 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxMessageResultIteratorTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxMessageResultIteratorTest.java
@@ -23,7 +23,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -82,11 +81,6 @@ class StoreMailboxMessageResultIteratorTest {
         }
 
         @Override
-        public List<MailboxCounters> getMailboxCounters(Collection<Mailbox> mailboxes) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
         public Iterator<MailboxMessage> findInMailbox(Mailbox mailbox, MessageRange set,
                                                               org.apache.james.mailbox.store.mail.MessageMapper.FetchType type, int limit)
                 throws MailboxException {


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


[james-project] 04/39: JAMES-3117 Add healthcheck.propeties config file in guice packages

Posted by bt...@apache.org.
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 d73c0038176e59d7891ba06ebe23cda749196c99
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Mon Mar 23 09:59:01 2020 +0700

    JAMES-3117 Add healthcheck.propeties config file in guice packages
---
 .../destination/conf/healthcheck.properties        | 30 +++++++++++++++
 .../destination/conf/healthcheck.properties        | 30 +++++++++++++++
 .../destination/conf/healthcheck.properties        | 30 +++++++++++++++
 .../destination/conf/healthcheck.properties        | 30 +++++++++++++++
 .../destination/conf/healthcheck.properties        | 30 +++++++++++++++
 .../jpa/destination/conf/healthcheck.properties    | 30 +++++++++++++++
 .../memory/destination/conf/healthcheck.properties | 30 +++++++++++++++
 src/site/site.xml                                  |  1 +
 src/site/xdoc/server/config-healthcheck.xml        | 44 ++++++++++++++++++++++
 9 files changed, 255 insertions(+)

diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties b/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties b/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/jpa-smtp/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties b/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/jpa/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties b/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
new file mode 100644
index 0000000..23b9879
--- /dev/null
+++ b/dockerfiles/run/guice/memory/destination/conf/healthcheck.properties
@@ -0,0 +1,30 @@
+#  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.
+
+#  This template file can be used as example for James Server configuration
+#  DO NOT USE IT AS SUCH AND ADAPT IT TO YOUR NEEDS
+
+# Configuration file for Periodical Health Checks
+
+# Read https://james.apache.org/server/config-healthcheck.html for further details
+
+# Optional. Initial delay in seconds before PeriodicalHealthChecks starts. Default value is 60
+# healthcheck.initial.delay=60
+
+# Optional. Period in seconds between two PeriodicalHealthChecks. Default value is 60
+# healthcheck.period=60
+
diff --git a/src/site/site.xml b/src/site/site.xml
index a8c662f..36ff240 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -130,6 +130,7 @@
                         <item name="Mail Repository Stores" href="/server/config-mailrepositorystore.html" />
                         <item name="Mailbox" href="/server/config-mailbox.html" />
                         <item name="Mailet Container" href="/server/config-mailetcontainer.html" />
+                        <item name="Periodical Health Checks" href="/server/config-healthcheck.html" />
                         <item name="POP3" href="/server/config-pop3.html" />
                         <item name="Quota" href="/server/config-quota.html" />
                         <item name="RabbitMQ" href="/server/config-rabbitmq.html" />
diff --git a/src/site/xdoc/server/config-healthcheck.xml b/src/site/xdoc/server/config-healthcheck.xml
new file mode 100644
index 0000000..e7604bd
--- /dev/null
+++ b/src/site/xdoc/server/config-healthcheck.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<document>
+
+    <properties>
+        <title>Apache James Server 3 - PeriodicalHealthChecks Configuration</title>
+    </properties>
+
+    <body>
+
+        <section name="PeriodicalHealthChecks Configuration">
+
+            <p>Consult <a href="https://github.com/apache/james-project/blob/master/dockerfiles/run/guice/cassandra/destination/conf/healthcheck.properties">healthcheck.properties</a> in GIT to get some examples and hints.</p>
+
+            <p>
+                Use this configuration to define the initial delay and period for the PeriodicalHealthChecks. It is only applicable with Guice products.
+            </p>
+
+            <dl>
+                <dt><strong>healthcheck.initial.delay</strong></dt>
+                <dd>Define the delay time before first health check starts (default: 60)</dd>
+                <dt><strong>healthcheck.period</strong></dt>
+                <dd>Define the period between two periodical health checks (default: 60)</dd>
+            </dl>
+        </section>
+    </body>
+</document>
\ No newline at end of file


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


[james-project] 02/39: JAMES-3117 Add PeriodicalHealthChecks/Test

Posted by bt...@apache.org.
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 2e7b08bc939b8bf6c3f0bb332b4c3c490345cc4b
Author: LanKhuat <kh...@gmail.com>
AuthorDate: Thu Mar 19 10:56:58 2020 +0700

    JAMES-3117 Add PeriodicalHealthChecks/Test
---
 server/container/guice/guice-common/pom.xml        |  9 +++
 .../org/apache/james/PeriodicalHealthChecks.java   | 64 +++++++++++++++
 .../apache/james/PeriodicalHealthChecksTest.java   | 91 ++++++++++++++++++++++
 3 files changed, 164 insertions(+)

diff --git a/server/container/guice/guice-common/pom.xml b/server/container/guice/guice-common/pom.xml
index dfff299..4433636 100644
--- a/server/container/guice/guice-common/pom.xml
+++ b/server/container/guice/guice-common/pom.xml
@@ -152,6 +152,15 @@
             <artifactId>guice</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
new file mode 100644
index 0000000..91d0872
--- /dev/null
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/PeriodicalHealthChecks.java
@@ -0,0 +1,64 @@
+/****************************************************************
+ * 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;
+
+import java.time.Duration;
+import java.util.Set;
+
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.lifecycle.api.Startable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
+public class PeriodicalHealthChecks implements Startable {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PeriodicalHealthChecks.class);
+    private static final long INITIAL_DELAY = 1;
+    private static final long PERIOD = 1;
+
+    private final Flux<HealthCheck> healthChecks;
+    private Disposable disposable;
+
+    @Inject
+    PeriodicalHealthChecks(Set<HealthCheck> healthChecks) {
+        this.healthChecks = Flux.fromIterable(healthChecks);
+    }
+
+    public void start() {
+        disposable = Flux.interval(Duration.ofSeconds(INITIAL_DELAY), Duration.ofSeconds(PERIOD))
+            .flatMap(any ->
+                healthChecks.flatMap(healthCheck -> Mono.just(healthCheck.check())))
+            .subscribeOn(Schedulers.elastic())
+            .subscribe();
+    }
+
+    @PreDestroy
+    public void stop() {
+        disposable.dispose();
+    }
+}
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
new file mode 100644
index 0000000..f029333
--- /dev/null
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/PeriodicalHealthChecksTest.java
@@ -0,0 +1,91 @@
+/****************************************************************
+ * 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;
+
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.james.core.healthcheck.ComponentName;
+import org.apache.james.core.healthcheck.HealthCheck;
+import org.apache.james.core.healthcheck.Result;
+import org.apache.james.mailbox.events.EventDeadLettersHealthCheck;
+import org.awaitility.Awaitility;
+import org.awaitility.Duration;
+import org.awaitility.core.ConditionFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class PeriodicalHealthChecksTest {
+
+    private static final ConditionFactory AWAIT = Awaitility.await()
+        .atMost(Duration.TEN_SECONDS)
+        .with()
+        .pollInterval(Duration.ONE_SECOND);
+
+    private HealthCheck mockHealthCheck1;
+    private HealthCheck mockHealthCheck2;
+    private PeriodicalHealthChecks testee;
+
+    @BeforeEach
+    void setUp() {
+        mockHealthCheck1 = Mockito.mock(EventDeadLettersHealthCheck.class);
+        mockHealthCheck2 = Mockito.mock(GuiceLifecycleHealthCheck.class);
+        when(mockHealthCheck1.check()).thenReturn(Result.healthy(new ComponentName("mock1")));
+        when(mockHealthCheck2.check()).thenReturn(Result.healthy(new ComponentName("mock2")));
+
+        Set<HealthCheck> healthCheckSet = new HashSet<>();
+        healthCheckSet.add(mockHealthCheck1);
+        healthCheckSet.add(mockHealthCheck2);
+
+        testee = new PeriodicalHealthChecks(healthCheckSet);
+        testee.start();
+    }
+
+    @AfterEach
+    void tearDown() {
+        testee.stop();
+    }
+
+    @Test
+    void startShouldCallHealthCheckAtLeastOnce() {
+        AWAIT.untilAsserted(() -> verify(mockHealthCheck1, atLeast(1)).check());
+    }
+
+    @Test
+    void startShouldCallHealthCheckMultipleTimes() {
+        AWAIT.untilAsserted(() -> verify(mockHealthCheck1, times(5)).check());
+    }
+
+    @Test
+    void startShouldCallAllHealthChecks() {
+        AWAIT.untilAsserted(() -> {
+            verify(mockHealthCheck1, atLeast(5)).check();
+            verify(mockHealthCheck2, atLeast(5)).check();
+        });
+    }
+}


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