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 61b367fb621a8c9a7a0ae42e43f7bd7d371155c1
Author: Jan-Eric Hellenberg <janeric.hellenb...@gmail.com>
AuthorDate: Fri Jan 3 10:25:28 2025 +0100

    James-4099 Make mailbox path delimiter configurable
---
 docs/modules/servers/partials/configure/jvm.adoc   |  15 ++
 .../james/mailbox/model/MailboxConstants.java      |  39 +++-
 .../apache/james/mailbox/MailboxSessionUtil.java   |  11 +-
 .../james/mailbox/store/SessionProviderImpl.java   |   2 +-
 .../org/apache/james/imap/main/PathConverter.java  |   2 +-
 .../james/imap/message/response/ListResponse.java  |   2 +-
 .../james/imap/main/DefaultPathConverterTest.java  |  47 ++---
 .../imap/main/PathConverterBasicContract.java      | 210 ++++++++++++---------
 .../docker-configuration/jvm.properties            |   6 +
 .../sample-configuration/jvm.properties            |   6 +
 .../docker-configuration/jvm.properties            |  12 +-
 .../sample-configuration/jvm.properties            |  10 +-
 .../docker-configuration/jvm.properties            |   6 +
 .../sample-configuration/jvm.properties            |   6 +
 .../jpa-app/sample-configuration/jvm.properties    |   6 +
 .../sample-configuration/jvm.properties            |   6 +
 .../memory-app/sample-configuration/jvm.properties |   6 +
 .../sample-configuration/jvm.properties            |   6 +
 18 files changed, 266 insertions(+), 132 deletions(-)

diff --git a/docs/modules/servers/partials/configure/jvm.adoc 
b/docs/modules/servers/partials/configure/jvm.adoc
index fdc71ef205..11f22bc5a3 100644
--- a/docs/modules/servers/partials/configure/jvm.adoc
+++ b/docs/modules/servers/partials/configure/jvm.adoc
@@ -5,6 +5,21 @@ when a property affects very early JVM start behaviour.
 
 For testing purposes, you may specify a different file path via the command 
line option `-Dextra.props=/some/other/jvm.properties`.
 
+== Adjusting the Mailbox folder delimiter
+
+The delimiter used to separate parent/child folders.
+
+WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments will likely lead to failure 
of some system components, as occurrences of old the delimiter will still be 
present in the database/data store.
+
+Optional. String. Defaults to 'dot'
+
+Ex in `jvm.properties`
+----
+james.mailbox.folder.delimiter=dot
+----
+
+Allowed values are: dot (will use '.' as delimiter), slash (will use '/' as 
delimiter).
+
 == Control the threshold memory
 This governs the threshold MimeMessageInputStreamSource relies on for storing 
MimeMessage content on disk.
 
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxConstants.java
 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxConstants.java
index 69bff299de..3afd9f21c7 100644
--- 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxConstants.java
+++ 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxConstants.java
@@ -19,28 +19,51 @@
 
 package org.apache.james.mailbox.model;
 
+import java.util.Optional;
+
 /**
  * Constants which are used within the mailbox api and implementations
  */
