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/01 03:32:51 UTC

[james-project] branch master updated (9d6b3a0 -> 30092e6)

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 9d6b3a0  JAMES-3078 Enable wiretapping
     new 4c9238a  [Performance] Enhance AppendProcessor performances
     new 4e581f2  [Performance] Enhance SelectProcessor performances
     new b761946  [Performance] Use MailboxId rather than MailboxPath in IMAP logs
     new 6f4e5be  [Performance] Avoid empty getMessages(FULL) when all messages have preview
     new 1d03686  JAMES-2813 Task executionListing is enough
     new 3f96a31  [Refactor] Replace Mono::switchIfEmpty(Mono.just(x)) by Mono::defaultIfEmpty(x)
     new b927b47  JAMES-3129 Sending mail via JMAP: Add test for Outbox & Sent counters
     new 6dadb19  JAMES-3129 Only apply PostDequeueDecorator to SPOOL queue
     new 7682112  JAMES-3022 Change default log file after log4j2 upgrade
     new 2d73a27  ADR: Fixing Cassandra ACL inconsistencies
     new f5569fd  ADR: Fixing Cassandra message inconsistencies
     new 79cc79b  [ADR] Recomputing mailbox counters
     new 5d5dda3  JAMES-3058 ADR: Cassandra mailbox object inconsistencies offline solving
     new 30092e6  Write an ADR about James polyglot strategy

The 14 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                                       |   1 +
 .../versions/CassandraSchemaVersionDAO.java        |   2 +-
 .../run/spring/destination/conf/log4j.properties   | 136 ------
 dockerfiles/run/spring/destination/conf/log4j2.xml | 486 +++++++++++++++++++++
 .../mailbox/cassandra/mail/CassandraACLMapper.java |   2 +-
 .../cassandra/mail/CassandraMailboxCounterDAO.java |   2 +-
 .../cassandra/mail/CassandraMailboxMapper.java     |   4 +-
 .../cassandra/mail/CassandraMessageIdDAO.java      |   2 +-
 .../task/SolveMailboxInconsistenciesService.java   |   6 +-
 .../imap/processor/AbstractMailboxProcessor.java   |   2 +-
 .../imap/processor/AbstractSelectionProcessor.java |   6 +-
 .../james/imap/processor/AppendProcessor.java      |   3 +-
 .../imap/processor/base/SelectedMailboxImpl.java   |   3 +-
 .../processor/base/MailboxEventAnalyserTest.java   |   2 +-
 .../processor/base/SelectedMailboxImplTest.java    |   4 +-
 server/app/src/main/resources/log4j.properties     | 154 -------
 server/app/src/main/resources/log4j2.xml           | 486 +++++++++++++++++++++
 .../sieve/cassandra/CassandraSieveRepository.java  |   4 +-
 .../methods/integration/SetMessagesMethodTest.java |  93 ++++
 .../model/message/view/MessageFastViewFactory.java |   3 +
 .../model/message/view/MessageFullViewFactory.java |   2 +-
 .../draft/send/PostDequeueDecoratorFactory.java    |  11 +-
 .../queue/activemq/ActiveMQCacheableMailQueue.java |   2 +-
 .../queue/api/MailQueueItemDecoratorFactory.java   |   2 +-
 .../api/RawMailQueueItemDecoratorFactory.java      |   2 +-
 .../james/queue/file/FileCacheableMailQueue.java   |   2 +-
 .../james/queue/jms/JMSCacheableMailQueue.java     |   2 +-
 .../james/queue/memory/MemoryMailQueueFactory.java |   2 +-
 .../apache/james/queue/rabbitmq/MailQueueName.java |   4 +
 .../james/queue/rabbitmq/RabbitMQMailQueue.java    |   2 +-
 .../CassandraMailQueueViewTestFactory.java         |   2 +-
 .../eventsourcing/EventSourcingTaskManager.scala   |   3 +-
 .../org/apache/james/jmap/JmapCommonRequests.java  |  10 +-
 .../0020-cassandra-mailbox-object-consistency.md   |  76 ++++
 src/adr/0021-cassandra-acl-inconsistency.md        |  66 +++
 src/adr/0022-cassandra-message-inconsistency.md    |  94 ++++
 ...3-cassandra-mailbox-counters-inconsistencies.md |  62 +++
 src/adr/0024-polyglot-strategy.md                  | 161 +++++++
 upgrade-instructions.md                            |   9 +-
 39 files changed, 1586 insertions(+), 329 deletions(-)
 delete mode 100644 dockerfiles/run/spring/destination/conf/log4j.properties
 create mode 100644 dockerfiles/run/spring/destination/conf/log4j2.xml
 delete mode 100644 server/app/src/main/resources/log4j.properties
 create mode 100644 server/app/src/main/resources/log4j2.xml
 create mode 100644 src/adr/0020-cassandra-mailbox-object-consistency.md
 create mode 100644 src/adr/0021-cassandra-acl-inconsistency.md
 create mode 100644 src/adr/0022-cassandra-message-inconsistency.md
 create mode 100644 src/adr/0023-cassandra-mailbox-counters-inconsistencies.md
 create mode 100644 src/adr/0024-polyglot-strategy.md


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


[james-project] 04/14: [Performance] Avoid empty getMessages(FULL) when all messages have preview

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 6f4e5beaaaf6f10ac5979c1da536ce103e6985eb
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Mar 30 14:45:52 2020 +0700

    [Performance] Avoid empty getMessages(FULL) when all messages have preview
    
    ```
    Flux.merge(
        fetch(withProjectionEntry, FetchGroup.HEADERS, mailboxSession)
            .map(messageResults -> Helpers.toMessageViews(messageResults, new FromMessageResultAndPreview(blobManager, fastProjections))),
        fetch(withoutProjectionEntry, FetchGroup.FULL_CONTENT, mailboxSession)
            .map(messageResults -> Helpers.toMessageViews(messageResults, messageFullViewFactory::fromMessageResults)))
    ```
    
    Calling code results in fetch called with empty message list. We should
    avoid propagating the query which results in a needless empty call on
    messageManager layer (gain of reactor pipeline evaluation)
---
 .../james/jmap/draft/model/message/view/MessageFastViewFactory.java    | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFastViewFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFastViewFactory.java
