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 rc...@apache.org on 2019/11/26 10:41:21 UTC

[james-project] branch master updated (9e976d3 -> d8a4a8c)

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

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


    from 9e976d3  JAMES-2949 Enforce username to be always lower cased
     new fff0427  [Refactoring] rewrite InMemoryEventStore in Scala
     new 2a099af  JAMES-2949 Add an integration test ensuring delivery when non matching case
     new f6cda85  JAMES-2949 Update documentation accordingly
     new 432a6f5  JAMES-2210 Evaluate the performance impact to use commons-io in FileSystemBlobStrategy
     new 98034ed  JAMES-2110 Use IOUtils::copy in more places
     new d8a4a8c  JAMES-2110 Reduce ImapRequestLineReader::consumeLiteral buffer size

The 6 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 +
 event-sourcing/event-store-memory/pom.xml          | 17 ++++
 .../eventstore/memory/InMemoryEventStore.java      | 99 ----------------------
 .../eventstore/memory/InMemoryEventStore.scala     | 58 +++++++++++++
 .../mailbox/maildir/mail/MaildirMessageMapper.java |  6 +-
 .../james/imap/decode/ImapRequestLineReader.java   | 13 +--
 .../main/OutputStreamImapResponseWriter.java       | 15 +---
 .../mailets/CommonMailetConfigurationTest.java     | 13 +++
 .../queue/activemq/FileSystemBlobStrategy.java     | 25 ++----
 src/site/xdoc/server/config-users.xml              |  3 +
 upgrade-instructions.md                            | 14 ++-
 11 files changed, 121 insertions(+), 143 deletions(-)
 delete mode 100644 event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java
 create mode 100644 event-sourcing/event-store-memory/src/main/scala/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.scala


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


[james-project] 04/06: JAMES-2210 Evaluate the performance impact to use commons-io in FileSystemBlobStrategy

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 432a6f59ff921c9466090d590d9e4c353d9f0eb6
Author: Manish Agarwal <it...@gmail.com>
AuthorDate: Fri Feb 15 00:16:04 2019 -0800

    JAMES-2210 Evaluate the performance impact to use commons-io in FileSystemBlobStrategy
    
    As per the description chaged the code to use IOUtils::copy.
    Did not use IOutils::closeQuietly as it is deprecated in latest releases, and recommended way is to use the  try-with-resources statement.
---
 .../queue/activemq/FileSystemBlobStrategy.java     | 25 ++++++----------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/FileSystemBlobStrategy.java b/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/FileSystemBlobStrategy.java