-public interface MailboxConstants {
+public class MailboxConstants {
 
     /**
      * The char which is used to prefix a namespace
      */
-    char NAMESPACE_PREFIX_CHAR = '#';
+    public static final char NAMESPACE_PREFIX_CHAR = '#';
 
     /** The namespace used for store user inboxes */
-    String USER_NAMESPACE = NAMESPACE_PREFIX_CHAR + "private";
+    public static final String USER_NAMESPACE = NAMESPACE_PREFIX_CHAR + 
"private";
+
+    /** The delimiter used to seperated parent/child folders */
+    public static char FOLDER_DELIMITER = 
Optional.ofNullable(System.getProperty("james.mailbox.folder.delimiter"))
+            
.map(MailboxFolderDelimiter::parse).orElse(MailboxFolderDelimiter.DOT).value;
+
+    enum MailboxFolderDelimiter {
+        DOT('.'),
+        SLASH('/');
+
+        public final char value;
+
+        MailboxFolderDelimiter(char value) {
+            this.value = value;
+        }
 
-    /** The default delimiter used to seperated parent/child folders */
-    char DEFAULT_DELIMITER = '.';
+        static MailboxFolderDelimiter parse(String input) {
+            for (MailboxFolderDelimiter delimiter: values()) {
+                if (delimiter.name().equalsIgnoreCase(input)) {
+                    return delimiter;
+                }
+            }
+            throw new IllegalArgumentException(String.format("Invalid mailbox 
delimiter `%s`", input));
+        }
+    }
 
     /** The name of the INBOX */
-    String INBOX = "INBOX";
+    public static final String INBOX = "INBOX";
 
     /** The limitation of annotation data */
-    int DEFAULT_LIMIT_ANNOTATION_SIZE = 1024;
+    public static final int DEFAULT_LIMIT_ANNOTATION_SIZE = 1024;
 
     /** The maximum number of annotations on a mailbox */
-    int DEFAULT_LIMIT_ANNOTATIONS_ON_MAILBOX = 10;
+    public static final int DEFAULT_LIMIT_ANNOTATIONS_ON_MAILBOX = 10;
 }
diff --git 
a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxSessionUtil.java 
b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxSessionUtil.java
index e2aa013008..2d4ac34f7d 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxSessionUtil.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxSessionUtil.java
@@ -31,11 +31,16 @@ import com.google.common.annotations.VisibleForTesting;
 
 public class MailboxSessionUtil {
     public static MailboxSession create(Username username) {
-        return create(username, 
MailboxSession.SessionId.of(ThreadLocalRandom.current().nextLong()));
+        return create(username, MailboxConstants.FOLDER_DELIMITER);
+    }
+
+    public static MailboxSession create(Username username, char 
folderDelimiter) {
+        return create(username, 
MailboxSession.SessionId.of(ThreadLocalRandom.current().nextLong()), 
folderDelimiter);
     }
 
     @VisibleForTesting
-    public static MailboxSession create(Username username, 
MailboxSession.SessionId sessionId) {
+    public static MailboxSession create(Username username, 
MailboxSession.SessionId sessionId,
+                                        char folderDelimiter) {
         ArrayList<Locale> locales = new ArrayList<>();
 
         return new MailboxSession(
@@ -43,7 +48,7 @@ public class MailboxSessionUtil {
             username,
             Optional.of(username),
             locales,
-            MailboxConstants.DEFAULT_DELIMITER,
+            folderDelimiter,
             MailboxSession.SessionType.User);
     }
 }
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/SessionProviderImpl.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/SessionProviderImpl.java
index 5651c699c8..b0eea7c0a3 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/SessionProviderImpl.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/SessionProviderImpl.java
@@ -127,7 +127,7 @@ public class SessionProviderImpl implements SessionProvider 
{
     }
 
     private MailboxSession createSession(Username userName, Optional<Username> 
loggedInUser, MailboxSession.SessionType type) {
-        return new MailboxSession(newSessionId(), userName, loggedInUser, new 
ArrayList<>(), MailboxConstants.DEFAULT_DELIMITER, type);
+        return new MailboxSession(newSessionId(), userName, loggedInUser, new 
ArrayList<>(), MailboxConstants.FOLDER_DELIMITER, type);
     }
 
     private MailboxSession.SessionId newSessionId() {
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java 
b/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java
index 7d2d53281a..f57cc3e3c1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java
@@ -112,7 +112,7 @@ public interface PathConverter {
                     return new MailboxPath(MailboxConstants.USER_NAMESPACE, 
null, sanitizeMailboxName(mailboxName));
                 }
                 String username = mailboxPathParts.get(USER);
-                String unescapedUsername = username.replace("__", ".")
+                String unescapedUsername = username.replace("__", 
String.valueOf(MailboxConstants.FOLDER_DELIMITER))
                     .replace("_-", "_");
                 Username user = Username.from(unescapedUsername, 
session.getUser().getDomainPart().map(Domain::asString));
                 String mailboxName = 
Joiner.on(session.getPathDelimiter()).join(Iterables.skip(mailboxPathParts, 2));
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
index ba8c6f785f..77c13f095a 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
@@ -106,7 +106,7 @@ public final class ListResponse extends 
AbstractListingResponse implements ImapR
             return name(subscribedPath.getName())
                 
.children(MailboxMetaData.Children.CHILDREN_ALLOWED_BUT_UNKNOWN)
                 .selectability(MailboxMetaData.Selectability.NONE)
-                .hierarchyDelimiter(MailboxConstants.DEFAULT_DELIMITER)
+                .hierarchyDelimiter(MailboxConstants.FOLDER_DELIMITER)
                 .returnSubscribed(RETURN_SUBSCRIBED)
                 .returnNonExistent(RETURN_NON_EXISTENT)
                 .mailboxType(MailboxType.OTHER);
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/main/DefaultPathConverterTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/main/DefaultPathConverterTest.java
index 8a47746da4..95172d8928 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/main/DefaultPathConverterTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/main/DefaultPathConverterTest.java
@@ -19,36 +19,41 @@
 
 package org.apache.james.imap.main;
 
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 
-public class DefaultPathConverterTest implements PathConverterBasicContract {
-
-    private PathConverter pathConverter;
-
-    @BeforeEach
-    void setup() {
-        pathConverter = 
PathConverter.Factory.DEFAULT.forSession(mailboxSession);
-    }
-
-    @Override
-    public PathConverter pathConverter() {
-        return pathConverter;
+public class DefaultPathConverterTest {
+    @Nested
+    public class DotDelimiter extends TestBase {
+        @Override
+        public char pathDelimiter() {
+            return '.';
+        }
     }
 
     @Nested
-    class WithEmailTest implements PathConverterBasicContract.WithEmail {
-
-        private PathConverter pathConverter;
-
-        @BeforeEach
-        void setup() {
-            pathConverter = 
PathConverter.Factory.DEFAULT.forSession(mailboxSession);
+    public class SlashDelimiter extends TestBase {
+        @Override
+        public char pathDelimiter() {
+            return '/';
         }
+    }
+
+    public abstract static class TestBase extends PathConverterBasicContract {
+        private final PathConverter pathConverter = 
PathConverter.Factory.DEFAULT.forSession(mailboxSession);
 
         @Override
         public PathConverter pathConverter() {
             return pathConverter;
         }
+
+        @Nested
+        public class WithEmail extends PathConverterBasicContract.WithEmail {
+            private final PathConverter pathConverter = 
PathConverter.Factory.DEFAULT.forSession(mailboxWithEmailSession);
+
+            @Override
+            public PathConverter pathConverter() {
+                return pathConverter;
+            }
+        }
     }
-}
\ No newline at end of file
+}
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterBasicContract.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterBasicContract.java
index 76ab0a6d36..a627462750 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterBasicContract.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterBasicContract.java
@@ -27,259 +27,285 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MailboxSessionUtil;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-public interface PathConverterBasicContract {
+public abstract class PathConverterBasicContract {
+    public static final Username USERNAME = Username.of("username");
+    public static final Username USERNAME_WITH_DELIMITER = 
Username.of("username.with.delimiter");
+    public static final Username USERNAME_WITH_UNDERSCORE = 
Username.of("username_with_underscore");
+    public static final Username USERNAME2 = Username.of("username2");
 
-    Username USERNAME = Username.of("username");
-    Username USERNAME_WITH_DOT = Username.of("username.with.dot");
-    Username USERNAME_WITH_UNDERSCORE = 
Username.of("username_with_underscore");
-    Username USERNAME2 = Username.of("username2");
+    public static final Username USERNAME_WITH_MAIL = 
Username.of("usern...@apache.org");
+    public static final Username USERNAME2_WITH_MAIL = 
Username.of("userna...@apache.org");
+    public static final boolean RELATIVE = true;
 
-    Username USERNAME_WITH_MAIL = Username.of("usern...@apache.org");
-    Username USERNAME2_WITH_MAIL = Username.of("userna...@apache.org");
-    char PATH_DELIMITER = '.';
-    boolean RELATIVE = true;
+    public final MailboxSession mailboxSession = 
MailboxSessionUtil.create(USERNAME, pathDelimiter());
 
-    MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME);
+    abstract PathConverter pathConverter();
 
-    PathConverter pathConverter();
+    abstract char pathDelimiter();
+
+    static char initialPathDelimiter;
+
+    @BeforeEach
+    public void setUp() {
+        initialPathDelimiter = MailboxConstants.FOLDER_DELIMITER;
+        MailboxConstants.FOLDER_DELIMITER = pathDelimiter();
+    }
+
+    @AfterEach
+    public void tearDown() {
+        MailboxConstants.FOLDER_DELIMITER = initialPathDelimiter;
+    }
+
+    static String adjustToTestDelimiter(String valueWithDots) {
+        // Because the test setup will configure the desired delimiter to be 
used,
+        // we do not need to pass it in manually here.
+        return valueWithDots.replace('.', MailboxConstants.FOLDER_DELIMITER);
+    }
+
+    static Username adjustToTestDelimiter(Username username) {
+        return Username.of(adjustToTestDelimiter(username.asString()));
+    }
 
     @Test
-    default void buildFullPathShouldAcceptNull() {
+    public void buildFullPathShouldAcceptNull() {
         assertThat(pathConverter().buildFullPath(null))
             .isEqualTo(new MailboxPath("", USERNAME, ""));
     }
 
     @Test
-    default void buildPathShouldAcceptEmpty() {
+    public void buildPathShouldAcceptEmpty() {
         assertThat(pathConverter().buildFullPath(""))
             .isEqualTo(new MailboxPath("", USERNAME, ""));
     }
 
     @Test
-    default void buildPathShouldAcceptRelativeMailboxName() {
+    public void buildPathShouldAcceptRelativeMailboxName() {
         String mailboxName = "mailboxName";
         assertThat(pathConverter().buildFullPath(mailboxName))
             .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
     }
 
     @Test
-    default void buildFullPathShouldAcceptUserNamespace() {
+    public void buildFullPathShouldAcceptUserNamespace() {
         
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE))
             .isEqualTo(MailboxPath.forUser(USERNAME, ""));
     }
 
     @Test
-    default void buildFullPathShouldAcceptUserNamespaceAndDelimiter() {
-        
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
PATH_DELIMITER))
+    public void buildFullPathShouldAcceptUserNamespaceAndDelimiter() {
+        
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
pathDelimiter()))
             .isEqualTo(MailboxPath.forUser(USERNAME, ""));
     }
 
     @Test
-    default void buildFullPathShouldAcceptFullAbsoluteUserPath() {
+    public void buildFullPathShouldAcceptFullAbsoluteUserPath() {
         String mailboxName = "mailboxName";
-        
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
PATH_DELIMITER + mailboxName))
-            .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
+        
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
pathDelimiter() + mailboxName))
+                .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
     }
 
     @Test
-    default void buildFullPathShouldAcceptRelativePathWithSubFolder() {
-        String mailboxName = "mailboxName" + PATH_DELIMITER + "subFolder";
+    public void buildFullPathShouldAcceptRelativePathWithSubFolder() {
+        String mailboxName = adjustToTestDelimiter("mailboxName.subFolder");
         assertThat(pathConverter().buildFullPath(mailboxName))
-            .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
+                .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
     }
 
     @Test
-    default void buildFullPathShouldAcceptAbsoluteUserPathWithSubFolder() {
-        String mailboxName = "mailboxName.subFolder";
-        
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
PATH_DELIMITER + mailboxName))
-            .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
+    public void buildFullPathShouldAcceptAbsoluteUserPathWithSubFolder() {
+        String mailboxName = adjustToTestDelimiter("mailboxName.subFolder");
+        
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
pathDelimiter() + mailboxName))
+                .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName));
     }
 
     @Test
-    default void buildFullPathShouldAcceptAbsoluteOtherUserPath() {
-        assertThat(pathConverter().buildFullPath("#user.username2.abc"))
+    public void buildFullPathShouldAcceptAbsoluteOtherUserPath() {
+        
assertThat(pathConverter().buildFullPath(adjustToTestDelimiter("#user.username2.abc")))
             .isEqualTo(MailboxPath.forUser(USERNAME2, "abc"));
     }
 
     @Test
-    default void buildFullPathShouldAcceptAbsoluteOtherUserPathWithDot() {
-        
assertThat(pathConverter().buildFullPath("#user.username__with__dot.abc"))
-            .isEqualTo(MailboxPath.forUser(USERNAME_WITH_DOT, "abc"));
+    public void buildFullPathShouldAcceptAbsoluteOtherUserPathWithDelimiter() {
+        
assertThat(pathConverter().buildFullPath(adjustToTestDelimiter("#user.username__with__delimiter.abc")))
+            
.isEqualTo(MailboxPath.forUser(adjustToTestDelimiter(USERNAME_WITH_DELIMITER), 
"abc"));
     }
 
     @Test
-    default void 
buildFullPathShouldAcceptAbsoluteOtherUserPathWithUnderscore() {
-        
assertThat(pathConverter().buildFullPath("#user.username_-with_-underscore.abc"))
+    public void buildFullPathShouldAcceptAbsoluteOtherUserPathWithUnderscore() 
{
+        
assertThat(pathConverter().buildFullPath(adjustToTestDelimiter("#user.username_-with_-underscore.abc")))
             .isEqualTo(MailboxPath.forUser(USERNAME_WITH_UNDERSCORE, "abc"));
     }
 
     @Test
-    default void buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() 
{
-        assertThat(pathConverter().buildFullPath("#user.username2.abc.def"))
-            .isEqualTo(MailboxPath.forUser(USERNAME2, "abc.def"));
+    public void buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() {
+        
assertThat(pathConverter().buildFullPath(adjustToTestDelimiter("#user.username2.abc.def")))
+            .isEqualTo(MailboxPath.forUser(USERNAME2, 
adjustToTestDelimiter("abc.def")));
     }
 
     @Test
-    default void buildFullPathShouldDenyMailboxPathNotBelongingToTheUser() {
+    public void buildFullPathShouldDenyMailboxPathNotBelongingToTheUser() {
         assertThatThrownBy(() -> pathConverter().buildFullPath("#any"))
             .isInstanceOf(DeniedAccessOnSharedMailboxException.class);
     }
 
     @Test
-    default void mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() {
+    public void mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() {
         assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME, "abc"), mailboxSession))
             .contains("abc");
     }
 
     @Test
-    default void mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() {
+    public void mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() {
         assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME2, "abc"), mailboxSession))
-            .contains("#user.username2.abc");
+            .contains(adjustToTestDelimiter("#user.username2.abc"));
     }
 
     @Test
-    default void mailboxNameShouldEscapeDotInUsername() {
-        assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_DOT, "abc"), mailboxSession))
-            .contains("#user.username__with__dot.abc");
+    public void mailboxNameShouldEscapeDelimiterInUsername() {
+        assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(adjustToTestDelimiter(USERNAME_WITH_DELIMITER), "abc"), 
mailboxSession))
+            
.contains(adjustToTestDelimiter("#user.username__with__delimiter.abc"));
     }
 
     @Test