index 7d7334b..cbf3c3c 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFastViewFactory.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFastViewFactory.java
@@ -140,6 +140,9 @@ public class MessageFastViewFactory implements MessageViewFactory<MessageFastVie
     }
 
     private Mono<List<MessageResult>> fetch(Collection<MessageId> messageIds, FetchGroup fetchGroup, MailboxSession mailboxSession) {
+        if (messageIds.isEmpty()) {
+            return Mono.empty();
+        }
         return Mono.fromCallable(() -> messageIdManager.getMessages(messageIds, fetchGroup, mailboxSession))
             .onErrorResume(MailboxException.class, ex -> {
                 LOGGER.error("cannot read messages {}", messageIds, ex);


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


[james-project] 13/14: JAMES-3058 ADR: Cassandra mailbox object inconsistencies offline solving

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 5d5dda3d966b42a3ae83ac8e97ee9c9d1e578e37
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Feb 27 11:05:08 2020 +0700

    JAMES-3058 ADR: Cassandra mailbox object inconsistencies offline solving
---
 .../0020-cassandra-mailbox-object-consistency.md   | 76 ++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/src/adr/0020-cassandra-mailbox-object-consistency.md b/src/adr/0020-cassandra-mailbox-object-consistency.md
new file mode 100644
index 0000000..559e2a4
--- /dev/null
+++ b/src/adr/0020-cassandra-mailbox-object-consistency.md
@@ -0,0 +1,76 @@
+# 20. Cassandra Mailbox object consistency
+
+Date: 2020-02-27
+
+## Status
+
+Accepted (lazy consensus)
+
+## Context
+
+Mailboxes are denormalized in Cassandra in order to access them both by their immutable identifier and their mailbox 
+path (name):
+
+ - `mailbox` table stores mailboxes by their immutable identifier
+ - `mailboxPathV2` table stores mailboxes by their mailbox path
+
+We furthermore maintain two invariants on top of these tables:
+ - **mailboxPath** unicity. Each mailbox path can be used maximum once. This is ensured by writing the mailbox path first
+ using Lightweight Transactions.
+ - **mailboxId** unicity. Each mailbox identifier is used by only a single path. We have no real way to ensure a given mailbox
+ is not referenced by two paths.
+
+Failures during the denormalization process will lead to inconsistencies between the two tables.
+
+This can lead to the following user experience:
+
+```
+BOB creates mailbox A
+Denormalization fails and an error is returned to A
+
+BOB retries mailbox A creation
+BOB is being told mailbox A already exist
+
+BOB tries to access mailbox A
+BOB is being told mailbox A does not exist
+```
+
+## Decision
+
+We should provide an offline (meaning absence of user traffic via for exemple SMTP, IMAP or JMAP) webadmin task to 
+solve mailbox object inconsistencies.
+
+This task will read `mailbox` table and adapt path registrations in `mailboxPathV2`:
+ - Missing registrations will be added
+ - Orphan registrations will be removed
+ - Mismatch in content between the two tables will require merging the two mailboxes together.
+
+## Consequences
+
+As an administrator, if some of my users reports the bugs mentioned above, I have a way to sanitize my Cassandra 
+mailbox database.
+
+However, due to the two invariants mentioned above, we can not identify a clear source of trust based on existing 
+tables for the mailbox object. The task previously mentioned is subject to concurrency issues that might cancel 
+legitimate concurrent user actions.
+
+Hence this task must be run offline (meaning absence of user traffic via for exemple SMTP, IMAP or JMAP). This can be
+achieved via reconfiguration (disabling the given protocols and restarting James) or via firewall rules.
+
+Due to all of those risks, a Confirmation header `I-KNOW-WHAT-I-M-DOING` should be positioned to 
+`ALL-SERVICES-ARE-OFFLINE` in order to prevent accidental calls.
+
+In the future, we should revisit the mailbox object data-model and restructure it, to identify a source of truth to 
+base the inconsistency fixing task on. Event sourcing is a good candidate for this.
+
+## References
+
+* [JAMES-3058 Webadmin task to solve Cassandra Mailbox inconsistencies](https://issues.apache.org/jira/browse/JAMES-3058)
+
+* [Pull Request: mailbox-cassandra utility to solve Mailbox inconsistency](https://github.com/linagora/james-project/pull/3110)
+
+* [Pull Request: JAMES-3058 Concurrency testing for fixing Cassandra mailbox inconsistencies](https://github.com/linagora/james-project/pull/3130)
+
+This [thread](https://github.com/linagora/james-project/pull/3130#discussion_r383349596) provides significant discussions leading to this Architecture Decision Record
+
+* [Discussion on the mailing list](https://www.mail-archive.com/server-dev@james.apache.org/msg64432.html)
\ 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] 06/14: [Refactor] Replace Mono::switchIfEmpty(Mono.just(x)) by Mono::defaultIfEmpty(x)

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 3f96a3151729c7f10bd77d2e92173ddd884e186d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Mar 30 10:46:48 2020 +0700

    [Refactor] Replace Mono::switchIfEmpty(Mono.just(x)) by Mono::defaultIfEmpty(x)
---
 .../backends/cassandra/versions/CassandraSchemaVersionDAO.java      | 2 +-
 .../org/apache/james/mailbox/cassandra/mail/CassandraACLMapper.java | 2 +-
 .../james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java    | 2 +-
 .../apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java | 4 ++--
 .../apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java  | 2 +-
 .../cassandra/mail/task/SolveMailboxInconsistenciesService.java     | 6 +++---
 .../org/apache/james/sieve/cassandra/CassandraSieveRepository.java  | 4 ++--
 .../james/jmap/draft/model/message/view/MessageFullViewFactory.java | 2 +-
 .../rabbitmq/view/cassandra/CassandraMailQueueViewTestFactory.java  | 2 +-
 9 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionDAO.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionDAO.java
index 963099e..908a40b 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionDAO.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionDAO.java
@@ -72,7 +72,7 @@ public class CassandraSchemaVersionDAO {
             .reduce(Math::max)
             .map(SchemaVersion::new)
             .map(Optional::of)
-            .switchIfEmpty(Mono.just(Optional.empty()));
+            .defaultIfEmpty(Optional.empty());
     }
 
     public Mono<Void> updateVersion(SchemaVersion newVersion) {
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapper.java
index 4a2734b..a14cd2b 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapper.java
@@ -103,7 +103,7 @@ public class CassandraACLMapper {
     public Mono<MailboxACL> getACL(CassandraId cassandraId) {
         return getStoredACLRow(cassandraId)
             .map(row -> getAcl(cassandraId, row))
-            .switchIfEmpty(Mono.just(MailboxACL.EMPTY));
+            .defaultIfEmpty(MailboxACL.EMPTY);
     }
 
     private MailboxACL getAcl(CassandraId cassandraId, Row row) {
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
index 02db4a7..c8898a4 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
@@ -110,7 +110,7 @@ public class CassandraMailboxCounterDAO {
         CassandraId mailboxId = (CassandraId) counters.getMailboxId();
 
         return retrieveMailboxCounters(mailboxId)
-            .switchIfEmpty(Mono.just(emptyCounters(mailboxId)))
+            .defaultIfEmpty(emptyCounters(mailboxId))
             .flatMap(storedCounters -> {
                 if (storedCounters.equals(counters)) {
                     return Mono.empty();
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
index 0f0244f..7a3f7ea 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
@@ -168,7 +168,7 @@ public class CassandraMailboxMapper implements MailboxMapper {
             .filter(isCreated -> isCreated)
             .flatMap(mailboxHasCreated -> persistMailboxEntity(cassandraMailbox)
                 .thenReturn(true))
-            .switchIfEmpty(Mono.just(false));
+            .defaultIfEmpty(false);
     }
 
     @Override
@@ -196,7 +196,7 @@ public class CassandraMailboxMapper implements MailboxMapper {
                 .flatMap(mailboxHasCreated -> deletePreviousMailboxPathReference(mailbox.generateAssociatedPath())
                     .then(persistMailboxEntity(cassandraMailbox))
                     .thenReturn(true))
-                .switchIfEmpty(Mono.just(false)))
+                .defaultIfEmpty(false))
             .switchIfEmpty(Mono.error(() -> new MailboxNotFoundException(cassandraId)));
     }
 
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
index 11b6045..a6275e3 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
@@ -210,7 +210,7 @@ public class CassandraMessageIdDAO {
         return row
                 .map(this::fromRowToComposedMessageIdWithFlags)
                 .map(Optional::of)
-                .switchIfEmpty(Mono.just(Optional.empty()));
+                .defaultIfEmpty(Optional.empty());
     }
 
     private Mono<Row> selectOneRow(CassandraId mailboxId, MessageUid uid) {
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesService.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesService.java
index b740233..29c1792 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesService.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesService.java
@@ -128,7 +128,7 @@ public class SolveMailboxInconsistenciesService {
                     context.addFixedInconsistency(pathRegistration.getCassandraId());
                 })
                 .map(any -> Result.COMPLETED)
-                .switchIfEmpty(Mono.just(Result.COMPLETED))
+                .defaultIfEmpty(Result.COMPLETED)
                 .onErrorResume(e -> {
                     LOGGER.error("Failed fixing inconsistency for orphan mailboxPath {} - {}",
                         pathRegistration.getCassandraId().serialize(),
@@ -410,7 +410,7 @@ public class SolveMailboxInconsistenciesService {
                 // Path entry references another mailbox.
                 return new ConflictingEntryInconsistency(mailbox, pathRegistration);
             })
-            .switchIfEmpty(Mono.just(new OrphanMailboxDAOEntry(mailbox)));
+            .defaultIfEmpty(new OrphanMailboxDAOEntry(mailbox));
     }
 
     private Mono<Inconsistency> detectInconsistency(CassandraIdAndPath pathRegistration) {
@@ -422,6 +422,6 @@ public class SolveMailboxInconsistenciesService {
                 // Mailbox references another path
                 return new ConflictingEntryInconsistency(mailbox, pathRegistration);
             })
-            .switchIfEmpty(Mono.just(new OrphanMailboxPathDAOEntry(pathRegistration)));
+            .defaultIfEmpty(new OrphanMailboxPathDAOEntry(pathRegistration));
     }
 }
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java
index cbb51b9..ec134e7 100644
--- a/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/sieve/cassandra/CassandraSieveRepository.java
@@ -101,7 +101,7 @@ public class CassandraSieveRepository implements SieveRepository {
     private Mono<Long> spaceThatWillBeUsedByNewScript(Username username, ScriptName name, long scriptSize) {
         return cassandraSieveDAO.getScript(username, name)
             .map(Script::getSize)
-            .switchIfEmpty(Mono.just(0L))
+            .defaultIfEmpty(0L)
             .map(sizeOfStoredScript -> scriptSize - sizeOfStoredScript);
     }
 
@@ -199,7 +199,7 @@ public class CassandraSieveRepository implements SieveRepository {
     @Override
     public void deleteScript(Username username, ScriptName name) throws ScriptNotFoundException, IsActiveException {
         ensureIsNotActive(username, name);
-        if (!cassandraSieveDAO.deleteScriptInCassandra(username, name).switchIfEmpty(Mono.just(false)).block()) {
+        if (!cassandraSieveDAO.deleteScriptInCassandra(username, name).defaultIfEmpty(false).block()) {
             throw new ScriptNotFoundException();
         }
     }
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java
index 364d1be..0f974bc 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java
@@ -155,7 +155,7 @@ public class MessageFullViewFactory implements MessageViewFactory<MessageFullVie
     private Mono<MessageFastViewPrecomputedProperties> computeProjection(MessageContent messageContent, Supplier<Boolean> hasAttachments) {
         return Mono.justOrEmpty(mainTextContent(messageContent))
             .map(Preview::compute)
-            .switchIfEmpty(Mono.just(Preview.EMPTY))
+            .defaultIfEmpty(Preview.EMPTY)
             .map(extractedPreview -> MessageFastViewPrecomputedProperties.builder()
                 .preview(extractedPreview)
                 .hasAttachment(hasAttachments.get())
diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/CassandraMailQueueViewTestFactory.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/CassandraMailQueueViewTestFactory.java
index a43c6c6..71b1e99 100644
--- a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/CassandraMailQueueViewTestFactory.java
+++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/CassandraMailQueueViewTestFactory.java
@@ -66,7 +66,7 @@ public class CassandraMailQueueViewTestFactory {
         BrowseStartDAO browseStartDao = new BrowseStartDAO(session);
         return browseStartDao.findBrowseStart(mailQueueName)
             .map(Optional::ofNullable)
-            .switchIfEmpty(Mono.just(Optional.empty()))
+            .defaultIfEmpty(Optional.empty())
             .block()
             .isPresent();
     }


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


[james-project] 01/14: [Performance] Enhance AppendProcessor performances

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 4c9238ac5307c598cb4c83438fa32cedd63fbaef
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Mar 31 10:33:40 2020 +0700

    [Performance] Enhance AppendProcessor performances
    
    Using glowroot, a huge amount of time is used getting mailbox information
    
    count   time     Query
    2,128	 12,604.5 SELECT FROM acl WHERE id=:id;
    2,128	  9,120.9 SELECT FROM mailboxPathV2 WHERE namespace=:nam.
    2,128	  5,873.4 SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;
    1,063	  5,653.2 UPDATE modseq SET nextModseq=:nextModseq WHERE mailboxId=:mailboxId IF nextModseq=:...
    
    This changeset allow dividing by 2 the reads on mailbox potentially saving
    many useless Cassandra queries.
---
 .../src/main/java/org/apache/james/imap/processor/AppendProcessor.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
index fd869cb..2b348fd 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
@@ -126,7 +126,6 @@ public class AppendProcessor extends AbstractMailboxProcessor<AppendRequest> {
         try {
             final MailboxSession mailboxSession = session.getMailboxSession();
             final SelectedMailbox selectedMailbox = session.getSelected();
-            final MailboxManager mailboxManager = getMailboxManager();
             final boolean isSelectedMailbox = selectedMailbox != null && selectedMailbox.getMailboxId().equals(mailbox.getId());
             final ComposedMessageId messageId = mailbox.appendMessage(message, datetime, mailboxSession, !isSelectedMailbox, flagsToBeSet);
             if (isSelectedMailbox) {
@@ -134,7 +133,7 @@ public class AppendProcessor extends AbstractMailboxProcessor<AppendRequest> {
             }
 
             // get folder UIDVALIDITY
-            UidValidity uidValidity = mailboxManager.getMailbox(mailboxPath, mailboxSession)
+            UidValidity uidValidity = mailbox
                 .getMailboxEntity()
                 .getUidValidity();
 


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


[james-project] 14/14: Write an ADR about James polyglot strategy

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 30092e66f4fbd493b283c44ccb715c34926267d1
Author: Matthieu Baechler <ma...@apache.org>
AuthorDate: Mon Feb 3 11:43:27 2020 +0100

    Write an ADR about James polyglot strategy
---
 src/adr/0024-polyglot-strategy.md | 161 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/src/adr/0024-polyglot-strategy.md b/src/adr/0024-polyglot-strategy.md
new file mode 100644
index 0000000..fac483e
--- /dev/null
+++ b/src/adr/0024-polyglot-strategy.md
@@ -0,0 +1,161 @@
+# 24. Polyglot codebase
+
+Date: 2020-03-17
+
+## Status
+
+Proposed
+
+## Context & Problem Statement
+
+James is written in Java for a very long time. In recent years, Java modernized a lot after a decade of slow progress.
+
+However, in the meantime, most software relying on the JVM started supporting alternative JVM languages to keep being relevant.
+
+It includes Groovy, Clojure, Scala and more recently Kotlin, to name a few.
+
+Not being open to those alternative languages can be a problem for James adoption.
+
+## Decision drivers
+
+Nowadays, libraries and framework targeting the JVM are expected to support usage of one or several of these alternative languages.
+
+James being not only a mail server but also a development framework needs to reach those expectations.
+
+At the same time, more and more developers and languages adopt Function Programming (FP) idioms to solve their problems.
+
+## Considered options
+
+### Strategies
+
+1. Let the users figure out how to make polyglot setups
+2. Document the usage of polyglot mailets for some popular languages
+3. Document the usage of polyglot components for some popular languages
+4. Actually implement some mailets in some popular languages
+5. Actually implement some components in some popular languages
+
+### Languages:
+
+[upperroman]
+I. Clojure
+II. Groovy
+III. Kotlin
+IV. Scala
+
+## Decision
+
+We decide for options 4, 5 and IV.
+
+That means we need to write some mailets in Scala and demonstrate how it's done and then used in a running server.
+
+It also means writing and/or refactoring some server components in Scala, starting where it's the most relevant.
+
+### Positive Consequences 
+
+* Modernize parts of James code
+* Leverage Scala richer FP ecosystem and language to overcome Java limitations on that topic
+* Should attract people that would not like Java
+
+### Negative Consequences 
+
+* Adds even more knowledge requirements to contribute to James
+* Scala build time is longer than Java build time
+
+## Pros and Cons of the Options
+
+### Option 1: Let the users figure out how to make polyglot setups
+
+Pros:
+* We don't have anything new to do
+
+Cons:
+* It's boring, we like new challenges
+* Java is declining despite language modernization, it means in the long term, less and less people will contribute to James
+
+### Option 2: Document the usage of polyglot mailets for some popular languages
+
+Pros:
+* It's not a lot of work and yet it opens James to alternatives and can attract people outside Java developers community
+
+Cons:
+* Documentation without implementation often gets outdated when things move forward
+* We don't really gain knowledge on the polyglot matters as a community and won't be able to help users much
+
+### Option 3: Document the usage of polyglot components for some popular languages
+
+Pros:
+* It opens James to alternatives and can attract people outside Java developers community
+
+Cons:
+* Documentation without implementation often gets outdated when things move forward
+* For such complex subject it's probably harder to do than actually implementing a component in another language
+* We don't really gain knowledge on the polyglot matters as a community and won't be able to help users much
+
+### Option 4: Actually implement some mailets in some popular languages
+
+Pros:
+* It's probably not a lot of work, a mailet is just a simple class, probably easy to do in most JVM language
+* It makes us learn how it works and maybe will help us go further than the basic polyglot experience by doing small
+enhancements to the codebase
+* We can document the process and illustrate with some actual code
+* It opens James to alternatives and can attract people outside Java developers community
+
+Cons:
+* It can have a negative impact on the build time and dependencies download
+
+### Option 5: Actually implement some components in some popular languages
+
+Pros:
+* Leverage a modern language for some complex components
+* It makes us learn how it works and maybe will help us go further than the basic polyglot experience by doing small
+enhancements to the codebase
+* We can document the process and illustrate with some actual code
+* It opens James to alternatives and can attract people outside Java developers community
+
+Cons:
+* It makes the codebase more complex, requiring knowledge in another language
+* It can have a negative impact on the build time and dependencies download
+
+### Option I: Clojure
+
+Pros:
+* Functional Language
+
+Cons:
+* Weak popularity
+* No prior experience among current active commiters
+* Not statically typed hence less likely to fit the size of the project
+
+### Option II: Groovy
+
+Pros:
+* More advanced than current Java on most topics
+
+Cons:
+* No prior experience among current active commiters
+* Not very FP
+* Replaced in JVM community by Kotlin last years
+
+### Option III. Kotlin
+
+Pros:
+* Great Intellij support
+* Most of the good parts of Scala
+* FP-ish with Arrow
+* Coroutine for handling high-performance IOs
+
+Cons:
+* No prior experience among current active commiters
+* Lack of some FP constructs like proper Pattern Matching, persistent collections 
+* Despite progress done by Arrow, Kotlin community aims mostly at writing "better Java"
+
+#### Option IV. Scala
+
+Pros:
+* Rich FP community and ecosystem
+* Existing knowledge among current active commiters
+
+Cons:
+* Needs work to master
+* Can be slow to build
+* 3.0 will probably require code changes


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


[james-project] 03/14: [Performance] Use MailboxId rather than MailboxPath in IMAP logs

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 b761946870256b395ae1a4767e1507d3e474af6a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Mar 31 11:43:30 2020 +0700

    [Performance] Use MailboxId rather than MailboxPath in IMAP logs
    
    Retrieving the mailboxPath of the selected mailbox requires some backend
    queries, and the mailboxPath is mutable.
    
    Retrieving the mailboxId of the selected mailbox does not require backend
    queries, and the mailboxId is immutable.
---
 .../org/apache/james/imap/processor/AbstractMailboxProcessor.java     | 2 +-
 .../org/apache/james/imap/processor/AbstractSelectionProcessor.java   | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
index 487e347..12c7733 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
@@ -247,7 +247,7 @@ public abstract class AbstractMailboxProcessor<R extends ImapRequest> extends Ab
             final MessageUid uid = mr.getUid();
             int msn = selected.msn(uid);
             if (msn == SelectedMailbox.NO_SUCH_MESSAGE) {
-                LOGGER.debug("No message found with uid {} in the uid<->msn mapping for mailbox {}. This may be because it was deleted by a concurrent session. So skip it..", uid, selected.getPath().asString());
+                LOGGER.debug("No message found with uid {} in the uid<->msn mapping for mailbox {}. This may be because it was deleted by a concurrent session. So skip it..", uid, selected.getMailboxId().serialize());
                 // skip this as it was not found in the mapping
                 // 
                 // See IMAP-346
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
index affcb1e..5441cc5 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
@@ -135,7 +135,7 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ
         while (unseen(responder, firstUnseen, selected) == false) {
             // if we not was able to get find the unseen within 5 retries we should just not send it
             if (retryCount == 5) {
-                LOGGER.info("Unable to uid for unseen message {} in mailbox {}", firstUnseen, selected.getPath());
+                LOGGER.info("Unable to uid for unseen message {} in mailbox {}", firstUnseen, selected.getMailboxId().serialize());
                 break;
             }
             firstUnseen = selectMailbox(fullMailboxPath, session).getFirstUnseen();
@@ -352,7 +352,7 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ
             int msn = selected.msn(unseenUid);
 
             if (msn == SelectedMailbox.NO_SUCH_MESSAGE) {
-                LOGGER.debug("No message found with uid {} in mailbox {}", unseenUid, selected.getPath().asString());
+                LOGGER.debug("No message found with uid {} in mailbox {}", unseenUid, selected.getMailboxId().serialize());
                 return false;
             } 
 


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


[james-project] 02/14: [Performance] Enhance SelectProcessor performances

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 4e581f2f41b88a505852177c635a624fdcca36bf
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Mar 31 11:11:30 2020 +0700

    [Performance] Enhance SelectProcessor performances
    
    Using GlowRoot, a huge amount of time is used getting mailbox information
    
    We can reasonably expect from it a 8% execution time reduction
    
    Query                                                                                   Time        Count
    SELECT messageId,mailboxId,uid,modSeq,flagAnswered,flagDeleted,flagDraft,flagFlagge... 	7,068.1 	599
    SELECT namespace,user,mailboxName,mailboxId FROM mailboxPathV2 WHERE namespace=:nam... 	4,489.1 	1,202
    SELECT acl,version FROM acl WHERE id=:id; 	                                            3,791.9 	1,202
    SELECT nextModseq FROM modseq WHERE mailboxId=:mailboxId; 	                            2,847.5 	936
    SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id; 	                    2,792.2 	1,202
---
 .../org/apache/james/imap/processor/AbstractSelectionProcessor.java   | 2 +-
 .../org/apache/james/imap/processor/base/SelectedMailboxImpl.java     | 3 +--
 .../apache/james/imap/processor/base/MailboxEventAnalyserTest.java    | 2 +-
 .../org/apache/james/imap/processor/base/SelectedMailboxImplTest.java | 4 ++--
 4 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
index 11edf91..affcb1e 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
@@ -399,7 +399,7 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ
             if (currentMailbox != null) {
                 getStatusResponseFactory().untaggedOk(HumanReadableText.QRESYNC_CLOSED, ResponseCode.closed());
             }
-            session.selected(new SelectedMailboxImpl(getMailboxManager(), eventBus, session, mailboxPath));
+            session.selected(new SelectedMailboxImpl(getMailboxManager(), eventBus, session, mailbox));
 
             sessionMailbox = session.getSelected();
             
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
index 741cdb5..a8ca86f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
@@ -74,7 +74,7 @@ public class SelectedMailboxImpl implements SelectedMailbox, MailboxListener {
     private final Flags applicableFlags;
     private boolean applicableFlagsChanged;
 
-    public SelectedMailboxImpl(MailboxManager mailboxManager, EventBus eventBus, ImapSession session, MailboxPath path) throws MailboxException {
+    public SelectedMailboxImpl(MailboxManager mailboxManager, EventBus eventBus, ImapSession session, MessageManager messageManager) throws MailboxException {
         this.session = session;
         this.sessionId = session.getMailboxSession().getSessionId();
         this.mailboxManager = mailboxManager;
@@ -86,7 +86,6 @@ public class SelectedMailboxImpl implements SelectedMailbox, MailboxListener {
 
         uidMsnConverter = new UidMsnConverter();
 
-        MessageManager messageManager = mailboxManager.getMailbox(path, mailboxSession);
         mailboxId = messageManager.getId();
 
         registration = eventBus.register(this, new MailboxIdRegistrationKey(mailboxId));
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
index 8538203..f15d929 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
@@ -158,7 +158,7 @@ public class MailboxEventAnalyserTest {
         when(messageManager.getMessages(any(), any(), any()))
             .thenReturn(new SingleMessageResultIterator(messageResult));
 
-        testee = new SelectedMailboxImpl(mailboxManager, eventBus, imapSession, MAILBOX_PATH);
+        testee = new SelectedMailboxImpl(mailboxManager, eventBus, imapSession, messageManager);
     }
 
     @Test
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
index 7144f3f..f93ffc3 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
@@ -122,7 +122,7 @@ public class SelectedMailboxImplTest {
             mailboxManager,
             eventBus,
             imapSession,
-            mailboxPath);
+            messageManager);
 
         assertThat(selectedMailbox.getLastUid().get()).isEqualTo(EMITTED_EVENT_UID);
     }
@@ -138,7 +138,7 @@ public class SelectedMailboxImplTest {
             mailboxManager,
             eventBus,
             imapSession,
-            mailboxPath);
+            messageManager);
 
         assertThat(successCount.get())
             .as("Get the incremented value in case of successful event processing.")


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


[james-project] 07/14: JAMES-3129 Sending mail via JMAP: Add test for Outbox & Sent 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 b927b4776b57a5a981e2cac71c105ee1ddade193
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 27 15:34:46 2020 +0700

    JAMES-3129 Sending mail via JMAP: Add test for Outbox & Sent counters
---
 .../methods/integration/SetMessagesMethodTest.java | 93 ++++++++++++++++++++++
 .../org/apache/james/jmap/JmapCommonRequests.java  | 10 ++-
 2 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
index b82aa58..c2e8ff3 100644
--- a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
@@ -30,6 +30,7 @@ import static org.apache.james.jmap.JMAPTestingConstants.BOB;
 import static org.apache.james.jmap.JMAPTestingConstants.BOB_PASSWORD;
 import static org.apache.james.jmap.JMAPTestingConstants.DOMAIN;
 import static org.apache.james.jmap.JMAPTestingConstants.DOMAIN_ALIAS;
+import static org.apache.james.jmap.JMAPTestingConstants.FIRST_MAILBOX;
 import static org.apache.james.jmap.JMAPTestingConstants.LOCALHOST_IP;
 import static org.apache.james.jmap.JMAPTestingConstants.NAME;
 import static org.apache.james.jmap.JMAPTestingConstants.SECOND_ARGUMENTS;
@@ -39,6 +40,7 @@ import static org.apache.james.jmap.JmapCommonRequests.getDraftId;
 import static org.apache.james.jmap.JmapCommonRequests.getInboxId;
 import static org.apache.james.jmap.JmapCommonRequests.getMailboxId;
 import static org.apache.james.jmap.JmapCommonRequests.getOutboxId;
+import static org.apache.james.jmap.JmapCommonRequests.getSentId;
 import static org.apache.james.jmap.JmapCommonRequests.getSetMessagesUpdateOKResponseAssertions;
 import static org.apache.james.jmap.JmapURIBuilder.baseUri;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -79,6 +81,7 @@ import org.apache.james.core.Username;
 import org.apache.james.core.quota.QuotaSizeLimit;
 import org.apache.james.jmap.AccessToken;
 import org.apache.james.jmap.HttpJmapAuthentication;
+import org.apache.james.jmap.JmapCommonRequests;
 import org.apache.james.jmap.MessageAppender;
 import org.apache.james.jmap.draft.JmapGuiceProbe;
 import org.apache.james.jmap.draft.MessageIdProbe;
@@ -1047,6 +1050,96 @@ public abstract class SetMessagesMethodTest {
             ;
     }
 
+    @Category(BasicFeature.class)
+    @Test
+    public void sendingAMailShouldLeadToAppropriateMailboxCountersOnOutbox() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME.asString();
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\"," +
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"Thank you for joining example.com!\"," +
+            "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        with()
+            .header("Authorization", accessToken.asString())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, getSentId(accessToken)));
+
+        given()
+            .header("Authorization", accessToken.asString())
+            .body("[[\"getMailboxes\", {" +
+                "  \"ids\": [\"" + getOutboxId(accessToken) + "\"], " +
+                "  \"properties\" : [\"unreadMessages\", \"totalMessages\"]}, " +
+                "\"#0\"]]")
+            .log().ifValidationFails()
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxes"))
+            .body(FIRST_MAILBOX + ".totalMessages", equalTo(0))
+            .body(FIRST_MAILBOX + ".unreadMessages", equalTo(0));
+    }
+
+    @Category(BasicFeature.class)
+    @Test
+    public void sendingAMailShouldLeadToAppropriateMailboxCountersOnSent() {
+        String messageCreationId = "creationId1337";
+        String fromAddress = USERNAME.asString();
+        String requestBody = "[" +
+            "  [" +
+            "    \"setMessages\"," +
+            "    {" +
+            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+            "        \"subject\": \"Thank you for joining example.com!\"," +
+            "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        with()
+            .header("Authorization", accessToken.asString())
+            .body(requestBody)
+            .post("/jmap");
+
+        calmlyAwait.until(
+            () -> JmapCommonRequests.isAnyMessageFoundInRecipientsMailbox(accessToken, getSentId(accessToken)));
+
+        given()
+            .header("Authorization", accessToken.asString())
+            .body("[[\"getMailboxes\", {" +
+                "  \"ids\": [\"" + getSentId(accessToken) + "\"], " +
+                "  \"properties\" : [\"unreadMessages\", \"totalMessages\"]}, " +
+                "\"#0\"]]")
+            .log().ifValidationFails()
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("mailboxes"))
+            .body(FIRST_MAILBOX + ".totalMessages", equalTo(1))
+            .body(FIRST_MAILBOX + ".unreadMessages", equalTo(0));
+    }
+
     @Test
     public void setMessagesShouldReturnCreatedMessageWithEmptySubjectWhenSubjectIsNull() {
         String messageCreationId = "creationId1337";
diff --git a/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java b/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java
index 3ed86ad..72138fe 100644
--- a/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java
+++ b/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java
@@ -48,6 +48,10 @@ public class JmapCommonRequests {
         return getMailboxId(accessToken, Role.OUTBOX);
     }
 
+    public static String getSentId(AccessToken accessToken) {
+        return getMailboxId(accessToken, Role.SENT);
+    }
+
     public static String getDraftId(AccessToken accessToken) {
         return getMailboxId(accessToken, Role.DRAFTS);
     }
@@ -89,10 +93,14 @@ public class JmapCommonRequests {
     }
 
     public static boolean isAnyMessageFoundInRecipientsMailbox(AccessToken recipientToken, MailboxId mailboxId) {
+        return isAnyMessageFoundInRecipientsMailbox(recipientToken, mailboxId.serialize());
+    }
+
+    public static boolean isAnyMessageFoundInRecipientsMailbox(AccessToken recipientToken, String serialize) {
         try {
             with()
                 .header("Authorization", recipientToken.asString())
-                .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + mailboxId.serialize() + "\"]}}, \"#0\"]]")
+                .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + serialize + "\"]}}, \"#0\"]]")
             .when()
                 .post("/jmap")
             .then()


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


[james-project] 05/14: JAMES-2813 Task executionListing is enough

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 1d0368624ba66eecd9fd206c7d76800a2dd2b93f
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Mar 30 11:08:28 2020 +0700

    JAMES-2813 Task executionListing is enough
    
    We already have the item in the list, we don't need an extra DB read per
    item to read it.
---
 .../org/apache/james/task/eventsourcing/EventSourcingTaskManager.scala | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/server/task/task-memory/src/main/scala/org/apache/james/task/eventsourcing/EventSourcingTaskManager.scala b/server/task/task-memory/src/main/scala/org/apache/james/task/eventsourcing/EventSourcingTaskManager.scala
index 87d8d55..ad5d848 100644
--- a/server/task/task-memory/src/main/scala/org/apache/james/task/eventsourcing/EventSourcingTaskManager.scala
+++ b/server/task/task-memory/src/main/scala/org/apache/james/task/eventsourcing/EventSourcingTaskManager.scala
@@ -25,8 +25,8 @@ import java.util
 import com.google.common.annotations.VisibleForTesting
 import javax.annotation.PreDestroy
 import javax.inject.Inject
-import org.apache.james.eventsourcing.{AggregateId, EventSourcingSystem, Subscriber}
 import org.apache.james.eventsourcing.eventstore.{EventStore, History}
+import org.apache.james.eventsourcing.{AggregateId, EventSourcingSystem, Subscriber}
 import org.apache.james.lifecycle.api.Startable
 import org.apache.james.task.TaskManager.ReachedTimeoutException
 import org.apache.james.task._
@@ -90,7 +90,6 @@ class EventSourcingTaskManager @Inject @VisibleForTesting private[eventsourcing]
 
   private def listScala: List[TaskExecutionDetails] = executionDetailsProjection
     .list
-    .flatMap(details => executionDetailsProjection.load(details.taskId))
 
   override def cancel(id: TaskId): Unit = {
     val command = RequestCancel(id)


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


[james-project] 11/14: ADR: Fixing Cassandra message inconsistencies

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 f5569fd028561eeb3d0b3c4ae9c3367a53d84742
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Feb 27 13:53:26 2020 +0700

    ADR: Fixing Cassandra message inconsistencies
---
 src/adr/0022-cassandra-message-inconsistency.md | 94 +++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/src/adr/0022-cassandra-message-inconsistency.md b/src/adr/0022-cassandra-message-inconsistency.md
new file mode 100644
index 0000000..8634048
--- /dev/null
+++ b/src/adr/0022-cassandra-message-inconsistency.md
@@ -0,0 +1,94 @@
+# 22. Cassandra Message inconsistencies
+
+Date: 2020-02-27
+
+## Status
+
+Proposed
+
+## Context
+
+Messages are denormalized in Cassandra in order to:
+
+ - access them by their unique identifier (messageId), for example through the JMAP protocol
+ - access them by their mailbox identifier and Unique IDentifier within that mailbox (mailboxId + uid), for example 
+ through the IMAP protocol
+
+Here is the table organisation:
+
+ - `messageIdTable` Holds mailbox and flags for each message, lookup by mailbox ID + UID
+ - `imapUidTable` Holds mailbox and flags for each message, lookup by message ID
+
+Failures during the denormalization process will lead to inconsistencies between the two tables.
+
+This can lead to the following user experience:
+
+```
+BOB receives a message
+The denormalization process fails
+BOB can read the message via JMAP
+BOB cannot read the message via IMAP
+
+BOB marks a message as SEEN
+The denormalization process fails
+The message is SEEN in JMAP
+The message is UNSEEN in IMAP
+```
+
+### Current operations
+
+ - Adding a message:
+   - (CassandraMessageMapper) First reference the message in `messageIdTable` then in `imapUidTable`.
+   - (CassandraMessageIdMapper) First reference the message in `imapUidTable` then in `messageIdTable`.
+ - Deleting a message:
+   - (CassandraMessageMapper) First delete the message in `imapUidTable` then in `messageIdTable`.
+   - (CassandraMessageIdMapper) Read the message metadata using `imapUidTable`, then first delete the message in 
+   `imapUidTable` then in `messageIdTable`.
+ - Copying a message:
+   - (CassandraMessageMapper) Read the message first, then first reference the message in `messageIdTable` then
+    in `imapUidTable`.
+ - Moving a message:
+   - (CassandraMessageMapper) Logically copy then delete. A failure in the chain migh lead to duplicated message (present 
+   in both source and destination mailbox) as well as different view in IMAP/JMAP.
+   - (CassandraMessageIdMapper) First reference the message in `imapUidTable` then in `messageIdTable`.
+ - Updating a message flags:
+   - (CassandraMessageMapper) First update conditionally the message in `imapUidTable` then in `messageIdTable`.
+   - (CassandraMessageIdMapper) First update conditionally the message in `imapUidTable` then in `messageIdTable`.
+
+## Decision
+
+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.
+
+We thus need to modify CassandraMessageMapper 'add' + 'copy' to first write to the source of truth (`imapUidTable`)
+
+We can adopt a retry policy of the `messageIdTable` projection update as a mitigation strategy.
+
+Using `imapUidTable` table as a source of truth, we can rebuild the `messageIdTable` projection:
+
+ - Iterating `imapUidTable` entries, we can rewrite entries in `messageIdTable`
+ - Iterating `messageIdTable` we can remove entries not referenced in `imapUidTable`
+ - Adding a delay and a re-check before the actual fix can decrease the occurrence of concurrency issues
+
+We will expose a webAdmin task for doing this.
+
+## Consequences
+
+User actions concurrent to the inconsistency fixing task could result in concurrency issues. New inconsistencies could be
+created. However table of truth would not be impacted hence rerunning the inconsistency fixing task will eventually fix 
+all issues.
+
+This task could be run safely online and can be scheduled on a recurring basis outside of peak traffic by an admin to
+ensure Cassandra message consistency.
+
+## References
+
+* [Plan for fixing Cassandra ACL inconsistencies](https://github.com/linagora/james-project/pull/3125)
+
+* [General mailing list discussion about inconsistencies](https://www.mail-archive.com/server-dev@james.apache.org/msg64432.html)
+
+* [Pull Request: JAMES-3058 Concurrency testing for fixing Cassandra mailbox inconsistencies](https://github.com/linagora/james-project/pull/3130)
+
+The delay strategy to decrease concurrency issue occurrence is described here.
\ 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] 12/14: [ADR] Recomputing 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 79cc79b0feab6880bb3c15f4943b598169de51cd
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Mar 7 15:00:52 2020 +0700

    [ADR] Recomputing mailbox counters
---
 ...3-cassandra-mailbox-counters-inconsistencies.md | 62 ++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/src/adr/0023-cassandra-mailbox-counters-inconsistencies.md b/src/adr/0023-cassandra-mailbox-counters-inconsistencies.md
new file mode 100644
index 0000000..377ff17
--- /dev/null
+++ b/src/adr/0023-cassandra-mailbox-counters-inconsistencies.md
@@ -0,0 +1,62 @@
+# 23. Cassandra Mailbox Counters inconsistencies
+
+Date: 2020-03-07
+
+## Status
+
+Proposed
+
+## Context
+
+Cassandra maintains a per mailbox projection for message count and unseen message count.
+
+As with any projection, it can go out of sync, leading to inconsistent results being returned to the client, which is not acceptable.
+
+Here is the table organisation:
+
+ - `mailbox` Lists the mailboxes
+ - `messageIdTable` Holds mailbox and flags for each message, lookup by mailbox ID + UID
+ - `imapUidTable` Holds mailbox and flags for each message, lookup by message ID and serves as a source of truth
+ - `mailboxCounters` Holds messages count and unseen message count for each mailbox.
+ 
+Failures during the denormalization process will lead to inconsistencies between the counts and the content of `imapUidTable`
+
+This can lead to the following user experience:
+
+ - Invalid message count can be reported in the Mail User Agent (IMAP & JMAP)
+ - Invalid message unseen count can be reported in the Mail User Agent (IMAP & JMAP)
+
+## Decision
+
+Implement a webadmin exposed task to recompute mailbox counters.
+
+This endpoints will:
+
+ - List existing mailboxes
+ - List their messages using `messageIdTable`
+ - Check them against their source of truth `imapUidTable`
+ - Compute mailbox counter values
+ - And reset the value of the counter if needed in `mailboxCounters`
+
+## Consequences
+
+This endpoint is subject to data races in the face of concurrent operations. Concurrent increments & decrements will be 
+ignored during a single mailbox processing. However the source of truth is unaffected hence, upon rerunning the task, 
+the result will be eventually correct. To be noted that Cassandra counters can't be reset in an atomic manner anyway.
+
+We rely on the "listing messages by mailbox" projection (that we recheck). Missing entries in there will
+be ignored until the given projection is healed (currently unsupported). 
+
+We furthermore can piggy back a partial check of the message denormalization described in 
+[this ADR](0021-cassandra-acl-inconsistency.md) upon counter recomputation (partial because 
+we cannot detect missing entries in the "list messages in mailbox" denormalization table)
+
+## References
+
+* [Plan for fixing Cassandra ACL inconsistencies](https://github.com/linagora/james-project/pull/3125)
+
+* [General mailing list discussion about inconsistencies](https://www.mail-archive.com/server-dev@james.apache.org/msg64432.html)
+
+* [JAMES-3105 Related JIRA](https://issues.apache.org/jira/browse/JAMES-3105)
+
+* [Pull Request: JAMES-3105 Corrective task for fixing mailbox counters](https://github.com/linagora/james-project/pull/3185)
\ 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] 09/14: JAMES-3022 Change default log file after log4j2 upgrade

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 7682112258dc2e0b7322bfcf9cb44c1d287af422
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Mar 24 08:05:12 2020 +0100

    JAMES-3022 Change default log file after log4j2 upgrade
---
 CHANGELOG.md                                       |   1 +
 .../run/spring/destination/conf/log4j.properties   | 136 ------
 dockerfiles/run/spring/destination/conf/log4j2.xml | 486 +++++++++++++++++++++
 server/app/src/main/resources/log4j.properties     | 154 -------
 server/app/src/main/resources/log4j2.xml           | 486 +++++++++++++++++++++
 upgrade-instructions.md                            |   9 +-
 6 files changed, 979 insertions(+), 293 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c61c434..30261cb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ of tasks being currently executed.
 - It is now forbidden to create new Usernames with the following set of characters in its local part : `"(),:; <>@\[]`, as we prefer it to stay simple to handle. However, the read of Usernames already existing with some of those characters is still allowed, to not introduce any breaking change. See JAMES-2950.
 - Linshare blob export configuration and mechanism change. See JAMES-3040.
 - Differentiation between domain alias and domain mapping. Read upgrade instructions.
+- JAMES-3122 Log4J2 adoption for Spring product. Log file configuration needs to be updated. See upgrade instructions.
 
 ### Fixed
 - JAMES-2828 & JAMES-2929 bugs affecting JDBCMailRepository usage with PostgresSQL thanks to Jörg Thomas & Sergey B
diff --git a/dockerfiles/run/spring/destination/conf/log4j.properties b/dockerfiles/run/spring/destination/conf/log4j.properties
deleted file mode 100644
index e7a095b..0000000
--- a/dockerfiles/run/spring/destination/conf/log4j.properties
+++ /dev/null
@@ -1,136 +0,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.
-
-# See http://james.apache.org/server/3/config.html for usage
-
-log4j.rootLogger=DEBUG
-
-log4j.appender.CONS=org.apache.log4j.ConsoleAppender
-log4j.appender.CONS.layout=org.apache.log4j.PatternLayout
-log4j.appender.CONS.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.FILE.File=../log/james-server.log
-log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
-log4j.appender.FILE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILBOXMANAGER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILBOXMANAGER.File=../log/mailboxmanager.log
-log4j.appender.MAILBOXMANAGER.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILBOXMANAGER.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILBOXMANAGER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.IMAPSERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.IMAPSERVER.File=../log/imapserver.log
-log4j.appender.IMAPSERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.IMAPSERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.IMAPSERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILETCONTAINER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILETCONTAINER.File=../log/mailetcontainer.log
-log4j.appender.MAILETCONTAINER.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILETCONTAINER.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILETCONTAINER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.DNSSERVICE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.DNSSERVICE.File=../log/dnsservice.log
-log4j.appender.DNSSERVICE.DatePattern='.'yyyy-MM-dd
-log4j.appender.DNSSERVICE.layout=org.apache.log4j.PatternLayout
-log4j.appender.DNSSERVICE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.POP3SERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.POP3SERVER.File=../log/pop3server.log
-log4j.appender.POP3SERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.POP3SERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.POP3SERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.SMTPSERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.SMTPSERVER.File=../log/smtpserver.log
-log4j.appender.SMTPSERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.SMTPSERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.SMTPSERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.LMTPSERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.LMTPSERVER.File=../log/lmtpserver.log
-log4j.appender.LMTPSERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.LMTPSERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.LMTPSERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILREPOSITORYSTORE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILREPOSITORYSTORE.File=../log/mailrepositorystore.log
-log4j.appender.MAILREPOSITORYSTORE.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILREPOSITORYSTORE.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILREPOSITORYSTORE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.USERSREPOSITORY=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.USERSREPOSITORY.File=../log/usersrepository.log
-log4j.appender.USERSREPOSITORY.DatePattern='.'yyyy-MM-dd
-log4j.appender.USERSREPOSITORY.layout=org.apache.log4j.PatternLayout
-log4j.appender.USERSREPOSITORY.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.FETCHMAIL=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.FETCHMAIL.File=../log/fetchmail.log
-log4j.appender.FETCHMAIL.DatePattern='.'yyyy-MM-dd
-log4j.appender.FETCHMAIL.layout=org.apache.log4j.PatternLayout
-log4j.appender.FETCHMAIL.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.DOMAINLIST=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.DOMAINLIST.File=../log/domainlist.log
-log4j.appender.DOMAINLIST.DatePattern='.'yyyy-MM-dd
-log4j.appender.DOMAINLIST.layout=org.apache.log4j.PatternLayout
-log4j.appender.DOMAINLIST.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.VIRTUALUSERTABLE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.VIRTUALUSERTABLE.File=../log/virtualusertable.log
-log4j.appender.VIRTUALUSERTABLE.DatePattern='.'yyyy-MM-dd
-log4j.appender.VIRTUALUSERTABLE.layout=org.apache.log4j.PatternLayout
-log4j.appender.VIRTUALUSERTABLE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILQUEUEFACTORY=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILQUEUEFACTORY.File=../log/mailqueuefactory.log
-log4j.appender.MAILQUEUEFACTORY.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILQUEUEFACTORY.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILQUEUEFACTORY.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.logger.org.apache.xbean.spring=WARN, CONS, FILE
-log4j.logger.org.apache.activemq=WARN, CONS, FILE
-
-log4j.logger.org.apache.camel=WARN, CONS, FILE
-log4j.logger.org.springframework=WARN, CONS, FILE
-log4j.logger.org.apache.james=DEBUG, CONS, FILE
-
-log4j.logger=DEBUG, CONS, FILE
-
-log4j.logger.org.apache.james.mailbox=DEBUG, MAILBOXMANAGER
-log4j.logger.org.apache.james.imap=DEBUG, IMAPSERVER
-log4j.logger.org.apache.james.mailetcontainer=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.mailetcontext=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.transport=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.mailspooler=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.mailprocessor=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.dnsservice=DEBUG, DNSSERVICE
-log4j.logger.org.apache.james.pop3server=DEBUG, POP3SERVER
-log4j.logger.org.apache.james.smtpserver=DEBUG, SMTPSERVER
-log4j.logger.org.apache.james.lmtpserver=DEBUG, LMTPSERVER
-log4j.logger.org.apache.james.mailrepositorystore=DEBUG, MAILREPOSITORYSTORE
-log4j.logger.org.apache.james.usersrepository=DEBUG, USERSREPOSITORY
-log4j.logger.org.apache.james.fetchmail=DEBUG, FETCHMAIL
-log4j.logger.org.apache.james.domainlist=DEBUG, DOMAINLIST
-log4j.logger.org.apache.james.virtualusertable=DEBUG, VIRTUALUSERTABLE
-log4j.logger.org.apache.james.rrt=DEBUG, VIRTUALUSERTABLE
-log4j.logger.org.james.apache.mailqueue=DEBUG, MAILQUEUEFACTORY
-log4j.logger.etm.core.monitor.EtmMonitor= DEBUG, CONS, FILE
diff --git a/dockerfiles/run/spring/destination/conf/log4j2.xml b/dockerfiles/run/spring/destination/conf/log4j2.xml
new file mode 100644
index 0000000..c89ad2e
--- /dev/null
+++ b/dockerfiles/run/spring/destination/conf/log4j2.xml
@@ -0,0 +1,486 @@
+
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="INFO" monitorInterval="30">
+    <Properties>
+        <Property name="logDir">${sys:app.home}/log</Property>
+        <Property name="logLayoutTomcat">%d{dd-MMM-yyyy HH:mm:ss.SSS} %level [%t] %C.%M:%L - %msg%n</Property>
+        <Property name="logLayout1">%d %-7level %logger{36} - %msg%n</Property>
+        <Property name="logLayout2">%d %-7level [%t] %C.%M:%L - %msg%n</Property>
+    </Properties>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="${logLayoutTomcat}" />
+        </Console>
+        <RollingFile name="SpringFramework" fileName="${logDir}/springframework.log"
+                     filePattern="${logDir}/springframework.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="openjpa" fileName="${logDir}/openjpa.log"
+                     filePattern="${logDir}/openjpa.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="James" fileName="${logDir}/james.log"
+                     filePattern="${logDir}/james.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="TransportProtocols" fileName="${logDir}/james_transport-protocols.log"
+                     filePattern="${logDir}/james_transport-protocols.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILBOXMANAGER" fileName="${logDir}/james_mailboxmanager.log"
+                     filePattern="${logDir}/james_mailboxmanager.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="IMAPSERVER" fileName="${logDir}/james_imapserver.log"
+                     filePattern="${logDir}/james_imapserver.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="DNSSERVICE" fileName="${logDir}/james_dnsservice.log"
+                     filePattern="${logDir}/james_dnsservice.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="POP3SERVER" fileName="${logDir}/james_pop3server.log"
+                     filePattern="${logDir}/james_pop3server.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="SMTPSERVER" fileName="${logDir}/james_smtpserver.log"
+                     filePattern="${logDir}/james_smtpserver.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="LMTPSERVER" fileName="${logDir}/james_lmtpserver.log"
+                     filePattern="${logDir}/james_lmtpserver.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILREPOSITORYSTORE" fileName="${logDir}/james_mailrepositorystore.log"
+                     filePattern="${logDir}/james_mailrepositorystore.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="USERSREPOSITORY" fileName="${logDir}/james_usersrepository.log"
+                     filePattern="${logDir}/james_usersrepository.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="FETCHMAIL" fileName="${logDir}/james_fetchmail.log"
+                     filePattern="${logDir}/james_fetchmail.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="DOMAINLIST" fileName="${logDir}/james_domainlist.log"
+                     filePattern="${logDir}/james_domainlist.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="VIRTUALUSERTABLE" fileName="${logDir}/james_virtualusertable.log"
+                     filePattern="${logDir}/james_virtualusertable.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILQUEUEFACTORY" fileName="${logDir}/james_mailqueuefactory.log"
+                     filePattern="${logDir}/james_mailqueuefactory.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+    </Appenders>
+    <Loggers>
+        <Logger name="org.springframework" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="SpringFramework" level="info" />
+        </Logger>
+        <Logger name="etm.core.monitor.EtmMonitor" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="James" level="info" />
+        </Logger>
+        <Logger name="org.apache" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="James" level="info" />
+        </Logger>
+
+        <Logger name="openjpa" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="openjpa" level="trace" />
+        </Logger>
+        <Logger name="org.apache.openjpa" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="openjpa" level="trace" />
+        </Logger>
+
+        <Logger name="org.apache.james" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="James" level="trace" />
+        </Logger>
+
+        <Logger name="org.apache.james.transport" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="TransportProtocols" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.protocols" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="TransportProtocols" level="trace" />
+        </Logger>
+
+        <Logger name="org.apache.james.mailboxmanager" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILBOXMANAGER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.imapserver" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="IMAPSERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.dnsservice" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="DNSSERVICE" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.pop3server" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="POP3SERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.smtpserver" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="SMTPSERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.lmtpserver" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="LMTPSERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailrepositorystore" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILREPOSITORYSTORE" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.usersrepository" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="USERSREPOSITORY" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.fetchmail" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="FETCHMAIL" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.domainlist" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="DOMAINLIST" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.virtualusertable" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="VIRTUALUSERTABLE" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailqueuefactory" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILQUEUEFACTORY" level="trace" />
+        </Logger>
+
+        <Root level="info">
+            <AppenderRef ref="Console" level="info" />
+            <!-- Only events at DIAG level or more specific are sent to the console. -->
+            <!-- <AppenderRef ref="Console" level="diag" /> -->
+            <AppenderRef ref="James" level="trace" />
+            <AppenderRef ref="openjpa" level="trace" />
+        </Root>
+    </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/server/app/src/main/resources/log4j.properties b/server/app/src/main/resources/log4j.properties
deleted file mode 100644
index 06ed8b0..0000000
--- a/server/app/src/main/resources/log4j.properties
+++ /dev/null
@@ -1,154 +0,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.
-
-# See http://james.apache.org/server/3/config.html for usage
-#  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.
-
-# See http://james.apache.org/server/3/config.html for usage
-
-log4j.rootLogger=DEBUG
-
-log4j.appender.CONS=org.apache.log4j.ConsoleAppender
-log4j.appender.CONS.layout=org.apache.log4j.PatternLayout
-log4j.appender.CONS.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.FILE.File=../log/james-server.log
-log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
-log4j.appender.FILE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILBOXMANAGER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILBOXMANAGER.File=../log/mailboxmanager.log
-log4j.appender.MAILBOXMANAGER.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILBOXMANAGER.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILBOXMANAGER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.IMAPSERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.IMAPSERVER.File=../log/imapserver.log
-log4j.appender.IMAPSERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.IMAPSERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.IMAPSERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILETCONTAINER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILETCONTAINER.File=../log/mailetcontainer.log
-log4j.appender.MAILETCONTAINER.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILETCONTAINER.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILETCONTAINER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.DNSSERVICE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.DNSSERVICE.File=../log/dnsservice.log
-log4j.appender.DNSSERVICE.DatePattern='.'yyyy-MM-dd
-log4j.appender.DNSSERVICE.layout=org.apache.log4j.PatternLayout
-log4j.appender.DNSSERVICE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.POP3SERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.POP3SERVER.File=../log/pop3server.log
-log4j.appender.POP3SERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.POP3SERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.POP3SERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.SMTPSERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.SMTPSERVER.File=../log/smtpserver.log
-log4j.appender.SMTPSERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.SMTPSERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.SMTPSERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.LMTPSERVER=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.LMTPSERVER.File=../log/lmtpserver.log
-log4j.appender.LMTPSERVER.DatePattern='.'yyyy-MM-dd
-log4j.appender.LMTPSERVER.layout=org.apache.log4j.PatternLayout
-log4j.appender.LMTPSERVER.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILREPOSITORYSTORE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILREPOSITORYSTORE.File=../log/mailrepositorystore.log
-log4j.appender.MAILREPOSITORYSTORE.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILREPOSITORYSTORE.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILREPOSITORYSTORE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.USERSREPOSITORY=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.USERSREPOSITORY.File=../log/usersrepository.log
-log4j.appender.USERSREPOSITORY.DatePattern='.'yyyy-MM-dd
-log4j.appender.USERSREPOSITORY.layout=org.apache.log4j.PatternLayout
-log4j.appender.USERSREPOSITORY.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.FETCHMAIL=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.FETCHMAIL.File=../log/fetchmail.log
-log4j.appender.FETCHMAIL.DatePattern='.'yyyy-MM-dd
-log4j.appender.FETCHMAIL.layout=org.apache.log4j.PatternLayout
-log4j.appender.FETCHMAIL.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.DOMAINLIST=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.DOMAINLIST.File=../log/domainlist.log
-log4j.appender.DOMAINLIST.DatePattern='.'yyyy-MM-dd
-log4j.appender.DOMAINLIST.layout=org.apache.log4j.PatternLayout
-log4j.appender.DOMAINLIST.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.VIRTUALUSERTABLE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.VIRTUALUSERTABLE.File=../log/virtualusertable.log
-log4j.appender.VIRTUALUSERTABLE.DatePattern='.'yyyy-MM-dd
-log4j.appender.VIRTUALUSERTABLE.layout=org.apache.log4j.PatternLayout
-log4j.appender.VIRTUALUSERTABLE.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.appender.MAILQUEUEFACTORY=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.MAILQUEUEFACTORY.File=../log/mailqueuefactory.log
-log4j.appender.MAILQUEUEFACTORY.DatePattern='.'yyyy-MM-dd
-log4j.appender.MAILQUEUEFACTORY.layout=org.apache.log4j.PatternLayout
-log4j.appender.MAILQUEUEFACTORY.layout.ConversionPattern=%-5p %d{HH:mm:ss,SSS} | %c | %m%n
-
-log4j.logger.org.apache.xbean.spring=WARN, CONS, FILE
-log4j.logger.org.apache.activemq=WARN, CONS, FILE
-
-log4j.logger.org.apache.camel=WARN, CONS, FILE
-log4j.logger.org.springframework=WARN, CONS, FILE
-log4j.logger.org.apache.james=INFO, CONS, FILE
-
-log4j.logger=DEBUG, CONS, FILE
-
-log4j.logger.org.apache.james.mailbox=DEBUG, MAILBOXMANAGER
-log4j.logger.org.apache.james.imap=DEBUG, IMAPSERVER
-log4j.logger.org.apache.james.mailetcontainer=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.mailetcontext=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.transport=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.mailspooler=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.mailprocessor=DEBUG, MAILETCONTAINER
-log4j.logger.org.apache.james.dnsservice=DEBUG, DNSSERVICE
-log4j.logger.org.apache.james.pop3server=DEBUG, POP3SERVER
-log4j.logger.org.apache.james.smtpserver=DEBUG, SMTPSERVER
-log4j.logger.org.apache.james.lmtpserver=DEBUG, LMTPSERVER
-log4j.logger.org.apache.james.mailrepositorystore=DEBUG, MAILREPOSITORYSTORE
-log4j.logger.org.apache.james.usersrepository=DEBUG, USERSREPOSITORY
-log4j.logger.org.apache.james.fetchmail=DEBUG, FETCHMAIL
-log4j.logger.org.apache.james.domainlist=DEBUG, DOMAINLIST
-log4j.logger.org.apache.james.virtualusertable=DEBUG, VIRTUALUSERTABLE
-log4j.logger.org.apache.james.rrt=DEBUG, VIRTUALUSERTABLE
-log4j.logger.org.james.apache.mailqueue=DEBUG, MAILQUEUEFACTORY
-log4j.logger.etm.core.monitor.EtmMonitor= DEBUG, CONS, FILE
diff --git a/server/app/src/main/resources/log4j2.xml b/server/app/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..c89ad2e
--- /dev/null
+++ b/server/app/src/main/resources/log4j2.xml
@@ -0,0 +1,486 @@
+
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="INFO" monitorInterval="30">
+    <Properties>
+        <Property name="logDir">${sys:app.home}/log</Property>
+        <Property name="logLayoutTomcat">%d{dd-MMM-yyyy HH:mm:ss.SSS} %level [%t] %C.%M:%L - %msg%n</Property>
+        <Property name="logLayout1">%d %-7level %logger{36} - %msg%n</Property>
+        <Property name="logLayout2">%d %-7level [%t] %C.%M:%L - %msg%n</Property>
+    </Properties>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="${logLayoutTomcat}" />
+        </Console>
+        <RollingFile name="SpringFramework" fileName="${logDir}/springframework.log"
+                     filePattern="${logDir}/springframework.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="openjpa" fileName="${logDir}/openjpa.log"
+                     filePattern="${logDir}/openjpa.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="James" fileName="${logDir}/james.log"
+                     filePattern="${logDir}/james.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="TransportProtocols" fileName="${logDir}/james_transport-protocols.log"
+                     filePattern="${logDir}/james_transport-protocols.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- <OnStartupTriggeringPolicy /> -->
+                <!-- <SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- <TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILBOXMANAGER" fileName="${logDir}/james_mailboxmanager.log"
+                     filePattern="${logDir}/james_mailboxmanager.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="IMAPSERVER" fileName="${logDir}/james_imapserver.log"
+                     filePattern="${logDir}/james_imapserver.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILETCONTAINER" fileName="${logDir}/james_mailetcontainer.log"
+                     filePattern="${logDir}/james_mailetcontainer.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="DNSSERVICE" fileName="${logDir}/james_dnsservice.log"
+                     filePattern="${logDir}/james_dnsservice.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="POP3SERVER" fileName="${logDir}/james_pop3server.log"
+                     filePattern="${logDir}/james_pop3server.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="SMTPSERVER" fileName="${logDir}/james_smtpserver.log"
+                     filePattern="${logDir}/james_smtpserver.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="LMTPSERVER" fileName="${logDir}/james_lmtpserver.log"
+                     filePattern="${logDir}/james_lmtpserver.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILREPOSITORYSTORE" fileName="${logDir}/james_mailrepositorystore.log"
+                     filePattern="${logDir}/james_mailrepositorystore.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="USERSREPOSITORY" fileName="${logDir}/james_usersrepository.log"
+                     filePattern="${logDir}/james_usersrepository.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="FETCHMAIL" fileName="${logDir}/james_fetchmail.log"
+                     filePattern="${logDir}/james_fetchmail.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="DOMAINLIST" fileName="${logDir}/james_domainlist.log"
+                     filePattern="${logDir}/james_domainlist.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="VIRTUALUSERTABLE" fileName="${logDir}/james_virtualusertable.log"
+                     filePattern="${logDir}/james_virtualusertable.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+        <RollingFile name="MAILQUEUEFACTORY" fileName="${logDir}/james_mailqueuefactory.log"
+                     filePattern="${logDir}/james_mailqueuefactory.%d{yyyy-MM-dd}-%i.log.gz"
+                     ignoreExceptions="false">
+            <PatternLayout pattern="${logLayoutTomcat}"
+                           charset="UTF-8" />
+            <Policies>
+                <!-- 				<OnStartupTriggeringPolicy /> -->
+                <!-- 				<SizeBasedTriggeringPolicy size="20 MB" /> -->
+                <!-- 				<TimeBasedTriggeringPolicy /> -->
+            </Policies>
+            <!-- 			<DefaultRolloverStrategy> -->
+            <!-- 				<Delete basePath="${logDir}" maxDepth="2"> -->
+            <!-- 					<IfFileName -->
+            <!-- 						glob="target/log4j2/roll-by-time-and-size/app.*.log.gz" /> -->
+            <!-- 					<IfLastModified age="20d" /> -->
+            <!-- 				</Delete> -->
+            <!-- 			</DefaultRolloverStrategy> -->
+        </RollingFile>
+    </Appenders>
+    <Loggers>
+        <Logger name="org.springframework" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="SpringFramework" level="info" />
+        </Logger>
+        <Logger name="etm.core.monitor.EtmMonitor" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="James" level="info" />
+        </Logger>
+        <Logger name="org.apache" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="James" level="info" />
+        </Logger>
+
+        <Logger name="openjpa" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="openjpa" level="trace" />
+        </Logger>
+        <Logger name="org.apache.openjpa" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="openjpa" level="trace" />
+        </Logger>
+
+        <Logger name="org.apache.james" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="James" level="trace" />
+        </Logger>
+
+        <Logger name="org.apache.james.transport" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="TransportProtocols" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.protocols" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="TransportProtocols" level="trace" />
+        </Logger>
+
+        <Logger name="org.apache.james.mailboxmanager" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILBOXMANAGER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.imapserver" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="IMAPSERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailetcontainer" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILETCONTAINER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.dnsservice" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="DNSSERVICE" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.pop3server" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="POP3SERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.smtpserver" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="SMTPSERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.lmtpserver" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="LMTPSERVER" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailrepositorystore" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILREPOSITORYSTORE" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.usersrepository" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="USERSREPOSITORY" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.fetchmail" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="FETCHMAIL" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.domainlist" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="DOMAINLIST" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.virtualusertable" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="VIRTUALUSERTABLE" level="trace" />
+        </Logger>
+        <Logger name="org.apache.james.mailqueuefactory" additivity="false">
+            <AppenderRef ref="Console" level="info" />
+            <AppenderRef ref="MAILQUEUEFACTORY" level="trace" />
+        </Logger>
+
+        <Root level="info">
+            <AppenderRef ref="Console" level="info" />
+            <!-- Only events at DIAG level or more specific are sent to the console. -->
+            <!-- <AppenderRef ref="Console" level="diag" /> -->
+            <AppenderRef ref="James" level="trace" />
+            <AppenderRef ref="openjpa" level="trace" />
+        </Root>
+    </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index ffdc651..399451a 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -32,9 +32,9 @@ Change list:
  - [Differentiation between domain alias and domain mapping](#differentiation-between-domain-alias-and-domain-mapping)
  - [ProtocolSession storng typing](#protocolsession-storng-typing)
  - [Tune Cassandra time serie tables options](#tune-cassandra-time-serie-tables-options)
- - [LogEnabled removal](#logenabled-removal)
+ - [Log4J2 Adoption](#log4j2-adoption)
 
-### LogEnabled removal
+### Log4J2 Adoption
 
 Date 20/03/2020
 
@@ -46,7 +46,10 @@ Concerned product: Spring
 
 As Log4J 1.x is not compatible with Java 9+ runtime, we adopted Log4J2 as a logging solution for the Spring product.
 
-As a consequence, the deprecated `LogEnabled` API will be removed. We recommend extension developers to obtain their
+As a consequence, the log configuration file for Spring product needs to be updated. See an example 
+[here](server/app/src/main/resources/log4j2.xml).
+
+Also, the deprecated `LogEnabled` API will be removed. We recommend extension developers to obtain their
 Logger instance using the SLF4J `LoggerFactory` class instead.
 
 ### Tune Cassandra time serie tables options


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


[james-project] 10/14: ADR: Fixing Cassandra ACL inconsistencies

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 2d73a279f273d653b98a9f76227b2b047ec4695f
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Feb 27 13:43:23 2020 +0700

    ADR: Fixing Cassandra ACL inconsistencies
---
 src/adr/0021-cassandra-acl-inconsistency.md | 66 +++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/src/adr/0021-cassandra-acl-inconsistency.md b/src/adr/0021-cassandra-acl-inconsistency.md
new file mode 100644
index 0000000..60922de
--- /dev/null
+++ b/src/adr/0021-cassandra-acl-inconsistency.md
@@ -0,0 +1,66 @@
+# 21. Cassandra ACL inconsistencies
+
+Date: 2020-02-27
+
+## Status
+
+Proposed
+
+## Context
+
+Mailboxes ACLs are denormalized in Cassandra in order to:
+
+ - given a mailbox, list its ACL (enforcing rights for example)
+ - discover which mailboxes are delegated to a given user (used to list mailboxes)
+
+Here is the tables organisation:
+
+ - `acl` stores the ACLs of a given mailbox
+ - `UserMailboxACL` stores which mailboxes had been delegated to which user
+
+Failures during the denormalization process will lead to inconsistencies between the two tables.
+
+This can lead to the following user experience:
+
+```
+ALICE delegates her INBOX mailbox to BOB
+The denormalisation process fails
+ALICE INBOX does not appear in BOB mailbox list
+
+Given a delegated mailbox INBOX.delegated
+ALICE undo the sharing of her INBOX.delegated mailbox
+The denormalisation process fails
+ALICE INBOX.delegated mailbox still appears in BOB mailbox list
+When BOB tries to select it, he is being denied
+```
+
+## Decision
+
+We can adopt a retry policy of the `UserMailboxACL` projection update as a mitigation strategy.
+
+Using `acl` table as a source of truth, we can rebuild the `UserMailboxACL` projection:
+
+ - Iterating `acl` entries, we can rewrite entries in `UserMailboxACL`
+ - Iterating `UserMailboxACL` we can remove entries not referenced in `acl`
+ - Adding a delay and a re-check before the actual fix can decrease the occurrence of concurrency issues
+
+We will expose a webAdmin task for doing this.
+
+## Consequences
+
+User actions concurrent to the inconsistency fixing task could result in concurrency issues. New inconsistencies could be
+created. However table of truth would not be impacted hence rerunning the inconsistency fixing task will eventually fix 
+all issues.
+
+This task could be run safely online and can be scheduled on a recurring basis outside of peak traffic by an admin to
+ensure Cassandra acl consistency.
+
+## References
+
+* [Plan for fixing Cassandra ACL inconsistencies](https://github.com/linagora/james-project/pull/3125)
+
+* [General mailing list discussion about inconsistencies](https://www.mail-archive.com/server-dev@james.apache.org/msg64432.html)
+
+* [Pull Request: JAMES-3058 Concurrency testing for fixing Cassandra mailbox inconsistencies](https://github.com/linagora/james-project/pull/3130)
+
+The delay strategy to decrease concurrency issue occurrence is described here.
\ 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] 08/14: JAMES-3129 Only apply PostDequeueDecorator to SPOOL queue

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 6dadb19cdd4109f63867edb7b3b95af3796555e3
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Mar 31 09:29:56 2020 +0700

    JAMES-3129 Only apply PostDequeueDecorator to SPOOL queue
    
    https://github.com/linagora/james-project/pull/3242/commits/62bab3269ceef65c1741fbbf243e5c109307ad8c highlighted a race condition when sending a mail via JMAP.
    
    Cause: the mail is processedn, and marked for outgoing delivery.
    
    It is reenqueued, and appended to outgoing queue.
    
    The post dequeue decorator, moving JMAP mails from outbox to sent is applied to times, leading eventualy to invalid results and race condition.
    
    Proposed fix: only apply post dequeue decorator to "spool"
---
 .../james/jmap/draft/send/PostDequeueDecoratorFactory.java    | 11 +++++++++--
 .../james/queue/activemq/ActiveMQCacheableMailQueue.java      |  2 +-
 .../apache/james/queue/api/MailQueueItemDecoratorFactory.java |  2 +-
 .../james/queue/api/RawMailQueueItemDecoratorFactory.java     |  2 +-
 .../org/apache/james/queue/file/FileCacheableMailQueue.java   |  2 +-
 .../org/apache/james/queue/jms/JMSCacheableMailQueue.java     |  2 +-
 .../org/apache/james/queue/memory/MemoryMailQueueFactory.java |  2 +-
 .../java/org/apache/james/queue/rabbitmq/MailQueueName.java   |  4 ++++
 .../org/apache/james/queue/rabbitmq/RabbitMQMailQueue.java    |  2 +-
 9 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/PostDequeueDecoratorFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/PostDequeueDecoratorFactory.java
index 2b642c1..9b09527 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/PostDequeueDecoratorFactory.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/send/PostDequeueDecoratorFactory.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.jmap.draft.send;
 
+import static org.apache.james.queue.api.MailQueueFactory.SPOOL;
+
 import javax.inject.Inject;
 
 import org.apache.james.mailbox.MailboxManager;
@@ -27,6 +29,8 @@ import org.apache.james.mailbox.SystemMailboxesProvider;
 import org.apache.james.mailbox.model.MessageId.Factory;
 import org.apache.james.queue.api.MailQueue.MailQueueItem;
 import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
+import org.apache.james.queue.api.MailQueueName;
+import org.apache.james.queue.api.RawMailQueueItem;
 
 public class PostDequeueDecoratorFactory implements MailQueueItemDecoratorFactory {
     private final MailboxManager mailboxManager;
@@ -44,8 +48,11 @@ public class PostDequeueDecoratorFactory implements MailQueueItemDecoratorFactor
     }
 
     @Override
-    public MailQueueItemDecorator decorate(MailQueueItem mailQueueItem) {
-        return new PostDequeueDecorator(mailQueueItem, mailboxManager, messageIdFactory, messageIdManager, systemMailboxesProvider);
+    public MailQueueItemDecorator decorate(MailQueueItem mailQueueItem, MailQueueName name) {
+        if (name.equals(SPOOL)) {
+            return new PostDequeueDecorator(mailQueueItem, mailboxManager, messageIdFactory, messageIdManager, systemMailboxesProvider);
+        }
+        return new RawMailQueueItem(mailQueueItem);
     }
 
 }
diff --git a/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java b/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java
index e798251..5d7bf03 100644
--- a/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java
+++ b/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java
@@ -229,7 +229,7 @@ public class ActiveMQCacheableMailQueue extends JMSCacheableMailQueue implements
     protected MailQueueItem createMailQueueItem(Session session, MessageConsumer consumer, Message message) throws JMSException, MessagingException {
         Mail mail = createMail(message);
         ActiveMQMailQueueItem activeMQMailQueueItem = new ActiveMQMailQueueItem(mail, session, consumer, message);
-        return mailQueueItemDecoratorFactory.decorate(activeMQMailQueueItem);
+        return mailQueueItemDecoratorFactory.decorate(activeMQMailQueueItem, queueName);
     }
 
     @Override
diff --git a/server/queue/queue-api/src/main/java/org/apache/james/queue/api/MailQueueItemDecoratorFactory.java b/server/queue/queue-api/src/main/java/org/apache/james/queue/api/MailQueueItemDecoratorFactory.java
index 5e62d92..ec48fae 100644
--- a/server/queue/queue-api/src/main/java/org/apache/james/queue/api/MailQueueItemDecoratorFactory.java
+++ b/server/queue/queue-api/src/main/java/org/apache/james/queue/api/MailQueueItemDecoratorFactory.java
@@ -23,7 +23,7 @@ import org.apache.james.queue.api.MailQueue.MailQueueItem;
 
 public interface MailQueueItemDecoratorFactory {
 
-    MailQueueItemDecorator decorate(MailQueueItem mailQueueItem);
+    MailQueueItemDecorator decorate(MailQueueItem mailQueueItem, MailQueueName name);
 
     abstract class MailQueueItemDecorator implements MailQueueItem {
         protected MailQueueItem mailQueueItem;
diff --git a/server/queue/queue-api/src/main/java/org/apache/james/queue/api/RawMailQueueItemDecoratorFactory.java b/server/queue/queue-api/src/main/java/org/apache/james/queue/api/RawMailQueueItemDecoratorFactory.java
index d489014..9068754 100644
--- a/server/queue/queue-api/src/main/java/org/apache/james/queue/api/RawMailQueueItemDecoratorFactory.java
+++ b/server/queue/queue-api/src/main/java/org/apache/james/queue/api/RawMailQueueItemDecoratorFactory.java
@@ -23,7 +23,7 @@ import org.apache.james.queue.api.MailQueue.MailQueueItem;
 public class RawMailQueueItemDecoratorFactory implements MailQueueItemDecoratorFactory {
 
     @Override
-    public MailQueueItemDecorator decorate(MailQueueItem mailQueueItem) {
+    public MailQueueItemDecorator decorate(MailQueueItem mailQueueItem, MailQueueName name) {
         return new RawMailQueueItem(mailQueueItem);
     }
 
diff --git a/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java b/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java
index 041e13d..d32968e 100644
--- a/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java
+++ b/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java
@@ -303,7 +303,7 @@ public class FileCacheableMailQueue implements ManageableMailQueue {
                             LifecycleUtil.dispose(mail);
                         }
                     };
-                    return Mono.just(mailQueueItemDecoratorFactory.decorate(fileMailQueueItem));
+                    return Mono.just(mailQueueItemDecoratorFactory.decorate(fileMailQueueItem, queueName));
                 }
                 // TODO: Think about exception handling in detail
             } catch (IOException | ClassNotFoundException | MessagingException e) {
diff --git a/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java b/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java
index 5c3bb17..4e7cee9 100644
--- a/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java
+++ b/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java
@@ -482,7 +482,7 @@ public class JMSCacheableMailQueue implements ManageableMailQueue, JMSSupport, M
     protected MailQueueItem createMailQueueItem(Session session, MessageConsumer consumer, Message message) throws JMSException, MessagingException {
         Mail mail = createMail(message);
         JMSMailQueueItem jmsMailQueueItem = new JMSMailQueueItem(mail, session, consumer);
-        return mailQueueItemDecoratorFactory.decorate(jmsMailQueueItem);
+        return mailQueueItemDecoratorFactory.decorate(jmsMailQueueItem, queueName);
     }
 
     protected String getMessageSelector() {
diff --git a/server/queue/queue-memory/src/main/java/org/apache/james/queue/memory/MemoryMailQueueFactory.java b/server/queue/queue-memory/src/main/java/org/apache/james/queue/memory/MemoryMailQueueFactory.java
index fa949e8..e13cb10 100644
--- a/server/queue/queue-memory/src/main/java/org/apache/james/queue/memory/MemoryMailQueueFactory.java
+++ b/server/queue/queue-memory/src/main/java/org/apache/james/queue/memory/MemoryMailQueueFactory.java
@@ -102,7 +102,7 @@ public class MemoryMailQueueFactory implements MailQueueFactory<ManageableMailQu
                 .subscribeOn(Schedulers.elastic())
                 .flatMap(item ->
                     Mono.fromRunnable(() -> inProcessingMailItems.add(item)).thenReturn(item))
-                .map(mailQueueItemDecoratorFactory::decorate);
+                .map(item -> mailQueueItemDecoratorFactory.decorate(item, name));
         }
 
         @Override
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailQueueName.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailQueueName.java
index 5c060de..dfc352d 100644
--- a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailQueueName.java
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailQueueName.java
@@ -142,6 +142,10 @@ public final class MailQueueName {
         return new WorkQueueName(name);
     }
 
+    org.apache.james.queue.api.MailQueueName toModel() {
+        return org.apache.james.queue.api.MailQueueName.of(asString());
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof MailQueueName) {
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/RabbitMQMailQueue.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/RabbitMQMailQueue.java
index 822fc5a..3ed3ba9 100644
--- a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/RabbitMQMailQueue.java
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/RabbitMQMailQueue.java
@@ -84,7 +84,7 @@ public class RabbitMQMailQueue implements ManageableMailQueue {
     @Override
     public Flux<MailQueueItem> deQueue() {
         return dequeuer.deQueue()
-            .map(decoratorFactory::decorate);
+            .map(item -> decoratorFactory.decorate(item, name.toModel()));
     }
 
     @Override


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