index 1195a8e..3568e5c 100644
--- a/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/FileSystemBlobStrategy.java
+++ b/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/FileSystemBlobStrategy.java
@@ -35,6 +35,7 @@ import org.apache.activemq.blob.BlobTransferPolicy;
 import org.apache.activemq.blob.BlobUploadStrategy;
 import org.apache.activemq.command.ActiveMQBlobMessage;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.apache.james.filesystem.api.FileSystem;
 
 /**
@@ -58,20 +59,16 @@ public class FileSystemBlobStrategy implements BlobUploadStrategy, BlobDownloadS
 
     @Override
     public URL uploadFile(ActiveMQBlobMessage message, File file) throws JMSException, IOException {
-        return uploadStream(message, new FileInputStream(file));
+        try (FileInputStream in = new FileInputStream(file)) {
+            return uploadStream(message, in);
+        }
     }
 
     @Override
     public URL uploadStream(ActiveMQBlobMessage message, InputStream in) throws JMSException, IOException {
-        FileOutputStream out = null;
-        try {
-            File f = getFile(message);
-            out = new FileOutputStream(f);
-            byte[] buffer = new byte[policy.getBufferSize()];
-            for (int c = in.read(buffer); c != -1; c = in.read(buffer)) {
-                out.write(buffer, 0, c);
-                out.flush();
-            }
+        File f = getFile(message);
+        try (FileOutputStream out = new FileOutputStream(f)) {
+            IOUtils.copy(in, out, policy.getBufferSize());
             out.flush();
             // File.toURL() is deprecated
             return f.toURI().toURL();
@@ -83,15 +80,7 @@ public class FileSystemBlobStrategy implements BlobUploadStrategy, BlobDownloadS
                     // ignore on close
                 }
             }
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (IOException e) {
-                    // ignore on close
-                }
-            }
         }
-
     }
 
     @Override


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


[james-project] 02/06: JAMES-2949 Add an integration test ensuring delivery when non matching case

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2a099af94d42c2faf6a1d252d57972a1a1ac0d25
Author: Raphael Ouazana <ra...@linagora.com>
AuthorDate: Thu Nov 21 11:48:59 2019 +0100

    JAMES-2949 Add an integration test ensuring delivery when non matching case
---
 .../apache/james/mailets/CommonMailetConfigurationTest.java | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/CommonMailetConfigurationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/CommonMailetConfigurationTest.java
index e05707b..f05cf3f 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/CommonMailetConfigurationTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/CommonMailetConfigurationTest.java
@@ -26,6 +26,8 @@ import static org.apache.james.mailets.configuration.Constants.PASSWORD;
 import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
 import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
 
+import java.util.Locale;
+
 import org.apache.james.modules.protocols.ImapGuiceProbe;
 import org.apache.james.modules.protocols.SmtpGuiceProbe;
 import org.apache.james.probe.DataProbe;
@@ -76,4 +78,15 @@ public class CommonMailetConfigurationTest {
             .select(IMAPMessageReader.INBOX)
             .awaitMessage(awaitAtMostOneMinute);
     }
+
+    @Test
+    public void simpleMailShouldBeSentToUpperCaseRecipient() throws Exception {
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .sendMessage(FROM, RECIPIENT.toUpperCase(Locale.US));
+
+        imapMessageReader.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .select(IMAPMessageReader.INBOX)
+            .awaitMessage(awaitAtMostOneMinute);
+    }
 }


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


[james-project] 05/06: JAMES-2110 Use IOUtils::copy in more places

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 98034ed4ea6fc3224f84bdac856adab708f4aa3d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Nov 22 09:02:48 2019 +0700

    JAMES-2110 Use IOUtils::copy in more places
---
 .../james/mailbox/maildir/mail/MaildirMessageMapper.java  |  6 ++----
 .../apache/james/imap/decode/ImapRequestLineReader.java   | 13 ++++---------
 .../imap/decode/main/OutputStreamImapResponseWriter.java  | 15 +++------------
 3 files changed, 9 insertions(+), 25 deletions(-)

diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
index 6bbddaa..b9d29fb 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
@@ -36,6 +36,7 @@ import javax.mail.Flags;
 import javax.mail.Flags.Flag;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
@@ -310,10 +311,7 @@ public class MaildirMessageMapper extends AbstractMessageMapper {
             try (FileOutputStream fos = new FileOutputStream(messageFile);
                 InputStream input = message.getFullContent()) {
                 byte[] b = new byte[BUF_SIZE];
-                int len = 0;
-                while ((len = input.read(b)) != -1) {
-                    fos.write(b, 0, len);
-                }
+                IOUtils.copy(input, fos, BUF_SIZE);
             }
         } catch (IOException ioe) {
             throw new MailboxException("Failure while save MailboxMessage " + message + " in Mailbox " + mailbox, ioe);
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java
index 9c86626..ab65ce3 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java
@@ -34,6 +34,7 @@ import java.util.List;
 
 import javax.mail.Flags;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.display.HumanReadableText;
@@ -370,16 +371,10 @@ public abstract class ImapRequestLineReader {
         } else {
             try (FastByteArrayOutputStream out = new FastByteArrayOutputStream();
                  InputStream in = consumeLiteral(false)) {
-                byte[] buf = new byte[0xFFFF];
-
-                for (int len; (len = in.read(buf)) != -1; ) {
-                    out.write(buf, 0, len);
-                }
-
-                final byte[] bytes = out.toByteArray();
-                final ByteBuffer buffer = ByteBuffer.wrap(bytes);
+                IOUtils.copy(in, out, 0xFFFF);
+                byte[] bytes = out.toByteArray();
+                ByteBuffer buffer = ByteBuffer.wrap(bytes);
                 return decode(charset, buffer);
-
             } catch (IOException e) {
                 throw new DecodingException(HumanReadableText.BAD_IO_ENCODING, "Bad character encoding", e);
             }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/main/OutputStreamImapResponseWriter.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/main/OutputStreamImapResponseWriter.java
index 27539bd..271698f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/main/OutputStreamImapResponseWriter.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/main/OutputStreamImapResponseWriter.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.james.imap.encode.ImapResponseWriter;
 import org.apache.james.imap.message.response.Literal;
 
@@ -32,28 +33,18 @@ import org.apache.james.imap.message.response.Literal;
  */
 public class OutputStreamImapResponseWriter implements ImapResponseWriter {
 
+    public static final int BUFFER_SIZE = 1024;
     private final OutputStream output;
 
     public OutputStreamImapResponseWriter(OutputStream output) {
         this.output = output;
     }
 
-    public void flush() throws IOException {
-        output.flush();
-    }
-
-
-
     @Override
     public void write(Literal literal) throws IOException {
         try (InputStream in = literal.getInputStream()) {
-
-            byte[] buffer = new byte[1024];
-            for (int len; (len = in.read(buffer)) != -1; ) {
-                output.write(buffer, 0, len);
-            }
+            IOUtils.copy(in, output, BUFFER_SIZE);
         }
-
     }
 
     @Override


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


[james-project] 03/06: JAMES-2949 Update documentation accordingly

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f6cda852d5e15f33b50087e5368d8f4aa2637c96
Author: Raphael Ouazana <ra...@linagora.com>
AuthorDate: Thu Nov 21 12:21:30 2019 +0100

    JAMES-2949 Update documentation accordingly
---
 CHANGELOG.md                          |  1 +
 src/site/xdoc/server/config-users.xml |  3 +++
 upgrade-instructions.md               | 14 +++++++++++++-
 3 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5baf925..b6abb79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ of tasks being currently executed.
   - Read related [upgrade instructions](upgrade-instructions.md)
 - JAMES-2855 Multiple library/plugin/docker images/build tool upgrades
 - By default the cassandra keyspace creation by James is now disabled by default. This allow to have credentials limited to a keyspace. It can be enabled by setting cassandra.keyspace.create=true in the cassandra.properties file.
+- Usernames are assumed to be always lower cased. Many users recently complained about mails non received when sending to upper cased local recipients. We decided to simplify the handling of case for local recipients and users by always storing them lower cased.
   
 ### Fixed
 - JAMES-2828 & JAMES-2929 bugs affecting JDBCMailRepository usage with PostgresSQL thanks to Jörg Thomas & Sergey B
diff --git a/src/site/xdoc/server/config-users.xml b/src/site/xdoc/server/config-users.xml
index 8024dc4..5829c23 100644
--- a/src/site/xdoc/server/config-users.xml
+++ b/src/site/xdoc/server/config-users.xml
@@ -47,6 +47,9 @@
             and the domain part should be concatenated after a domain delimiter('@'). E.g. 'myuser@james.org'
         </li>
       </ul>
+      <p>
+        A user is always considered as lower cased, so 'myUser' and 'myuser' are the same user, and can be used as well as recipient local part than as login for different protocols.
+      </p>
     </subsection>
 
     <subsection name="General configuration">
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index f1b0ab3..32db72d 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -16,10 +16,22 @@ Changes to apply between 3.4.x and 3.5.x will be reported here.
 
 Change list:
 
+ - [Enforce usernames to be lower cased](#enforce-usernames-to-be-lower-cased)
+ - [Cassandra keyspace creation configuration](#cassandra-keyspace-creation-configuration)
  - [UsersFileRepository](#usersfilerepository)
  - [ElasticSearch performance enhancements](#elasticsearch-performance-enhancements)
  - [JAMES-2703 Post 3.4.0 release removals](#james-2703-post-340-release-removals)
- - [Cassandra keyspace creation configuration](#cassandra-keyspace-creation-configuration)
+
+#### Enforce usernames to be lower cased
+
+Date 21/11/2019
+
+SHA-1 xxxxxxxxx
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-2949
+
+Many users recently complained about mails non received when sending to upper cased local recipients. We decided to simplify the handling of case for local recipients and users by always storing them lower cased.
+Now all the users repositories are storing user in lower case to ensure that. If you previously used to store users in a case sensitive way (which is very unlikely as it is broking delivery), you could need to update your user database to lower case all your users.
 
 #### Cassandra keyspace creation configuration
 


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


[james-project] 06/06: JAMES-2110 Reduce ImapRequestLineReader::consumeLiteral buffer size

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d8a4a8c066ea0e38db3e7e503e4a56a58c98e829
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Nov 22 09:11:41 2019 +0700

    JAMES-2110 Reduce ImapRequestLineReader::consumeLiteral buffer size
    
    This method is used by ListCommandParser (list of mailboxes), Set Annotation
    command parser, in search command parser, setAcl, deleteAcl,
    loginCommandParser, listRight, enable, authenticate.
    
    Usage also includes parsing mailbox names.
    
    0xFFFF means 64KB and is a big size for such purposes, I propose to
    decrease that buffer size tpo a more reasonable 2KB value, that should
    be more than enough regarding the aforementioned usages.
---
 .../main/java/org/apache/james/imap/decode/ImapRequestLineReader.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java
index ab65ce3..b4f5e75 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/ImapRequestLineReader.java
@@ -371,7 +371,7 @@ public abstract class ImapRequestLineReader {
         } else {
             try (FastByteArrayOutputStream out = new FastByteArrayOutputStream();
                  InputStream in = consumeLiteral(false)) {
-                IOUtils.copy(in, out, 0xFFFF);
+                IOUtils.copy(in, out, 2048);
                 byte[] bytes = out.toByteArray();
                 ByteBuffer buffer = ByteBuffer.wrap(bytes);
                 return decode(charset, buffer);


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


[james-project] 01/06: [Refactoring] rewrite InMemoryEventStore in Scala

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fff0427a01dc79252f5ff6373c24101da683bf32
Author: Matthieu Baechler <ma...@apache.org>
AuthorDate: Mon Nov 25 15:33:50 2019 +0100

    [Refactoring] rewrite InMemoryEventStore in Scala
---
 event-sourcing/event-store-memory/pom.xml          | 17 ++++
 .../eventstore/memory/InMemoryEventStore.java      | 99 ----------------------
 .../eventstore/memory/InMemoryEventStore.scala     | 58 +++++++++++++
 3 files changed, 75 insertions(+), 99 deletions(-)

diff --git a/event-sourcing/event-store-memory/pom.xml b/event-sourcing/event-store-memory/pom.xml
index aa57dd3..6f4b932 100644
--- a/event-sourcing/event-store-memory/pom.xml
+++ b/event-sourcing/event-store-memory/pom.xml
@@ -69,6 +69,23 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.scala-lang</groupId>
+            <artifactId>scala-library</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.scala-lang.modules</groupId>
+            <artifactId>scala-java8-compat_${scala.base}</artifactId>
+        </dependency>
     </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>net.alchim31.maven</groupId>
+                <artifactId>scala-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
 
 </project>
\ No newline at end of file
diff --git a/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java b/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java
deleted file mode 100644
index de65ca6..0000000
--- a/event-sourcing/event-store-memory/src/main/java/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.java
+++ /dev/null
@@ -1,99 +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.                                           *
- ****************************************************************/
-
-package org.apache.james.eventsourcing.eventstore.memory;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.james.eventsourcing.AggregateId;
-import org.apache.james.eventsourcing.Event;
-import org.apache.james.eventsourcing.eventstore.EventStore;
-import org.apache.james.eventsourcing.eventstore.EventStoreFailedException;
-import org.apache.james.eventsourcing.eventstore.History;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-public class InMemoryEventStore implements EventStore {
-
-    private final ConcurrentHashMap<AggregateId, History> store;
-
-    public InMemoryEventStore() {
-        this.store = new ConcurrentHashMap<>();
-    }
-
-    @Override
-    public void appendAll(List<Event> events) {
-        if (events.isEmpty()) {
-            return;
-        }
-        AggregateId aggregateId = getAggregateId(events);
-
-        if (!store.containsKey(aggregateId)) {
-            appendToEmptyHistory(aggregateId, events);
-        } else {
-            appendToExistingHistory(aggregateId, events);
-        }
-    }
-
-    private AggregateId getAggregateId(List<? extends Event> events) {
-        Preconditions.checkArgument(!events.isEmpty());
-        Preconditions.checkArgument(Event.belongsToSameAggregate(events));
-
-        return events.stream()
-            .map(Event::getAggregateId)
-            .findFirst()
-            .get();
-    }
-
-    private void appendToEmptyHistory(AggregateId aggregateId, List<Event> events) {
-        History newHistory = History.of(events);
-
-        History previousHistory = store.putIfAbsent(aggregateId, newHistory);
-        if (previousHistory != null) {
-            throw new EventStoreFailedException("Concurrent update to the EventStore detected");
-        }
-    }
-
-    private void appendToExistingHistory(AggregateId aggregateId, List<? extends Event> events) {
-        History currentHistory = store.get(aggregateId);
-        List<Event> updatedEvents = updatedEvents(currentHistory, events);
-        History updatedHistory = History.of(updatedEvents);
-
-        boolean isReplaced = store.replace(aggregateId, currentHistory, updatedHistory);
-        if (!isReplaced) {
-            throw new EventStoreFailedException("Concurrent update to the EventStore detected");
-        }
-    }
-
-    private List<Event> updatedEvents(History currentHistory, List<? extends Event> newEvents) {
-        return ImmutableList.<Event>builder()
-            .addAll(currentHistory.getEvents())
-            .addAll(newEvents)
-            .build();
-    }
-
-    @Override
-    public History getEventsOfAggregate(AggregateId aggregateId) {
-        return Optional.ofNullable(store.get(aggregateId))
-            .orElse(History.empty());
-    }
-}
diff --git a/event-sourcing/event-store-memory/src/main/scala/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.scala b/event-sourcing/event-store-memory/src/main/scala/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.scala
new file mode 100644
index 0000000..d7bf451
--- /dev/null
+++ b/event-sourcing/event-store-memory/src/main/scala/org/apache/james/eventsourcing/eventstore/memory/InMemoryEventStore.scala
@@ -0,0 +1,58 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0                 *
+ * *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ * ***************************************************************/
+package org.apache.james.eventsourcing.eventstore.memory
+
+import java.util
+import java.util.concurrent.atomic.AtomicReference
+
+import com.google.common.base.Preconditions
+import org.apache.james.eventsourcing.eventstore.{EventStore, History}
+import org.apache.james.eventsourcing.{AggregateId, Event}
+
+import scala.collection.JavaConverters._
+
+class InMemoryEventStore() extends EventStore {
+  private val storeRef: AtomicReference[Map[AggregateId, History]] = new AtomicReference(Map().withDefault(_ => History.empty()))
+
+  override def appendAll(events: util.List[Event]): Unit = if (!events.isEmpty) appendAll(events.asScala)
+
+  override def getEventsOfAggregate(aggregateId: AggregateId): History = {
+    Preconditions.checkNotNull(aggregateId)
+    storeRef.get()(aggregateId)
+  }
+
+  def appendAll(events: Seq[Event]): Unit = {
+    val aggregateId: AggregateId = getAggregateId(events)
+    storeRef.updateAndGet(store => {
+      val updatedHistory = History.of((store(aggregateId).getEvents.asScala ++ events).asJava)
+      store.updated(aggregateId, updatedHistory)
+    })
+  }
+
+  private def getAggregateId(events: Seq[Event]): AggregateId = {
+    Preconditions.checkArgument(events.nonEmpty)
+    val aggregateId = events.head.getAggregateId
+    Preconditions.checkArgument(belongsToSameAggregate(aggregateId, events))
+    aggregateId
+  }
+
+  private def belongsToSameAggregate(aggregateId: AggregateId, events: Seq[Event]) =
+    events.forall(_.getAggregateId.equals(aggregateId))
+
+}
\ 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