-    default void mailboxNameShouldEscapeUnderscoreInUsername() {
+    public void mailboxNameShouldEscapeUnderscoreInUsername() {
         assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_UNDERSCORE, "abc"), mailboxSession))
-            .contains("#user.username_-with_-underscore.abc");
+            
.contains(adjustToTestDelimiter("#user.username_-with_-underscore.abc"));
     }
 
     @Test
-    default void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() {
+    public void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() {
         assertThat(pathConverter().mailboxName(RELATIVE, new 
MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession))
-            .contains("#Shared.marketing.abc");
+            .contains(adjustToTestDelimiter("#Shared.marketing.abc"));
     }
 
     @Test
-    default void mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() {
+    public void mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() {
+        String mailboxName = adjustToTestDelimiter("#private.abc");
         assertThat(pathConverter().mailboxName(!RELATIVE, 
MailboxPath.forUser(USERNAME, "abc"), mailboxSession))
-            .contains("#private.abc");
+            .contains(mailboxName);
     }
 
     @Test
-    default void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() {
+    public void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() {
         assertThat(pathConverter().mailboxName(!RELATIVE, 
MailboxPath.forUser(USERNAME2, "abc"), mailboxSession))
-            .contains("#user.username2.abc");
+            .contains(adjustToTestDelimiter("#user.username2.abc"));
     }
 
     @Test
-    default void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() {
+    public void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() {
         assertThat(pathConverter().mailboxName(!RELATIVE, new 
MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession))
-            .contains("#Shared.marketing.abc");
+            .contains(adjustToTestDelimiter("#Shared.marketing.abc"));
     }
     
     @Nested
-    interface WithEmail {
-        MailboxSession mailboxSession = 
MailboxSessionUtil.create(USERNAME_WITH_MAIL);
+    public abstract class WithEmail {
+        public final MailboxSession mailboxWithEmailSession = 
MailboxSessionUtil.create(USERNAME_WITH_MAIL, pathDelimiter());
 
-        PathConverter pathConverter();
+        public abstract PathConverter pathConverter();
 
         @Test
-        default void buildFullPathShouldAcceptNull() {
+        public void buildFullPathShouldAcceptNull() {
             assertThat(pathConverter().buildFullPath(null))
                 .isEqualTo(new MailboxPath("", USERNAME_WITH_MAIL, ""));
         }
 
         @Test
-        default void buildPathShouldAcceptEmpty() {
+        public void buildPathShouldAcceptEmpty() {
             assertThat(pathConverter().buildFullPath(""))
                 .isEqualTo(new MailboxPath("", USERNAME_WITH_MAIL, ""));
         }
 
         @Test
-        default void buildPathShouldAcceptRelativeMailboxName() {
+        public void buildPathShouldAcceptRelativeMailboxName() {
             String mailboxName = "mailboxName";
             assertThat(pathConverter().buildFullPath(mailboxName))
                 .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, 
mailboxName));
         }
 
         @Test
-        default void buildFullPathShouldAcceptUserNamespace() {
+        public void buildFullPathShouldAcceptUserNamespace() {
             
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE))
                 .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, ""));
         }
 
         @Test
-        default void buildFullPathShouldAcceptUserNamespaceAndDelimiter() {
-            
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
PATH_DELIMITER))
+        public void buildFullPathShouldAcceptUserNamespaceAndDelimiter() {
+            
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
pathDelimiter()))
                 .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, ""));
         }
 
         @Test
-        default void buildFullPathShouldAcceptFullAbsoluteUserPath() {
+        public void buildFullPathShouldAcceptFullAbsoluteUserPath() {
             String mailboxName = "mailboxName";
-            
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
PATH_DELIMITER + mailboxName))
+            
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
pathDelimiter() + mailboxName))
                 .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, 
mailboxName));
         }
 
         @Test
-        default void buildFullPathShouldAcceptRelativePathWithSubFolder() {
-            String mailboxName = "mailboxName" + PATH_DELIMITER + "subFolder";
+        public void buildFullPathShouldAcceptRelativePathWithSubFolder() {
+            String mailboxName = "mailboxName" + pathDelimiter() + "subFolder";
             assertThat(pathConverter().buildFullPath(mailboxName))
                 .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, 
mailboxName));
         }
 
         @Test
-        default void buildFullPathShouldAcceptAbsoluteUserPathWithSubFolder() {
-            String mailboxName = "mailboxName.subFolder";
-            
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
PATH_DELIMITER + mailboxName))
+        public void buildFullPathShouldAcceptAbsoluteUserPathWithSubFolder() {
+            String mailboxName = 
adjustToTestDelimiter("mailboxName.subFolder");
+            
assertThat(pathConverter().buildFullPath(MailboxConstants.USER_NAMESPACE + 
pathDelimiter() + mailboxName))
                 .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, 
mailboxName));
         }
 
         @Test
-        default void buildFullPathShouldAcceptAbsoluteOtherUserPath() {
-            assertThat(pathConverter().buildFullPath("#user.username2.abc"))
+        public void buildFullPathShouldAcceptAbsoluteOtherUserPath() {
+            
assertThat(pathConverter().buildFullPath(adjustToTestDelimiter("#user.username2.abc")))
                 .isEqualTo(MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"));
         }
 
         @Test
-        default void 
buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() {
-            
assertThat(pathConverter().buildFullPath("#user.username2.abc.def"))
-                .isEqualTo(MailboxPath.forUser(USERNAME2_WITH_MAIL, 
"abc.def"));
+        public void 
buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() {
+            
assertThat(pathConverter().buildFullPath(adjustToTestDelimiter("#user.username2.abc.def")))
+                .isEqualTo(MailboxPath.forUser(USERNAME2_WITH_MAIL, 
adjustToTestDelimiter("abc.def")));
         }
 
         @Test
-        default void 
mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() {
-            assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_MAIL, "abc"), mailboxSession))
+        public void 
mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() {
+            assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_MAIL, "abc"), mailboxWithEmailSession))
                 .contains("abc");
         }
 
         @Test
-        default void 
mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() {
-            assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"), mailboxSession))
-                .contains("#user.username2.abc");
+        public void 
mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() {
+            assertThat(pathConverter().mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"), mailboxWithEmailSession))
+                .contains(adjustToTestDelimiter("#user.username2.abc"));
         }
 
         @Test
-        default void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() 
{
-            assertThat(pathConverter().mailboxName(RELATIVE, new 
MailboxPath("#Shared", Username.of("market...@apache.org"), "abc"), 
mailboxSession))
-                .contains("#Shared.marketing.abc");
+        public void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() {
+            assertThat(pathConverter().mailboxName(RELATIVE, new 
MailboxPath("#Shared", Username.of("market...@apache.org"), "abc"), 
mailboxWithEmailSession))
+                .contains(adjustToTestDelimiter("#Shared.marketing.abc"));
         }
 
         @Test
-        default void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() {
-            assertThat(pathConverter().mailboxName(!RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_MAIL, "abc"), mailboxSession))
-                .contains("#private.abc");
+        public void mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() 
{
+            assertThat(pathConverter().mailboxName(!RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_MAIL, "abc"), mailboxWithEmailSession))
+                .contains(adjustToTestDelimiter("#private.abc"));
         }
 
         @Test
-        default void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() {
-            assertThat(pathConverter().mailboxName(!RELATIVE, 
MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"), mailboxSession))
-                .contains("#user.username2.abc");
+        public void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() {
+            assertThat(pathConverter().mailboxName(!RELATIVE, 
MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"), mailboxWithEmailSession))
+                .contains(adjustToTestDelimiter("#user.username2.abc"));
         }
 
         @Test
-        default void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() {
-            assertThat(pathConverter().mailboxName(!RELATIVE, new 
MailboxPath("#Shared", Username.of("market...@apache.org"), "abc"), 
mailboxSession))
-                .contains("#Shared.marketing.abc");
+        public void 
mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() {
+            assertThat(pathConverter().mailboxName(!RELATIVE, new 
MailboxPath("#Shared", Username.of("market...@apache.org"), "abc"), 
mailboxWithEmailSession))
+                .contains(adjustToTestDelimiter("#Shared.marketing.abc"));
         }
     }
 }
diff --git a/server/apps/cassandra-app/docker-configuration/jvm.properties 
b/server/apps/cassandra-app/docker-configuration/jvm.properties
index 85e932e7c9..3c81157e45 100644
--- a/server/apps/cassandra-app/docker-configuration/jvm.properties
+++ b/server/apps/cassandra-app/docker-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # Required to locate Cassandra driver configuration
 config.file=/root/conf/cassandra-driver.conf
 
diff --git a/server/apps/cassandra-app/sample-configuration/jvm.properties 
b/server/apps/cassandra-app/sample-configuration/jvm.properties
index 82ea594769..d763daefa8 100644
--- a/server/apps/cassandra-app/sample-configuration/jvm.properties
+++ b/server/apps/cassandra-app/sample-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # Required to locate Cassandra driver configuration
 config.file=conf/cassandra-driver.conf
 
diff --git a/server/apps/distributed-app/docker-configuration/jvm.properties 
b/server/apps/distributed-app/docker-configuration/jvm.properties
index 573d064bcc..3c81157e45 100644
--- a/server/apps/distributed-app/docker-configuration/jvm.properties
+++ b/server/apps/distributed-app/docker-configuration/jvm.properties
@@ -1,12 +1,18 @@
 # ============================================= Extra JVM System Properties 
===========================================
 # To avoid clutter on the command line, any properties in this file will be 
added as system properties on server start.
 
-# Required to locate Cassandra driver configuration
-config.file=/root/conf/cassandra-driver.conf
-
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
+# Required to locate Cassandra driver configuration
+config.file=/root/conf/cassandra-driver.conf
+
 # (Optional). String (size, integer + size units, example: `12 KIB`, supported 
units are bytes KIB MIB GIB TIB). Defaults to 100KIB.
 # This governs the threshold MimeMessageInputStreamSource relies on for 
storing MimeMessage content on disk.
 # Below, data is stored in memory. Above data is stored on disk.
diff --git a/server/apps/distributed-app/sample-configuration/jvm.properties 
b/server/apps/distributed-app/sample-configuration/jvm.properties
index a98626760d..600d91b329 100644
--- a/server/apps/distributed-app/sample-configuration/jvm.properties
+++ b/server/apps/distributed-app/sample-configuration/jvm.properties
@@ -1,11 +1,17 @@
 # ============================================= Extra JVM System Properties 
===========================================
 # To avoid clutter on the command line, any properties in this file will be 
added as system properties on server start.
 
+# Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
+# my.property=whatever
+
 # Required to locate Cassandra driver configuration
 config.file=conf/cassandra-driver.conf
 
-# Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
-# my.property=whatever
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
 
 # (Optional). String (size, integer + size units, example: `12 KIB`, supported 
units are bytes KIB MIB GIB TIB). Defaults to 100KIB.
 # This governs the threshold MimeMessageInputStreamSource relies on for 
storing MimeMessage content on disk.
diff --git 
a/server/apps/distributed-pop3-app/docker-configuration/jvm.properties 
b/server/apps/distributed-pop3-app/docker-configuration/jvm.properties
index 85e932e7c9..3c81157e45 100644
--- a/server/apps/distributed-pop3-app/docker-configuration/jvm.properties
+++ b/server/apps/distributed-pop3-app/docker-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # Required to locate Cassandra driver configuration
 config.file=/root/conf/cassandra-driver.conf
 
diff --git 
a/server/apps/distributed-pop3-app/sample-configuration/jvm.properties 
b/server/apps/distributed-pop3-app/sample-configuration/jvm.properties
index 3901fdf50c..37646e5cec 100644
--- a/server/apps/distributed-pop3-app/sample-configuration/jvm.properties
+++ b/server/apps/distributed-pop3-app/sample-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # Required to locate Cassandra driver configuration
 config.file=conf/cassandra-driver.conf
 
diff --git a/server/apps/jpa-app/sample-configuration/jvm.properties 
b/server/apps/jpa-app/sample-configuration/jvm.properties
index d6d2e0ae4b..8cf7dfb861 100644
--- a/server/apps/jpa-app/sample-configuration/jvm.properties
+++ b/server/apps/jpa-app/sample-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # (Optional). String (size, integer + size units, example: `12 KIB`, supported 
units are bytes KIB MIB GIB TIB). Defaults to 100KIB.
 # This governs the threshold MimeMessageInputStreamSource relies on for 
storing MimeMessage content on disk.
 # Below, data is stored in memory. Above data is stored on disk.
diff --git a/server/apps/jpa-smtp-app/sample-configuration/jvm.properties 
b/server/apps/jpa-smtp-app/sample-configuration/jvm.properties
index 04cfad0b6c..a90fd7a31f 100644
--- a/server/apps/jpa-smtp-app/sample-configuration/jvm.properties
+++ b/server/apps/jpa-smtp-app/sample-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # (Optional). String (size, integer + size units, example: `12 KIB`, supported 
units are bytes KIB MIB GIB TIB). Defaults to 100KIB.
 # This governs the threshold MimeMessageInputStreamSource relies on for 
storing MimeMessage content on disk.
 # Below, data is stored in memory. Above data is stored on disk.
diff --git a/server/apps/memory-app/sample-configuration/jvm.properties 
b/server/apps/memory-app/sample-configuration/jvm.properties
index 317261dd03..c362f00894 100644
--- a/server/apps/memory-app/sample-configuration/jvm.properties
+++ b/server/apps/memory-app/sample-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # (Optional). String (size, integer + size units, example: `12 KIB`, supported 
units are bytes KIB MIB GIB TIB). Defaults to 100KIB.
 # This governs the threshold MimeMessageInputStreamSource relies on for 
storing MimeMessage content on disk.
 # Below, data is stored in memory. Above data is stored on disk.
diff --git 
a/server/apps/scaling-pulsar-smtp/sample-configuration/jvm.properties 
b/server/apps/scaling-pulsar-smtp/sample-configuration/jvm.properties
index 579b688dcb..886dec96f5 100644
--- a/server/apps/scaling-pulsar-smtp/sample-configuration/jvm.properties
+++ b/server/apps/scaling-pulsar-smtp/sample-configuration/jvm.properties
@@ -4,6 +4,12 @@
 # Example: If you need an option -Dmy.property=whatever, you can instead add 
it here as
 # my.property=whatever
 
+# The delimiter used to separate parent/child folders.
+# Optional. Allowed values are: dot (will use '.' as delimiter), slash (will 
use '/' as delimiter).
+# WARNING: This value should only be changed when setting up a new deployment. 
Changing the parameter for an existing deployments
+# will likely lead to failure of some system components, as occurrences of old 
delimiter will still be present in the database/data store.
+# james.mailbox.folder.delimiter=dot
+
 # (Optional). String (size, integer + size units, example: `12 KIB`, supported 
units are bytes KIB MIB GIB TIB). Defaults to 100KIB.
 # This governs the threshold MimeMessageInputStreamSource relies on for 
storing MimeMessage content on disk.
 # Below, data is stored in memory. Above data is stored on disk.


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org


Reply via email to